对于未对齐的 128 位加载,请使用:
-
movups xmm0, [v0]: 移动未对齐的单精度浮点 for
float
or double
数据。 (movupd
多了 1 个字节,但不会造成性能差异。)
-
movdqu xmm0, [v0]: 移动未对齐的双四字
即使两个四字跨缓存行边界分开,这通常也是吞吐量的最佳选择。 (在 AMD CPU 上,当负载不适合缓存行的对齐 32 字节块(而不仅仅是 64 字节缓存行边界)时,可能会受到惩罚。但在 Intel 上,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.1 pinsrq xmm0, [v1], 1(2 uop,无法在 Intel Sandybridge 系列上进行微熔丝:1 uop 用于负载端口,1 个用于端口 5)。movhps
是 1 个微融合 uop,但仍然需要相同的后端端口:加载 + 洗牌。
请参阅 Agner Fog 的 x86 优化指南;他有一章是关于 SIMD 的,其中很大一部分是关于数据移动的。https://agner.org/optimize/并查看其他链接https://stackoverflow.com/tags/x86/info.
为了取回数据,movups
可以作为商店,也可以movlps
/movhps
分散 qword 的两半。 (但不要使用movlps
作为负载 - 它合并创建了错误的依赖关系与错误的依赖关系。movq
or movsd
.)
movlps
比 短 1 个字节movq
,但两者都可以将 xmm 寄存器的低 64 位存储到内存中。编译器通常会忽略存储的域交叉(vec-int 与 vec-fp),因此您也应该:通常使用 SSE1...ps
当它们与商店完全相同时的说明。 (不适用于 reg-reg 移动;Nehalem 可以放慢速度movaps
整数 SIMD 之间,如paddd
, 或相反亦然。)
在所有情况下,据我所知,没有 CPU 关心float
vs. double
对于除实际加法/乘法指令以外的任何指令,没有具有单独的CPUfloat
and double
绕过转发域。 ISA 设计保留了该选项,但实际上,通过使用来节省字节永远不会受到惩罚movups
or movaps
围绕向量进行复制double
。或者使用movlps
代替movlpd
. double
随机播放有时很有用,因为unpcklpd
就好像punpcklqdq
(交错 64 位元素)与unpcklps
就像punpckldq
(交织 32 位元素)。