作为对 hirschhornsalz 解决方案的修改,如果i
是编译时常量,您可以通过使用 shuffle 完全避免联合路径:
template<unsigned i>
float vectorGetByIndex( __m128 V)
{
// shuffle V so that the element that you want is moved to the least-
// significant element of the vector (V[0])
V = _mm_shuffle_ps(V, V, _MM_SHUFFLE(i, i, i, i));
// return the value in V[0]
return _mm_cvtss_f32(V);
}
标量浮点数只是XMM 寄存器的底部元素,高元素允许非零;_mm_cvtss_f32
是免费的,将编译为零指令。这将作为一个 shufps 内联(或者对于 i==0 什么都没有)。
编译器足够聪明,可以优化掉 shuffle i==0
(长期过时的 ICC13 除外),因此不需要if (i)
. https://godbolt.org/z/K154Pe。clang 的 shuffle 优化器将编译vectorGetByIndex<2>
成movhlps xmm0, xmm0
比它短 1 个字节shufps
并产生相同的低元素。switch
您可以使用/case
为其他编译器手动执行此操作,因为i
它是编译时常量,但是在手动矢量化时使用它的少数地方的 1 字节代码大小非常简单。
请注意,SSE4.1_mm_extract_epi32(V, i);
在这里不是一个有用的 shuffle:extractps r/m32, xmm, imm
只能将 FP 位模式提取到整数寄存器或内存(https://www.felixcloutier.com/x86/extractps)。(并且内在函数将其返回为int
,因此它实际上会编译为extractps
+cvtsi2ss
以在 FP 位模式上进行 int->float 转换,除非您在 C++ 代码中对它进行类型双关。但是您希望它能够编译to extractps eax, xmm0, i
/movd xmm0, eax
这与 shufps 相比很糟糕。)
唯一extractps
有用的情况是编译器希望将此结果直接存储到内存中,并将存储折叠到提取指令中。(对于 i!=0,否则它将使用movss
)。将结果作为标量浮点数保留在 XMM 寄存器中shufps
是很好的。
(SSE4.1insertps
可以使用但没必要:它可以在获取任意源元素的同时将其他元素归零。)