查看使用您选择的调整和-march
选项的_mm_set_ps(f3,f2,f1,f0)
编译器输出或者使用_mm_setr_ps(f0,f1,f2,f3)
。请注意,不同的选项可能会产生不同的结果。
或者查看Agner Fog的优化指南:他有一章关于SSE/AVX的内容,并提供了一个方便的按类型分类的数据移动指令表。这对于了解高度非正交的SSE/AVX扩展中可用的洗牌方式非常有帮助。
人们已经指出,标准方法是使用2x
unpcklps
将一对转换为向量
[00ba]
[00dc]
,其中
0
是一个不关心的值或实际上是
0.0
,如果你的标量浮点数的上部元素恰好为零。(我的符号遵循英特尔的约定,即将高元素放在
左边的图示中,因此在您的符号中,左移将数据向左移动,并且使用不同的元素宽度查看数据不会改变写入方式。)
然后使用
movlhps
将一个xmm寄存器的低qword复制到另一个寄存器的高qword(合并到现有值)。
如果这对您来说不明显或不熟悉,您应该使用内部函数编写C代码,并查看优化的编译器输出以了解基本方法。clang具有非常好的洗牌优化器,可以找到更好的方法将您内部函数的逻辑实现为汇编语言。
也许有更好的方法:
这三个指令都是洗牌指令,在英特尔 Sandybridge 系列 CPU 上每时钟周期只能执行一次(竞争第 5 口)。
如果我们可以使用 SSE4.1 中的 blendps
(带有立即混合控制),我们可能可以将其用作最后一步,而不是洗牌。它可以在任何口上运行。
我认为我们可以使用 shufps
来创建向量 [0c0a]
和 [d0b0]
。 shufps
的低 2 个元素来自第一个源=目标操作数,另一半来自另一个源。
如果您的输入向量实际上被零扩展并且绝对没有高垃圾,则可以使用 SSE1 的 orps
代替混合以获得 [dcba]
。
unpcklps
和movlhps
可以将内存或另一个寄存器作为第二个参数。如果您不想使用AVX指令,只需使用@MargaretBloom的链接,并将-march=cannonlake
替换为-msse
(或-msse4.2
,或您喜欢的任何指令集):https://godbolt.org/z/ruhVhl - chtzunpcklps
+unpcklpd
或者movlhps
。但是既然你提到了,如果你能使用shufps
将输入设置为[abxx]
[xxcd]
,或者实际上可能是[axcx]
[xbxd]
(因为shufps
输出的低2个元素来自第一个源=dst,另一半来自另一个源),那么你可能可以设置第三条指令为高效的blendps
立即数。x86-64 GCC默认使用基线x86-64,即SSE1 + SSE2。 - Peter Cordes_mm_set_ps(f3,f2,f1,f0)
的编译器输出。或者查看 Agner Fog 的优化指南:他有一章关于 SSE/AVX,其中包含按类型分类的数据移动指令方便的表格。https://agner.org/optimize/ - Peter Cordes