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

Luk*_*ohl 3 x86 assembly sse simd micro-optimization

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

MOV RAX, 0xFFFFFFFFFFFFFFFF
MOVQ xmm2, RAX
PSHUFD xmm2, xmm2, 0b00001111
PAND xmm1, xmm2
Run Code Online (Sandbox Code Playgroud)

有没有更快的方法?

Pet*_*des 8

您可以通过以下方式更有效地创建常量

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
Run Code Online (Sandbox Code Playgroud)

(另请参阅Agner Fog 的优化指南;他有一节关于动态创建常量。还有什么是动态生成向量常量的最佳指令序列?

或者正如@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.
Run Code Online (Sandbox Code Playgroud)

(作为从内存中加载,movsd零扩展。但对于 reg-reg 移动它并movss保持目标上部不变。)

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

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

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

movsd某些 CPU一样高效,只需要 SSE1:

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

类似地,shufpd并且shufps可以将 的上半部分复制xmm1到归零寄存器的上半部分。(如果您不想破坏原始 reg,则很有用)。但您可以movsd同样轻松、高效地做到这一点。


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