vhaddps
指令以一种非常奇特的方式添加:
来源:https://www.felixcloutier.com/x86/haddps
这是什么原因?该指令适用于哪些用例?看起来设计有一些特定的想法。
最佳答案
它是在低和高 128 位 channel 中的 2 个 channel 内 haddps
指令。 大多数 AVX 指令并没有真正将操作扩展到 256 位,它们执行 2 个单独的 channel 内操作。这使得 AVX 难以使用,尤其是在没有 AVX2 的情况下,用于小于 128 位粒度的车道交叉洗牌!
但它节省了晶体管。使 vpshufb
成为单个 32 字节随机播放而不是 2x 16 字节随机播放。 AVX2 甚至不提供:Where is VPERMB in AVX2?(必须等待 AVX512VBMI)。
(相关:best way to shuffle across AVX lanes? 此外,AVX512 添加了很多灵活的车道交叉洗牌,但 AXV512 版本的 SSE/AVX 指令,如 vhaddps zmm
仍在车道内。另见 Do 128bit cross lane operations in AVX512 give better performance? )
AVX2 vpack*
链通常需要一个 vpermq
来在最后进行车道交叉修复,除非您要再次在车道内解包。 因此在大多数情况下,2x channel 内洗牌比完整的 256 位宽操作更糟糕,但这不是我们从 AVX 中得到的结果。通常仍然有加速到 256-位向量从 128 增加,即使它需要额外的洗牌来纠正 channel 内行为,但这通常意味着它不是 2 倍的加速,即使没有内存瓶颈。
vpalignr
可能是同一 shuffle 的 2x 128 位版本本身并不是有用构建 block 的最令人震惊的示例;我不记得我是否见过使用 2 个单独的 channel 内字节数据窗口的用例。哦,实际上是的,如果你用 vperm2i128
How to concatenate two vector efficiently using AVX2? (a lane-crossing version of VPALIGNR) 喂它,但通常未对齐的负载在支持 AVX2 的 CPU 上更好。
(v)haddps
的用例非常有限
也许英特尔计划在将 haddps
与 SSE3 一起引入后的某个时候将其变成单 uop 指令,但那从未发生过。
用例包括转置和添加类型的东西,您无论如何都需要为垂直 addps
打乱两个输入。例如Most efficient way to get a __m256 of horizontal sums of 8 source __m256 vectors 包括 vhaddps
。 (加上 AVX1 vperm2f128
以纠正车道内行为。)
许多人错误地认为它适用于单个向量的水平求和,但 128 位和 256 位 (v)haddps
都解码为 2x shuffle uops 以为垂直 准备输入向量(v)addps
uop。对于水平总和,每次添加只需要 1 个洗牌 uop。 ( Fastest way to do horizontal float vector sum on x86 )
首先缩小到 128 位(使用 vextractf128
/vaddps
)通常是更好的第一步,除非您希望将结果广播到每个元素,而您不是在 AMD CPU 上(其中 256 位向量运算解码为至少 2 微指令,或更多用于交叉车道洗牌)。 (v)haddps xmm
或整数 vphaddd
如果您针对代码大小而不是速度进行优化,则对水平求和很有用,例如my x86 machine-code answer 关于代码高尔夫问题“计算两个数字的均值”。
AVX 非破坏性目标操作数也消除了具有多 uop 指令的一些吸引力。如果没有 AVX,有时您无法避免 movaps
在销毁寄存器之前复制寄存器,因此烘焙 2x shuffle + add into 1 instruction 实际上节省了 uops 与必须使用 手动执行此操作相比movaps
+ shufps
.
关于assembly - 为什么 vhaddps 指令会以如此复杂的方式添加?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56097209/