assembly - 为什么 vhaddps 指令会以如此复杂的方式添加?

标签 assembly x86 avx

vhaddps 指令以一种非常奇特的方式添加:

enter image description here

来源: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/

相关文章:

windows - x86 程序中的控制台

assembly - x86 中断表修改

delphi - 在内联汇编中将常量读入 SSE/AVX 寄存器

macos - g++:AVX 没有这样的指令

assembly - 如何在汇编中查找数组中的最小有符号值

swift - 仅在条件编译 block 中设置可选,编译器是否从发布版本中完全删除语句?

c++ - 当返回一个没有 RVO 的类/结构时会发生什么?

c - SIMD 内在函数 - 段错误

c++ - 查找正在运行的 valgrind 版本

assembly - 通过堆栈从过程返回一个值