广播 DWORD 到 YMM

Broadcasting DWORD to YMM

我只是想知道下面的代码是否:

mov eax, r9d    ; eax = j
mul n           ; eax = n * j
shl eax, 2      ; eax = 4 * n * j
                ; now I want to 'broadcast' this to YMM, like so:
                ; ymm = { eax, eax, eax, eax, eax, eax, eax, eax }
  ; This requires AVX512, not just AVX2
  ; vpbroadcastd ymm7, eax
  movd xmm7, eax             ; therefore I must do this workaround?
  vpbroadcastd ymm7, xmm7    ; and finally, the result

能否以某种方式对其进行简化或优化?

是的,如果您没有 AVX512,对于 Intel 和 AMD CPU,vmovd + vpbroadcastd 是正常方法。


我看到 2 个优化:

mul n 替换为 imul r9d, n,因为无论如何您都没有使用乘法结果的 EDX 高半部分。 2-operand imul r32, r/m32 在所有现代 CPU 上都是一个 uop; mul r/m32 需要多个。 https://uops.info/ https://agner.org/optimize/。 (当然,如果 n 是立即数,imul eax, r9d, n*4)。

movd xmm7, eax 上使用 VEX 前缀。即 vmovd xmm7, eax. 如果在 legacy-SSE movd 写入 xmm7 时任何 YMM 寄存器的上半部分脏了,它将触发 AVX-SSE 转换惩罚在哈斯韦尔和冰湖上。 ( 包含 HSW/ICL 和 SKL 使用的不同策略的详细信息。)


如果没有 AVX512,是的,它需要一个 uop(如 movd 指令)将数据从 GP-integer 域传输到 SIMD 域,而且那个 uop 也不能广播。然后你需要另一个 uop 来洗牌。

正如@chtz 指出的那样,如果英特尔 CPU 后端的端口 5 压力是包含此循环的主要瓶颈(而不是总 front-end uops 或延迟),您可以 mov 存储(例如到堆栈)并 vpbroadcastd 重新加载。

vmovd xmm, r32vpbroadcastd 都只能在 Intel CPU 的端口 5 上 运行。但是存储是 micro-fused p237 + p4,broadcast-load(32 位或更宽的元素)纯粹在加载端口处理,不需要 ALU uop,所以总成本仍然是 2 front-end 微指令在 Intel CPU 上,成本为 p237+p4 + p23。而不是 2p5Store-forwarding ~5 或 6 个周期的延迟实际上类似于 1 到 3 个周期 vmovd + 3 个周期 vpbroadcastd 所以也许这值得考虑对于来自寄存器的 32 位和 64 位广播,如果 load/store 端口没有太大压力。

(可能包括 SSE3 movddup broadcast-loads 到 XMM 寄存器中,尽管 in-lane 洗牌只有 1 个周期延迟所​​以 movd + xmm 洗牌在 Haswell 上只有大约 4 个周期延迟和稍后。)

测量 movd xmm, r / movd r, xmm 往返的延迟很容易,但很难确定哪条指令有哪条延迟。它们可能只是 1 个周期的 ALU 加上 Skylake 上的 1 个周期的旁路延迟。 Haswell 显然在每个方向上都有 1 个周期 movdhttps://uops.info/ just measures a not-very-tight upper bound on latency by putting it in a loop with instructions to create a loop-carried dependency, and assuming others have 1-cycle latency. https://agner.org/optimize/ makes a guess on how to split up latency for a pair of instructions. Perhaps one could do better by including store-forwarding for one direction and an ALU transfer for the other, but store-forwarding latency on Sandybridge-family is notoriously variable, faster if you don't try to reload right away. (e.g. useless stores can speed up the critical path through a store-forwarding bottleneck. )。并且不能假定整数存储和 vmovd xmm 重新加载之间的 store-forwarding 具有与整数重新加载相同的延迟。

Skylake 的 movd xmm<->eax 往返总共有 4 个周期延迟,高于 Sandybridge / Haswell 中的 2 个。这可能是 2 和 2 有旁路延迟,或者 1 和 3 没有告诉我们哪个方向更慢。

Zen 的是 6 个周期,所以每个方向可能是 3 个周期。


AVX512F vpbroadcastd ymm, r32是single-uop(端口5),所以有AVX512就好了