如果我的理解是正确的,_mm_movehdup_ps(a)
给出与以下结果相同的结果_mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 3, 3))
?
两者在性能上有区别吗?
最佳答案
_MM_SHUFFLE
首先使用高元素,因此_MM_SHUFFLE(3,3, 1,1)
将进行 movshdup
随机播放。
主要的区别在于装配级别。 movshdup
是一种复制和改组,如果以后仍然需要输入movaps
(例如,作为水平和的一部分,则避免a
复制输入)(例如,请参见Fastest way to do horizontal float vector sum on x86以获取无movaps
与SSE1版本进行编译的示例)使用shufps
。movshdup
/movsldup
也可以是带有内存源操作数的load + shuffle。 (shufps
显然不能,因为它需要两次相同的输入。)在现代Intel CPU(Sandybridge系列)上, movshdup xmm0, [rdi]
解码为纯负载uop,而不是与ALU uop 微融合。因此,它无法与其他洗牌竞争ALU洗牌吞吐量(端口5)。加载端口包含执行广播加载的逻辑(包括movddup
64位广播),以及成对元素的movs[lh]dup
复制。像vpermilps xmm0, [rdi], 0x12
或pshufd xmm, [rdi], 0x12
这样的更复杂的load + shuffle仍然会解码为多个uops,可能会根据uarch微融合到load + ALU中。
两条指令的长度相同:movshdup
避免使用立即数字节,但是shufps
是SSE1指令,因此它只有2个字节的操作码,比SSE2和SSE3指令短1个字节。 但是启用AVX后,vmovshdup
确实节省了一个字节,因为操作码大小的优势消失了。
在只有64位洗牌单元的较旧CPU(例如Pentium-M和第一代Core 2(Merom))上,具有更大的性能优势。 movshdup
仅在向量的64位一半内随机播放。在Core 2 Merom上,movshdup xmm, xmm
解码为1 uop,但是shufps xmm, xmm, i
解码为3 uop。 (有关说明表和微体系结构指南,请参见https://agner.org/optimize/)。另请参阅我的水平总和答案(前面已链接),以了解有关诸如Merom和K8之类的SlowShuffle CPU的更多信息。
在具有内在函数的C++中
如果启用了SSE3,则如果编译器未将_mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 1, 1))
优化为与_mm_movehdup_ps(a)
相同的程序集,则将错过优化。
但是,某些编译器(例如MSVC)通常不会优化内部函数,因此,程序员应了解通过使用内在函数进行复制和混洗指令(例如movaps
和pshufd
)来避免movshdup
指令的asm含义,而无需进行洗牌必然会破坏其目标寄存器(例如shufps
和psrldq
字节移位。)
同样,MSVC不允许您启用编译器对SSE3的使用,如果对它们使用内在函数,则仅会获得超出基线SSE2(或没有SIMD)的指令。或者,如果启用了AVX,则允许编译器也使用SSE4.2及更早版本,但仍选择不进行优化。如此一来,由人工程序员来寻找优化。 ICC与此类似。如果您确切地知道自己在做什么并且正在检查编译器的asm输出,则有时这可能是一件好事,因为有时gcc或clang的优化可能会使您的代码悲观。
用clang编译并查看它是否使用与源代码中的内在函数相同的指令可能是个好主意;在支持Intel内在函数的4个主要编译器中,它具有迄今为止最好的改组优化器,基本上可以像编译器通常对纯C进行优化的方式(即仅遵循as-if规则以产生相同的结果)那样来优化内在代码。
最简单的例子:
#include <immintrin.h>
__m128 shuf1(__m128 a) {
return _mm_shuffle_ps(a,a, _MM_SHUFFLE(3,3, 1,1));
}
compiled with gcc/clang/MSVC/ICC on Godbolt
GCC和带有
-O3 -march=core2
的clang都可以发现优化:shuf1:
movshdup xmm0, xmm0
ret
ICC
-O3 -march=haswell
和MSVC -O2 -arch:AVX -Gv
(启用vectorcall调用约定,而不是通过引用传递SIMD矢量。)shuf1:
vshufps xmm0, xmm0, xmm0, 245 #4.12
ret #4.12
关于x86 - 在这种情况下,_mm_movehdup_ps和_mm_shuffle_ps有什么区别?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56238197/