在Intel x86程序集中执行以下操作的最有效方法是什么(a、b是32位浮点(:
从xmm1: [-, -, a, b]
到xmm1: [a, a, b, b]
我找不到任何有用的说明
我的想法是将a和b复制到其他寄存器,然后将xmm1
寄存器移位4个字节,并将a移动到最低的4个字节。
您正在寻找unpcklps xmm1, xmm1
(https://www.felixcloutier.com/x86/unpcklps)将寄存器中的低元素与其自身交错:
low element->底部2,从第二低到最高2。
您可以使用shufps
,但在这种情况下不会更好,并且需要一个立即字节。要复制和混洗,可以使用pshufd
,但在少数CPU上,整数指令在FP指令之间较慢(但它通常仍然比movaps
+unpcklps
更好。要么没有旁路延迟,要么是1个周期,移动将花费相同的延迟,但也需要一些吞吐量资源。除了Nehalem,旁路延迟为2个周期。我不认为任何具有mov消除功能的CPU都有旁路延迟用于洗牌,但可能有些AMD有。(
如果你很难找到正确的shuffle指令,可以考虑用C写它,看看clang是否能把它变成shuffle。像_mm_set_ps(v[1], v[1], v[0], v[0])
。一般来说,这并不总是编译成好的asm,但值得尝试clang -O3
(clang有一个非常好的shuffle优化器(。在这种情况下,GCC和clang都会想出如何使用一个unpcklps xmm0,xmm0
来实现这一点(https://godbolt.org/z/o6PTeP)而不是可能发生的灾难。或者与shufps xmm0,xmm0, 5
相反(5是0b00'00'01'01
(。
(请注意,将__m128
索引为v[idx]
是GNU扩展,但我只是建议使用clang来找到一个好的shuffle。如果你最终想要内部函数,请检查clang的asm,然后在代码中使用内部函数,而不是_mm_set
(
另请参阅Agner Fog优化指南中的SIMD章节(https://agner.org/optimize/);对于不同类型的数据移动,他有一个很好的说明表。而且https://www.officedaytime.com/simd512e/simd.html具有良好的视觉快速参考,并且https://software.intel.com/sites/landingpage/IntrinsicsGuide/允许您按类别(Swizzle=shuffles(和ISA级别进行筛选(因此您可以排除AVX512,它具有大量带有掩码的内部版本。(
另请参阅https://stackoverflow.com/tags/sse/info这些链接和更多。
如果您不太了解可用的指令(以及CPU架构/性能调整的详细信息(,您可能会更好地使用带有内部函数的C。当你想出一种效率较低的方法来进行洗牌时,编译器可以找到更好的方法。例如,编译器有望为您将_mm_shuffle_ps(v,v, _MM_SHUFFLE(1,1,0,0))
优化为unpcklps
。
很少有手工编写的asm是正确的选择,尤其是对于x86。编译器通常能很好地处理内部函数,尤其是GCC和clang。如果您不知道unpcklps
的存在,那么您可能离轻松/常规地击败编译器还有很长的路要走。