【问题标题】:Why does the vhaddps instruction add in such an involved way?为什么 vhaddps 指令以如此复杂的方式添加?
【发布时间】:2019-05-12 07:51:22
【问题描述】:

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

来源:https://www.felixcloutier.com/x86/haddps

这是什么原因?该指令适用于哪些用例?看起来设计有特定的想法。

【问题讨论】:

    标签: assembly x86 avx


    【解决方案1】:

    在低和高 128 位通道中有 2 个通道内 haddps 指令。 大多数 AVX 指令并没有真正将操作扩展到 256 位,它们执行 2 个单独的通道内操作。这使得 AVX 难以使用,尤其是没有 AVX2 用于小于 128 位粒度的车道交叉洗牌!

    但它节省了晶体管,例如使vpshufb 成为单个 32 字节的随机播放,而不是 2 个 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 通道内随机播放比完整的 256 位宽操作更糟糕,但这不是我们从 AVX 中得到的。通常还有一个加速到 256-位向量从 128 上升,即使它需要额外的洗牌来纠正通道内的行为,但这通常意味着即使没有内存瓶颈,它也不是 2 倍的加速。

    vpalignr 可能是同一个 shuffle 的 2x 128 位版本本身并不是一个有用的构建块的最令人震惊的例子;我不记得我是否见过使用 2 个单独的通道内字节数据窗口的用例。哦,实际上是的,如果你用 vperm2i128 How to concatenate two vector efficiently using AVX2? (a lane-crossing version of VPALIGNR) 喂它,但通常未对齐的负载在支持 AVX2 的 CPU 上会更好。


    (v)haddps 的用例非常有限

    也许英特尔计划在将haddps 与 SSE3 一起引入后,在某个时候将其制成单指令指令,但这从未发生过。

    用例包括转置和添加类型的东西,无论如何您都需要将两个输入随机排列为垂直 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 uop 以为垂直 (v)addps uop 准备输入向量。对于水平总和,每次添加只需要 1 个 shuffle 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 非破坏性目标操作数还消除了多指令指令的一些吸引力。如果没有 AVX,有时您无法避免 movaps 在销毁寄存器之前复制寄存器,因此烘焙 2x shuffle + add into 1 指令实际上确实节省了 uops,而不必手动使用 movaps + shufps

    【讨论】:

    • 我猜你的打字速度比我快很多。:)
    • @wim:我可以很快地输入文字,是的 :) 我从一开始就养成了好习惯;我父亲在 Atari ST 上玩过一款打字辅导游戏,名为 Word Invaders,我在 90 年代还是青少年的时候玩过这款游戏,所以我一直都是触摸打字的,并且知道你是如何应该有效地打字,我主要遵循。到现在为止,我已经练习了好几年了。 :P
    • 我在 ZX Spectrum 开始打字,这解释了很多。
    【解决方案2】:

    与许多 256 位宽的指令一样,高 128 位 vhaddps ymm ymm ymm 只是 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-11
      • 2013-12-27
      • 2017-10-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多