【问题标题】:aarch64 xtn2 clearing lower halfaarch64 xtn2 清除下半部分
【发布时间】:2022-01-16 10:31:18
【问题描述】:

是否有类似于xtn2 的操作,但实际上清除了下半部分而不是保持原样?我有一个 128 位向量 v0 其视图为 4s{a,x,b,y} 与 x 和 y 无关。我想获得{0,0,a,b}。如果我这样做了

xtn2     v0.4s, v0.2d
mov      v0.d[0], xzr

我得到了我想要的结果。有没有办法通过一条指令或更有效的方式来做到这一点?

【问题讨论】:

  • 如果您可以备用另一个寄存器,那么movi v1.4s, #0 ; xtn2 v1.4s, v0.2d 可能会好一点,因为movi 可以更早地执行乱序,而无需等待v0 中的输入准备好。另外,我相信从通用寄存器到 SIMD 寄存器的移动非常缓慢,尽管我不知道这是否适用于零寄存器。
  • 我想你已经考虑过是否可以重写后续代码以使用{a, b, 0, 0},然后只使用xtn
  • 也许uzp2可以用来做这个。
  • @fuz:好地方。我认为它实际上是uzp1 v0.4s, v1.4s, v0.4s,其中v1 用零填充。这似乎是最好的答案 - 你仍然需要一个备用寄存器,但它可以始终保持为零,例如跨循环迭代。
  • @potuz:使用uzp1,您无需在初始清零后写入第二个寄存器。如果您在循环中执行此操作,您可以在循环之前movi v1.4s, #0,并且每次迭代只有uzp1 v0.4s, v1.4s, v0.4s。使用xtn2,您会覆盖之前清零的寄存器,因此每次循环迭代都需要movi v1.4s, #0

标签: assembly simd arm64 neon armv8


【解决方案1】:

(感谢 fuz 的原始建议)

如果你可以腾出另一个寄存器来保存零,比如v1,那么你可以这样做

uzp1 v0.4s, v1.4s, v0.4s

一般uzp1 vd.t, vn.t, vm.tvn 的偶数元素(从零开始)打包到vd 的低半部分,将vm 的偶数元素打包到高半部分。 (uzp2 对奇数元素执行相同的操作。)因此,如果 v1 为零,则结果的低半部分为零,v0 的第 0 和第 2 个元素在高半部分,它们是你的ab

请注意,如果您多次执行此操作,则可以将 v1 初始化为零一次并在整个代码中使用,因为它不是由该指令编写的。 (如果 ARM 提供了一个零 SIMD 寄存器 vzr 会更容易。)如果我们忽略它的开销,那么这是非常有效的。以 Cortex A-72 时序为例(因为我碰巧有它的优化指南),uzp1 是最便宜的 SIMD 指令,3 个周期的延迟和 2 个每个周期的吞吐量(它可以在两者中的任何一个中执行) SIMD 算术流水线)。

您的原始版本的一个性能说明是,在 SIMD 和通用寄存器之间移动非常昂贵,应尽可能避免。在 Cortex A-72 上,mov v*, x*ins 的特殊情况别名)是 8 个周期的延迟和 1 个每个周期的吞吐量,它需要加载管道(其中只有一个)以及其中一个两个 SIMD 算术流水线。可以想象,xzr 可能有一个特殊情况,但没有提到 Cortex A-72。

不幸的是,据我所知,没有直接的替代方法可以将 SIMD 寄存器中的一个元素归零; movi 将立即数写入所有元素,而不仅仅是其中一些元素。因此,即使您坚持使用xtn2,您也可能希望在另一个寄存器中保持零,因为ins v0.d[0], v1.d[0] 很便宜。

【讨论】:

    猜你喜欢
    • 2017-12-19
    • 1970-01-01
    • 1970-01-01
    • 2013-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-19
    相关资源
    最近更新 更多