如果我的理解是正确的,
_mm_movehdup_ps(a)
给出相同的结果
_mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 3, 3))
?
两者有性能差异吗?
如果我的理解是正确的,
_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了解如何在没有 a 的情况下编译的示例)movaps
与使用shufps
.
movshdup
/movsldup
也可以是带有内存源操作数的加载+随机播放。(shufps
显然不能,因为它需要两次相同的输入。)在现代 Intel CPU(Sandybridge 系列)上,movshdup xmm0, [rdi]
解码为纯负载 uop,而不是与 ALU uop 微融合。因此,它不会与其他 shuffle 竞争 ALU shuffle 吞吐量(端口 5)。加载端口包含执行广播加载(包括movddup
64 位广播)和movs[lh]dup
元素对复制的逻辑。更复杂的加载+随机播放,例如vpermilps xmm0, [rdi], 0x12
或pshufd xmm, [rdi], 0x12
仍然解码为多个微指令,可能根据uarch微融合到加载+ ALU中。
两条指令的长度相同:movshdup
避免了立即字节,但是shufps
是一条 SSE1 指令,因此它只有一个 2 字节的操作码,比 SSE2 和 SSE3 指令短 1 个字节。 但是启用 AVX 后,vmovshdup
确实节省了一个字节,因为操作码大小的优势消失了。
在只有 64 位 shuffle 单元的旧 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 的更多信息,另请参阅我的水平总和答案(之前链接)。
如果启用了 SSE3,如果您的编译器没有优化_mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 1, 1))
到它会为_mm_movehdup_ps(a)
.
但是,一些编译器(如 MSVC)通常不会优化内在函数,因此程序员需要了解movaps
通过使用内在函数来复制和洗牌指令(如pshufd
and movshdup
)而不是洗牌来避免指令的 asm 含义,而洗牌必然会破坏它们目标寄存器(如shufps
和psrldq
字节移位。)
此外,MSVC 不允许您启用 SSE3 的编译器使用,如果您对它们使用内部函数,则只能获得超出基线 SSE2(或没有 SIMD)的指令。或者,如果您启用 AVX,这将允许编译器也使用 SSE4.2 及更早版本,但它仍然选择不优化。再次,由人类程序员来寻找优化。ICC 类似。有时,如果您确切地知道自己在做什么并检查编译器的 asm 输出,这可能是一件好事,因为有时 gcc 或 clang 的优化会使您的代码感到悲观。
使用 clang 编译并查看它是否使用与源代码中的内在函数相同的指令可能是一个好主意;它拥有迄今为止支持英特尔内在函数的 4 个主要编译器中最好的 shuffle 优化器,基本上以与编译器通常优化纯 C 的方式相同的方式优化您的内在代码,即仅遵循 as-if 规则来产生相同的结果。
最简单的例子:
#include <immintrin.h>
__m128 shuf1(__m128 a) {
return _mm_shuffle_ps(a,a, _MM_SHUFFLE(3,3, 1,1));
}
在 Godbolt 上使用 gcc/clang/MSVC/ICC 编译
GCC 和 clang-O3 -march=core2
都发现了优化:
shuf1:
movshdup xmm0, xmm0
ret
ICC-O3 -march=haswell
和 MSVC -O2 -arch:AVX -Gv
(启用向量调用调用约定,而不是通过引用传递 SIMD 向量。)
shuf1:
vshufps xmm0, xmm0, xmm0, 245 #4.12
ret #4.12