如果您的程序+库不包含 SSE 指令,使用 VZEROUPPER 是否有用?

Is it useful to use VZEROUPPER if your program+libraries contain no SSE instructions?

我知道在混合 SSE 和 AVX 代码时使用 VZEROUPPER 很重要,但是如果我只使用 AVX(和普通的 x86-64 代码)而不使用任何旧的 SSE 指令怎么办?

如果我从不在我的代码中使用单个 SSE 指令,是否有任何性能原因导致我需要使用 VZEROUPPER

这是假设我没有调用任何外部库(可能正在使用 SSE)。

你是对的,如果你的整个程序不使用 any non-VEX 写入 xmm 寄存器的指令,你不需要 vzeroupper 以避免 state-transition 惩罚。

请注意 non-VEX 指令可能潜伏在 CRT 启动代码 and/or 动态链接器或其他高度 non-obvious 的地方。

也就是说,non-VEX 指令只能在 运行 时导致 one-time 惩罚。反之则不然:一条 VEX-256 指令通常可以生成 non-VEX 指令(或仅使用该寄存器).


那里有,不用vzeroupper了。


在 Skylake-AVX512 上:vzerouppervzeroall 是弄脏 ZMM 寄存器 后恢复 max-turbo 的唯一方法,假设您程序仍然在 xmm/ymm0..15.

上使用任何 SSE*、AVX1 或 AVX2 指令

另请参阅 - 仅读取 zmm 不会导致此问题。

Posted by @BeeOnRope in chat:

