X86: 如何将 xmm0 的下半部分设置为 0,而不影响上半部分?

X86: How to set lower half of xmm0 to 0, without affecting the upper half?

我使用 xmm0 有 128 位的系统。 我想将 [63...0] 设置为零,而不影响 [127...64]。 我使用:

MOV RAX, 0xFFFFFFFFFFFFFFFF
MOVQ xmm2, RAX
PSHUFD xmm2, xmm2, 0b00001111
PAND xmm1, xmm2

有没有更快的方法?

您可以通过

更有效地创建常量
pcmpeqd xmm2,xmm2       ; xmm2 = all-ones.  Needs any ALU port
pslldq  xmm2, 8         ; left shift by 8 bytes.  Needs the shuffle port

PAND    xmm1, xmm2

(另见 Agner Fog's optimization guide; he has a section on creating constants on the fly. Also

或者如@RossRidge 所建议的那样,如果您经常需要它以在高速缓存中保持热,那么使用常量的内存源操作数可能是最有效的,但不能只是将其从循环中提升并保持在循环中一个寄存器。


或混入新的低8字节零.

pxor   xmm2, xmm2       ; xmm2=0; very efficient on Intel CPUs; no back-end uop

movsd  xmm1, xmm2       ; runs on port5 only on Intel CPUs, like shuffles.

(作为内存加载,movsd 零扩展。但是对于 reg-reg 移动它并且 movss 保留目标上半部分不变。)

混合的替代方法更有效,但需要的不仅仅是 SSE2:

  • SSE4.1:pblendw xmm1, xmm2, 0b00001111 - 一切都更差(或速度相同但代码大小更差)。在 Intel 的端口 5 上仍然只有 运行s。 Ryzen 运行s movsd xmm,xmm 在比 pblendw 更多的端口上。低功耗 Atom/Silvermont 运行s movsd 在比 pblendw 更多的端口上,但 Goldmont 和 KNL 对此和 movsd 有 2/clock 吞吐量。所以还是比不上movsd.
  • SSE4.1 blendpd xmm1, xmm2, 0b01(或blendps)- 与 vpblendd 一样高效,但如果在整数指令之间使用,会产生旁路转发延迟。如果您在吞吐量方面遇到瓶颈,这可能没问题,特别是如果您必须避免后端压力。
  • AVX2:vpblendd xmm1, xmm1, xmm2, 0b0011 - 运行s 在任何 AVX2 CPU 上的任何 ALU 端口上 CPU。

一些 CPUs 也可能在整数指令之间有 movsd 的旁路延迟,但 Sandybridge 系列对随机播放非常宽容。

在某些 CPU 上与 movsd 一样有效,仅需要 SSE1:

  • movhlps xmm1, xmm2 - 将 xmm1 的低 qword 替换为 xmm2 的高 qword(也是零)。在 Ryzen 或 Silvermont 上效率较低。

类似地,shufpdshufps 可以将 xmm1 的上半部分复制到置零寄存器的上半部分。 (如果您不想破坏原始 reg,则很有用)。但是您可以使用 movsd 轻松高效地做到这一点。


也可能:movlps xmm, [mem] 加载零,可能是您刚刚存储到堆栈中。它不允许注册源操作数,并且需要 Intel 上的 port5 uop(洗牌/不常见混合)。它可以微融合到一个融合域 uop 中,但它比使用内存源的 pand 更糟糕,因为它可以 运行 在更少的端口上。