如何将两个打包的 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]floatdouble 数据移动未对齐的单精度浮点数。 (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 作为负载 - 它会合并创建虚假依赖项与 movqmovsd。)

movlpsmovq 短 1 个字节,但两者都可以将 xmm 寄存器的低 64 位存储到内存中。编译器通常会忽略存储的域交叉(vec-int 与 vec-fp),因此您也应该:当它们与存储完全相同时,通常使用 SSE1 ...ps 指令。 (不适用于 reg-reg 移动;Nehalem 可以在 movaps 在整数 SIMD 之间减慢速度,例如 paddd,反之亦然。)

在所有情况下,AFAIK,除了实际的加法/乘法指令之外,没有 CPU 关心 floatdouble,没有单独的 floatdouble 旁路转发域的 CPU。 ISA 设计使该选项保持打开状态,但在实践中,通过使用movupsmovaps 围绕double 的向量进行复制来保存字节永远不会受到惩罚。或者使用movlps 而不是movlpddouble 洗牌有时很有用,因为 unpcklpd 类似于 punpcklqdq(交错 64 位元素),而unpcklps 类似于 punpckldq(交错 32 位元素)。

【讨论】:

也许还可以谈谈整数域和浮点域,选择movupsmovdqu 中的哪一个。

以上是关于如何将两个打包的 64 位四字加载到 128 位 xmm 寄存器中的主要内容,如果未能解决你的问题,请参考以下文章

如何在 xmm 寄存器中旋转压缩四字?

在 64 位机器上,我可以安全地并行处理 64 位四字的各个字节吗?

NEON:将 uint8_t 数组加载到 128 位寄存器中

加载 128 位混合浮点 + 整数数据?

将四字移动到 xmm

如果我的编译器不支持它们,如何在C或C ++中添加和减去128位整数?