如何将两个打包的 64 位四字加载到 128 位 xmm 寄存器中
Posted
技术标签:
【中文标题】如何将两个打包的 64 位四字加载到 128 位 xmm 寄存器中【英文标题】:How to load two packed 64-bit quadwords into a 128-bit xmm register 【发布时间】:2018-11-26 23:01:24 【问题描述】:我有两个 UInt64(即 64 位四字)整数。
它们与 8 字节 (sizeof(UInt64)
) 边界对齐(如果对任何事情有用,我也可以将它们对齐到 16 字节)
它们被打包在一起,因此它们在内存中并排
如何将它们加载到 xmm 寄存器中,例如xmm0
:
我找到了:
movq xmm0, v[0]
但这只会移动 v[0]
,并将xmm0
中的高 64 位设置为零:
xmm0
0000000000000000 24FC18D93B2C9D8F
额外问题
如何将它们取出? 如果它们不在内存中并排怎么办? 如果它们是 4 字节对齐的呢?编辑
正如 W. Chang 所指出的,字节顺序化很少,我可以接受其他方式:
我的难题是如何让他们进来,然后让他们出去。
【问题讨论】:
对于未来的此类问题,请参阅this nice overview 的可用说明。 有解说指南的指南吗?在不知道参考是什么的情况下,我看到的只是,“下划线五一二下划线四下划线小便双 u es es dee 下划线小便眼三十二”。然而,我正在寻找 i) 如何将 UInt64 放入 xmm ii) 如何并行添加两个 64 位整数,以及如何得到答案。没有解码指南的指南,我盯着...上帝...那里必须有 900 次操作。我想要的三个似乎是一个秘密。 Intrinsics 是与汇编密切相关的 C 风格函数。每个内在函数对应一个或几个汇编指令。它们是内联的(没有函数调用开销)并且在大多数情况下与编写汇编一样高效。 是否有必要像这样反向加载它们(将第二个元素放入向量寄存器的低半部分)? 请注意,下面彼得的回答将 V[0] 加载到 XMM 寄存器的下半部分。在您的绘图中,V[0] 位于上半部分。 Intel/AMD CPU 是 little-endian,这意味着第一个字节存储在最低 8 位中,依此类推。所以上半部分有 V[0] 是不寻常的。 【参考方案1】:对于未对齐的 128 位加载,请使用:
movups xmm0, [v0]
:为float
或double
数据移动未对齐的单精度浮点数。 (movupd
长 1 个字节,但不会产生性能差异。)
movdqu xmm0, [v0]
: 移动未对齐的双四字
即使两个四字跨越缓存行边界,这通常也是吞吐量的最佳选择。 (在 AMD CPU 上,当负载不适合缓存线的对齐 32 字节块时,可能会受到惩罚,而不仅仅是 64 字节缓存线边界。但在 Intel 上,64 字节内的任何未对齐缓存线是免费的。)
如果您的负载提供整数 SIMD 指令,您可能需要 movdqu
,即使 movups
在机器代码中短 1 个字节。一些 CPU 可能关心不同类型负载的“跨域”。对于存储没关系,许多编译器总是使用movups
,即使是整数数据。
另请参阅How can I accurately benchmark unaligned access speed on x86_64,了解有关未对齐负载成本的更多信息。 (SIMD 和其他)。
如果没有连续的,你最好的选择是
movq xmm0, [v0]
: 移动四字
movhps xmm0, [v1]
:移动高压缩单精度浮点。 (没有整数等价物,无论如何都要使用它。永远不要使用movhpd
,它不再是无益的,因为没有 CPU 关心双精度和浮点随机播放。)
或者在旧的 x86 上,例如 Core2 和其他旧 CPU,即使 16 个字节都来自同一个缓存行,movups
也很慢,您可以使用
movq xmm0, [v0]
:移动四字
movhps xmm0, [v0+8]
:移动高压缩单精度浮点数
movhps
比SSE4.1 pinsrq xmm0, [v1], 1
略高效(2 微指令,不能在英特尔 Sandybridge 系列上进行微熔:1 微指令用于负载端口,1 微指令用于端口 5)。 movhps
是 1 个微融合 uop,但仍需要相同的后端端口:加载 + 随机播放。
参见 Agner Fog 的 x86 优化指南;他有一章是关于 SIMD 的,其中很大一部分是关于数据移动的。 https://agner.org/optimize/ 并查看https://***.com/tags/x86/info 中的其他链接。
要取出数据,movups
可以用作存储,movlps
/movhps
也可以分散 qword 的一半。 (但不要使用 movlps
作为负载 - 它会合并创建虚假依赖项与 movq
或 movsd
。)
movlps
比movq
短 1 个字节,但两者都可以将 xmm 寄存器的低 64 位存储到内存中。编译器通常会忽略存储的域交叉(vec-int 与 vec-fp),因此您也应该:当它们与存储完全相同时,通常使用 SSE1 ...ps
指令。 (不适用于 reg-reg 移动;Nehalem 可以在 movaps
在整数 SIMD 之间减慢速度,例如 paddd
,反之亦然。)
在所有情况下,AFAIK,除了实际的加法/乘法指令之外,没有 CPU 关心 float
与 double
,没有单独的 float
和 double
旁路转发域的 CPU。 ISA 设计使该选项保持打开状态,但在实践中,通过使用movups
或movaps
围绕double
的向量进行复制来保存字节永远不会受到惩罚。或者使用movlps
而不是movlpd
。 double
洗牌有时很有用,因为 unpcklpd
类似于 punpcklqdq
(交错 64 位元素),而unpcklps
类似于 punpckldq
(交错 32 位元素)。
【讨论】:
也许还可以谈谈整数域和浮点域,选择movups
和movdqu
中的哪一个。以上是关于如何将两个打包的 64 位四字加载到 128 位 xmm 寄存器中的主要内容,如果未能解决你的问题,请参考以下文章
在 64 位机器上,我可以安全地并行处理 64 位四字的各个字节吗?