为什么vhaddps指令要这么复杂地进行加法运算?

3
vhaddps指令以一种非常特殊的方式进行加法操作:

enter image description here

来源:https://www.felixcloutier.com/x86/haddps 这种设计的原因是什么?这个指令用于哪些情况?它看起来像是有特定的目的。
2个回答

2
与许多256位宽指令一样,vhaddps ymm ymm ymm 的上128位只是复制粘贴了128位宽的vhaddps xmm xmm xmm指令。以下示例表明以这种方式定义vhaddps xmm xmm xmm是有意义的:使用此指令两次可获得4个xmm寄存器的水平总和。"最初的回答"
/* gcc -m64 -O3 hadd_ex.c -march=sandybridge           */
#include<immintrin.h>
#include<stdio.h>
int main(){
    float tmp[4];
    __m128 a = _mm_set_ps(1.0, 2.0, 3.0, 4.0);
    __m128 b = _mm_set_ps(10.0, 20.0, 30.0, 40.0);
    __m128 c = _mm_set_ps(100.0, 200.0, 300.0, 400.0);
    __m128 d = _mm_set_ps(1000.0, 2000.0, 3000.0, 4000.0);
    __m128 sum1 = _mm_hadd_ps(a, b);
    __m128 sum2 = _mm_hadd_ps(c, d);
    __m128 sum = _mm_hadd_ps(sum1, sum2);
    _mm_storeu_ps(tmp,sum);
    printf("sum = %f  %f  %f  %f\n", tmp[0], tmp[1], tmp[2], tmp[3]);
    return 0;
}

输出:

sum = 10.000000  100.000000  1000.000000  10000.000000

2
“这是在低位和高位的128位通道中进行2个haddps指令。大多数AVX指令实际上并没有将操作扩展到256位,它们执行2个单独的通道内操作。这使得AVX难以使用,特别是在没有AVX2的情况下进行小于128位粒度的通道交叉洗牌!但与例如将vpshufb作为单个32字节洗牌而不是2x 16字节洗牌相比,它可以节省晶体管。即使是AVX2也没有提供: AVX2中的VPERMB在哪里?(必须等待AVX512VBMI)。 ”
相关:最佳的AVX通道交错方法是什么? 此外,AVX512添加了许多灵活的通道交错洗牌,但SSE/AVX指令的AXV512版本(如vhaddps zmm)仍在通道内。另请参见在AVX512中进行128位跨通道操作是否会提高性能? AVX2的一系列vpack*通常需要使用vpermq来进行通道交错修复,除非您打算再次在通道内进行解包。因此,在大多数情况下,两个通道内洗牌比一个完整的256位宽度操作更差,但这并不是我们从AVX得到的。即使需要额外的洗牌来纠正通道内行为,从128位向量转换为256位向量通常仍然可以加速,但这通常意味着即使没有内存瓶颈,它也不是2倍加速。
“vpalignr”可能是两个相同洗牌的2x 128位版本中最为严重的例子,它本身并不是一个有用的构建块;我不记得是否曾经看到过使用2个独立的字节窗口的数据的用例。但实际上,如果您使用“vperm2i128”如何使用AVX2高效地连接两个向量?(VPALIGNR的跨通道版本),则可以将其输入,但通常在支持AVX2的CPU上,未对齐的加载更好。

(v)haddps的使用情况非常有限

也许英特尔计划在引入SSE3之后的某个时候将haddps变为单一uop指令,但这从未发生。

使用案例包括转置和添加类型的操作,其中您需要对垂直addps的两个输入进行重排。例如: 8个源__m256向量的水平总和最有效的方法包括vhaddps。(另外还需要AVX1 vperm2f128来纠正车道内行为。)

许多人错误地认为对于单个向量的水平求和,128位和256位的 (v)haddps 都是好的选择,但实际上它们都需要2个洗牌操作来准备输入向量以进行竖直的 (v)addps 操作。对于水平求和,每个加法只需要1个洗牌操作。(参见x86 上执行浮点向量水平求和的最快方法)
缩小到128位(使用vextractf128 / vaddps)通常是更好的第一步,除非您希望将结果广播到每个元素,并且您不在AMD CPU上(在那里,256位矢量操作解码至少为2个uop,或者对于跨越车道的洗牌更多)。如果您正在优化代码大小而不是速度,则(v)haddps xmm或整数vphaddd对于水平求和很有用,例如在code-golf问题“计算两个数字的平均值”的我的x86机器代码答案中。
AVX非破坏性目的操作数也消除了具有多个uop指令的吸引力。没有AVX,有时您无法避免在破坏寄存器之前复制寄存器的movaps,因此将2x shuffle + add烘焙成1个指令实际上确实节省了uops,而不必手动使用movaps + shufps。

我猜你的打字速度比我快多了。:) - wim
@wim:我打字还是挺快的,是啊 :) 我从一开始就养成了好习惯;我爸爸在 Atari ST 上有一个打字教练游戏叫做 Word Invaders,我在90年代的时候还是个少年时玩过一些,所以我一直都是用触摸打字并知道如何高效地打字,我大多数时间都遵循这种方式。而且现在我已经练了好几年了。 :P - Peter Cordes
我开始打字是在ZX Spectrum上,这解释了很多问题。 - wim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接