如何将4个32位浮点数存储到一个128位xmm寄存器中?

4

我希望将4个32位浮点数存储到xmm0中,其中每个浮点数都存储在一个128位寄存器中。 例如,我有4个浮点数: xmm1: 10.2 xmm2: 5.8 xmm3: 9.3 xmm4: 12.7(每个仅使用了128位寄存器的32位) 并希望将它们存储到xmm0中,如下所示: 10.2, 5.8, 9.3, 12.7, 以便它们在xmm0中相邻地存储。

然后,在对xmm0进行一些运算(例如mulps...)之后,我还想单独提取它们中的每一个。

我尝试过使用movlps和movhps,但它们只使用内存加载,而不使用128位寄存器作为源。由于性能原因,我不想使用任何额外的内存。

PSLLDQ可能有帮助,但是否有更好的解决方案来解决我的问题呢?


1
“PSLLD”并不能做你想做的事情。它不会转移双字本身,而是转移每个双字内部的位。请尝试使用“SHUFPS”或“PSLLDQ”。 - Jester
4
我请求GCC和Clang来实现这个操作(https://godbolt.org/z/SDR4Ov),将`xmm1..4`合并到`xmm0`中。两者都使用了3条指令,每个指令都应该使用端口5,延迟/吞吐量为1c(但我只浏览了Agner Fog的表格,并且仅限于SklX)。然而,如果我没记错的话,GCC只使用了SSE扩展。 - Margaret Bloom
2
unpcklpsmovlhps可以将内存或另一个寄存器作为第二个参数。如果您不想使用AVX指令,只需使用@MargaretBloom的链接,并将-march=cannonlake替换为-msse(或-msse4.2,或您喜欢的任何指令集):https://godbolt.org/z/ruhVhl - chtz
2
@MargaretBloom:我不知道有什么方法可以用SSE/AVX2打败3 port5 uops。显然最明显的方法是2x unpcklps + unpcklpd 或者 movlhps。但是既然你提到了,如果你能使用shufps将输入设置为[abxx] [xxcd],或者实际上可能是[axcx] [xbxd](因为shufps输出的低2个元素来自第一个源=dst,另一半来自另一个源),那么你可能可以设置第三条指令为高效的blendps立即数。x86-64 GCC默认使用基线x86-64,即SSE1 + SSE2。 - Peter Cordes
1
@pedzer:查看 _mm_set_ps(f3,f2,f1,f0) 的编译器输出。或者查看 Agner Fog 的优化指南:他有一章关于 SSE/AVX,其中包含按类型分类的数据移动指令方便的表格。https://agner.org/optimize/ - Peter Cordes
显示剩余3条评论
1个回答

5

查看使用您选择的调整和-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]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接