如何将两个打包的 64 位四字加载到 128 位 xmm 寄存器中
How to load two packed 64-bit quadwords into a 128-bit xmm register
我有两个 UInt64(即 64 位四字)整数。
- 它们与 8 字节(
sizeof(UInt64)
)边界对齐(如果这对任何事情有用,我也可以将它们与 16 字节对齐)
- 它们被打包在一起,所以它们在内存中并排
如何将它们加载到 xmm 寄存器中,例如xmm0
:
我发现:
movq xmm0, v[0]
但这只会移动 v[0]
,并将 xmm0
中的高 64 位设置为零:
xmm0 0000000000000000 24FC18D93B2C9D8F
奖金问题
- 我如何让他们退出?
- 如果它们在内存中不是并排的怎么办?
- 如果它们是 4 字节对齐的怎么办?
编辑
正如 W. Chang 指出的那样,字节顺序化很少,我同意它是相反的:
我的难题是如何让他们进来,又让他们出去。
对于未对齐的 128 位加载,使用:
movups xmm0, [v0]
:为float
或double
数据移动未对齐的单精度浮点数。 (movupd
长 1 个字节,但不会造成性能差异。)
movdqu xmm0, [v0]
: 移动未对齐的双四字
即使两个四字跨高速缓存行边界拆分,这通常也是吞吐量的最佳选择。 (在 AMD CPU 上,当负载不适合缓存行的对齐 32 字节块时,可能会受到惩罚,而不仅仅是 64 字节缓存行边界。但在 Intel 上,64 字节内存中的任何未对齐缓存行是免费的。)
如果您的负载正在提供整数 SIMD 指令,您可能需要 movdqu
,即使 movups
在机器代码中短了 1 个字节。一些 CPU 可能关心不同类型负载的“域交叉”。对于存储并不重要,许多编译器总是使用 movups
甚至整数数据。
另请参阅 以了解有关未对齐负载成本的更多信息。 (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 微指令,不能在 Intel Sandybridge 系列上进行微熔断:1 微指令用于负载端口,1 微指令用于端口 5)。 movhps
是 1 个微融合 uop,但仍需要相同的后端端口:load + shuffle。
参见 Agner Fog 的 x86 优化指南;他有一个关于 SIMD 的章节,其中很大一部分是关于数据移动的。 https://agner.org/optimize/ And see other links in https://whosebug.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
,没有 CPU 具有单独的 float
和 double
绕过转发域。 ISA 设计保留了该选项,但在实践中,通过使用 movups
或 movaps
复制 double
向量来节省字节永远不会受到惩罚。或者使用 movlps
而不是 movlpd
。 double
混洗有时很有用,因为 unpcklpd
类似于 punpcklqdq
(交错 64 位元素)而 unpcklps
类似于 punpckldq
(交错 32 位元素) ).
我有两个 UInt64(即 64 位四字)整数。
- 它们与 8 字节(
sizeof(UInt64)
)边界对齐(如果这对任何事情有用,我也可以将它们与 16 字节对齐) - 它们被打包在一起,所以它们在内存中并排
如何将它们加载到 xmm 寄存器中,例如xmm0
:
我发现:
movq xmm0, v[0]
但这只会移动 v[0]
,并将 xmm0
中的高 64 位设置为零:
xmm0
0000000000000000 24FC18D93B2C9D8F
奖金问题
- 我如何让他们退出?
- 如果它们在内存中不是并排的怎么办?
- 如果它们是 4 字节对齐的怎么办?
编辑
正如 W. Chang 指出的那样,字节顺序化很少,我同意它是相反的:
我的难题是如何让他们进来,又让他们出去。
对于未对齐的 128 位加载,使用:
movups xmm0, [v0]
:为float
或double
数据移动未对齐的单精度浮点数。 (movupd
长 1 个字节,但不会造成性能差异。)movdqu xmm0, [v0]
: 移动未对齐的双四字
即使两个四字跨高速缓存行边界拆分,这通常也是吞吐量的最佳选择。 (在 AMD CPU 上,当负载不适合缓存行的对齐 32 字节块时,可能会受到惩罚,而不仅仅是 64 字节缓存行边界。但在 Intel 上,64 字节内存中的任何未对齐缓存行是免费的。)
如果您的负载正在提供整数 SIMD 指令,您可能需要 movdqu
,即使 movups
在机器代码中短了 1 个字节。一些 CPU 可能关心不同类型负载的“域交叉”。对于存储并不重要,许多编译器总是使用 movups
甚至整数数据。
另请参阅
如果不是连续的,你最好的选择是
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 微指令,不能在 Intel Sandybridge 系列上进行微熔断:1 微指令用于负载端口,1 微指令用于端口 5)。 movhps
是 1 个微融合 uop,但仍需要相同的后端端口:load + shuffle。
参见 Agner Fog 的 x86 优化指南;他有一个关于 SIMD 的章节,其中很大一部分是关于数据移动的。 https://agner.org/optimize/ And see other links in https://whosebug.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
,没有 CPU 具有单独的 float
和 double
绕过转发域。 ISA 设计保留了该选项,但在实践中,通过使用 movups
或 movaps
复制 double
向量来节省字节永远不会受到惩罚。或者使用 movlps
而不是 movlpd
。 double
混洗有时很有用,因为 unpcklpd
类似于 punpcklqdq
(交错 64 位元素)而 unpcklps
类似于 punpckldq
(交错 32 位元素) ).