首页 > 解决方案 > X86:如何将xmm0的下半部分设置为0,而不影响上半部分?

问题描述

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

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

有更快的方法吗?

标签: assemblyx86ssesimdmicro-optimization

解决方案


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

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 的优化指南;他有一个关于动态创建常量的部分。还有什么是动态生成向量常量的最佳指令序列?

或者正如@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 的 port5 上运行。Ryzenmovsd xmm,xmm在比pblendw. 低功耗 Atom/Silvermont 在比 pblendw 更多的端口上运行 movsd,但 Goldmont 和 KNL 对此和 movsd 有 2 个/时钟的吞吐量。所以它仍然永远不会比 movsd 更好。
  • SSE4.1blendpd xmm1, xmm2, 0b01(或blendps) - 与 vpblendd 一样高效,但如果在整数指令之间使用会产生绕过转发延迟。如果您在吞吐量方面遇到瓶颈,这可能没问题,尤其是在您必须避免后端压力的情况下。
  • AVX2:vpblendd xmm1, xmm1, xmm2, 0b0011- 在任何 AVX2 CPU 的任何 ALU 端口上运行。

一些 CPU 也可能在整数指令之间有一个旁路延迟movsd,但 Sandybridge 系列对 shuffle 非常宽容。

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

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

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


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


推荐阅读