There is a new, pretty bad effect with AVX-512 instructions on surrounding code: once a 512-bit instruction is executed (except perhaps for instructions that don't write to a zmm register) the core enters an "upper 256 dirty state". In this state, any later scalar FP/SSE/AVX instruction (anything using xmm or ymm regs) will internally be extended to 512 bits. This means the processor will be locked to no higher than the AVX turbo (the so-called "L1 license") until vzeroupper or vzeroall are issued.

Unlike the earlier "dirty upper 128" issue with AVX and legacy non-VEX SSE (which still exists on Skylake Xeon), this will slow down all code due to the lower frequency, but there are no "merging uops" or false dependencies or anything like that: it's just that the smaller operations are effectively treated as 512-bit wide in order to implement the zero-extending behavior.

about "writing the low halves ..." - no, it is a global state, and only vzero gets you out of it*. It occurs even if you dirty a zmm register but use different ones for ymm and xmm. It occurs even if the only dirtying instruction is a zeroing idiom like vpxord zmm0, zmm0, zmm0. It doesn't occur for writes to zmm16-31 though.

他对实际上将所有向量操作扩展到 512 位的描述不太正确,因为他后来证实这不会降低 128 位和 256 位指令的吞吐量.但我们知道,当 512 位微指令运行时,端口 1 上的向量 ALU 会关闭。 (因此通常可通过端口 0 和 1 访问的 256 位 FMA 单元可以组合成一个 512 位单元,用于所有 FP 数学、整数乘法和可能的其他一些东西。一些 SKX Xeons 在端口上有第二个 512 位 FMA 单元5,有些没有。)


对于 max-turbo 在仅使用 AVX1 / AVX2 之后(包括早期的 CPUs,如 Haswell):机会性地关闭执行单元的上半部分如果它们有一段时间没有被使用(有时允许更高的 Turbo 时钟速度)取决于最近是否使用了 YMM 指令,而不是上半部分是否脏了。所以 AFAIK,vzeroupper 帮助 CPU un-throttle 使用 AVX1 / AVX2 后的时钟速度更快,其中 CPUs 256 位的 max turbo 较低。

这与英特尔的 Skylake-AVX512 (SKX / Skylake-SP) 不同,其中 AVX512 有点“固定”。


VZEROUPPER 可能会使上下文切换 稍微 更便宜

因为 CPU 仍然知道 ymm-upper 状态是干净还是脏。

如果干净的话,我认为 xsaveopt or xsavec can write out the FPU state more compactly, without storing the all-zero upper halves at all (just setting a bit that says they're clean). Notice xsave / xrstor 是图片的一部分。

如果您的代码在此之后的 long 时间内不使用任何 256b 指令,那么仅此而已的额外 vzeroupper 才值得考虑,因为理想情况下您赢了在下次使用 256 位向量之前没有任何上下文切换/CPU 迁移。

这可能不适用于 AVX512 CPUs:vzeroupper / vzeroall 不要触及 ZMM16..31,仅触及 ZMM0..15。所以在 vzeroall.

之后你仍然可以有很多脏状态

(理论上合理):脏的上半部分可能正在占用物理寄存器(尽管 IDK 的任何证据表明这在任何真实的 CPUs 上都是正确的) .如果是这样,它将限制 out-of-order window 大小,以便 CPU 找到 instruction-level 并行度。 (ROB 大小是另一个主要限制因素,but PRF size can be the bottleneck。)

这在 Zen2 之前的 AMD CPUs 上可能是正确的,其中 256b ops 被分成两个 128b ops。 YMM 寄存器在内部被处理为两个 128 位寄存器,例如vmovaps ymm0, ymm1 以零延迟重命名低 128,但上半部分需要一个 uop。 (参见 Agner Fog's microarch pdf)。不过,不知道 vzeroupper 是否真的可以放弃上半部分的重命名。 AMD Zen 上的归零习语(不像 SnB-family)仍然需要一个 back-end uop 来写入寄存器值,即使对于 128b 低半部分也是如此;只有 mov-elimination 避免了 back-end uop。因此可能没有可以重命名 uppers 的物理零寄存器。

ROB 大小/PRF 大小的实验 blog post 表明 FP 物理寄存器文件条目在 Sandybridge 中是 256 位的。 vzeroupper 不应在 AVX/AVX2 的主流英特尔 CPU 上释放更多寄存器。 Haswell-style 转换惩罚足够慢,它可能耗尽 ROB 来保存或恢复鞋面到未重命名的单独存储,而不是用完有价值的 PRF 条目。

Silvermont 不支持 AVX。并且它使用 a separate retirement register file 作为架构状态,因此 out-of-order PRF 仅保存推测执行结果。因此,即使它确实支持具有 128 位一半的 AVX,具有脏上半部分的陈旧 YMM 寄存器也可能不会用完重命名寄存器文件中的额外 space。

KNL (Knight's Landing / Xeon Phi) 是专门为 运行 AVX512 设计的,因此大概它的 FP 寄存器文件有 512 位条目。它基于 Silvermont,但核心的 SIMD 部分不同(例如,它可以重新排序 FP/vector 指令,而 Silvermont 只能推测性地执行它们,但不能在 FP/vector 管道内重新排序,根据 Agner Fog 的说法).尽管如此,KNL 也可能使用单独的报废寄存器文件,因此脏 ZMM 鞋面不会消耗额外的 space 即使它能够拆分 512-它存储两个 256 位向量的条目。这不太可能,因为在 KNL 上仅 AVX1/AVX2 的更大 out-of-order window 不值得在上面花费晶体管。

vzeroupper 在 KNL 上比主流英特尔 CPUs 慢得多(在 64 位模式下每 36 个周期一个),所以你可能不会'想用,特别是为了小小的context-switch优势。


在 Skylake-AVX512 上,证据支持向量物理寄存器文件为 512 位宽的结论。

将来 CPU 可能会将物理寄存器文件中的条目配对以存储宽向量,即使它们通常不会像 AMD 对 256 位向量那样解码以分离微指令。

YMM 与 ZMM 的长 FP 依赖链代码意外减速但其他代码相同,但后来的实验不同意 SKX 使用 2x 256 位寄存器文件条目作为 ZMM 寄存器的结论高 256 位是脏的。