Ian*_*oyd 3 language-agnostic x86 assembly sse2
我有两个 UInt64(即 64 位四字)整数。
sizeof(UInt64)) 边界(如果这对任何有用,我也可以将它们对齐到 16 字节)我如何将它们加载到 xmm 寄存器中,例如xmm0:
我发现:
movq xmm0, v[0]
Run Code Online (Sandbox Code Playgroud)
但这只会移动v[0],并将高 64 位xmm0设置为零:
xmm0
0000000000000000 24FC18D93B2C9D8F
正如 W. Chang 指出的那样,字节序化很小,我认为它是相反的:
我的难题是如何让他们进来,又如何让他们出去。
对于未对齐的 128 位加载,请使用:
movups xmm0, [v0]:移动未对齐的单精度浮点为float或double数据。(movupd长 1 个字节,但永远不会产生性能差异。)movdqu xmm0, [v0]:移动未对齐的双四字即使两个四字跨缓存行边界拆分,这通常也是吞吐量的最佳选择。(在 AMD CPU 上,当负载不适合缓存线的对齐 32 字节块时,可能会受到惩罚,而不仅仅是 64 字节缓存线边界。但在英特尔上,64 字节内的任何未对齐缓存线是免费的。)
如果您的负载提供整数 SIMD 指令,您可能需要movdqu,即使movups机器代码短 1 个字节。一些 CPU 可能会关心不同类型负载的“域交叉”。对于存储来说,这并不重要,许多编译器movups甚至总是使用整数数据。
另请参阅如何准确地对 x86_64 上的未对齐访问速度进行基准测试,以了解有关未对齐加载成本的更多信息。(SIMD 和其他)。
如果没有连续的,你最好的选择是
movq xmm0, [v0]:移动四字movhps xmm0, [v1]:移动高压缩单精度浮点数。(没有整数等价物,无论如何都使用movhpd它。永远不要使用,它不再有任何好处,因为没有 CPU 关心 double 与 float shuffle。)或者在旧的 x86 上,例如 Core2 和其他旧的 CPU,movups即使 16 个字节都来自同一个缓存行,它们也很慢,您可能会使用
movq xmm0, [v0]:移动四字movhps xmm0, [v0+8]:移动高压缩单精度浮点数movhps比SSE4.1pinsrq xmm0, [v1], 1稍有效率(2 uop,不能在 Intel Sandybridge 系列上进行微熔断器:1 uop 用于负载端口,1 uop 用于端口 5)。 movhps是 1 个微融合 uop,但仍然需要相同的后端端口:加载 + shuffle。
请参阅 Agner Fog 的 x86 优化指南;他有一个关于 SIMD 的章节,其中有很大一部分是关于数据移动的。https://agner.org/optimize/ 并查看其他环节https://stackoverflow.com/tags/x86/info。
要取回数据,movups可以用作存储,因此可以movlps/movhps分散 qword 的一半。(但不要movlps用作负载 - 它会合并创建一个错误的依赖项 vs. movqor movsd。)
movlps比 短 1 个字节movq,但两者都可以将 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. doubleshuffle 有时很有用,因为unpcklpd就像punpcklqdq (交错 64 位元素)与unpcklps就像punpckldq(交错 32 位元素)。