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



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

如果我从未在代码中使用单个 SSE 指令,那么我需要使用VZEROUPPER有什么性能原因吗?

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

参见 首次使用 AVX 256 位矢量会减慢 128 位矢量的速度,AVX标量操作 re:如果有任何鞋面脏污,则将 128 位 AVX 操作隐式扩大到 256 位。 (包括出于"轻"与"重"涡轮增压限制的目的)。 这可能是使用vzeroupper的一个原因,特别是如果您的程序的某些区域使用 256 位向量(尤其是"轻量级"指令,例如乘法以外的整数内容),而其他区域则大量使用 128 位 FMA。 如果没有vzeroupper,128位FP数学指令可以降低您的最大涡轮增压,就好像您一直在使用繁重的256位指令一样。(如果你无论如何都在这样做,也许没什么大不了的)。


您是正确的,如果您的整个程序不使用任何写入寄存器xmm的非 VEX 指令,则无需vzeroupper即可避免状态转换惩罚。

请注意,非 VEX 指令可能潜伏在 CRT 启动代码和/或动态链接器或其他高度不明显的地方。

也就是说,非 VEX 指令在运行时只能造成一次性惩罚。 反之则不然:一条VEX-256指令可以使非VEX指令在程序的其余部分(或仅使用该寄存器)变慢。


混合使用VEX和EVEX时不会受到任何惩罚,因此无需在那里使用vzeroupper


On Skylake-AVX512:vzerouppervzeroall是在弄脏ZMM寄存器后恢复max-turbo的唯一方法,假设您的程序仍然使用xmm/ymm0上的任何SSE*,AVX1或AVX2指令。15.

参见 Skylake 是否需要 vzeroupper 才能在仅读取 ZMM 寄存器的 512 位指令写入 k 掩码后恢复涡轮时钟? - 仅读取 ZMM 不会导致这种情况。

发布者 @BeeOnRope 在聊天中:

AVX-512指令对周围代码有一个新的、非常糟糕的影响:一旦执行了512位指令(也许除了不写入zmm寄存器的指令),内核就会进入"上256脏状态"。在这种状态下,任何后来的标量 FP/SSE/AVX 指令(任何使用 xmm 或 ymm regs 的指令)都将在内部扩展到 512 位。这意味着处理器将被锁定为不高于AVX turbo(所谓的"L1许可证"),直到vzeroupper或vzeroall发布。

与早期AVX和遗留的非VEX SSE(仍然存在于Skylake Xeon)的"脏上128"问题不同,由于频率较低,这将减慢所有代码的速度,但没有"合并uops"或错误依赖项或类似的东西:只是较小的操作实际上被视为512位宽,以实现零扩展行为。

关于"写低半部分..." -不,这是一个全局状态,只有 vzero 才能让您摆脱它*。即使您弄脏了 zmm 寄存器,但对 ymm 和 xml 使用不同的寄存器,也会发生这种情况。即使唯一的脏指令是像vpxord zmm0, zmm0, zmm0这样的归零成语,也会发生这种情况。不过,写入 zmm16-31 不会发生这种情况

实际上将所有矢量运算扩展到 512 位的描述并不完全正确,因为他后来证实它不会降低 128 位和 256 位指令的吞吐量。 但我们知道,当 512 位 uop 处于运行状态时,端口 1 上的矢量 ALU 会关闭。 (因此,通常通过端口 0 和 1 访问的 256 位 FMA 单元可以组合成一个 512 位单元,用于所有 FP 数学、整数乘法以及可能的其他一些东西。 一些 SKX Xeon 在端口 5 上具有第二个 512 位 FMA 单元,有些则没有。


对于仅使用 AVX1/AVX2 后的最大睿频(包括在 Haswell 等早期 CPU 上):如果执行单元的上半部分一段时间没有使用(有时允许更高的 Turbo 时钟速度),则机会性地关闭它们取决于最近是否使用了 YMM 指令,而不是上半部分是否脏。 因此,AFAIKvzeroupper并不能帮助CPU 在使用 AVX1/AVX2 后更快地取消时钟速度限制,对于最大睿频低于 256 位的 CPU。

这与英特尔的Skylake-AVX512(SKX/Skylake-SP)不同,其中AVX512有点"螺栓连接"。

<小时 />

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

因为 CPU 仍然知道 ymm 上部状态是干净的还是脏的。

如果它是干净的,我认为xsaveoptxsavec可以更紧凑地写出 FPU 状态,根本不存储全零的上半部分(只是设置一点说它们是干净的)。 请注意,在 SSE/AVX 的状态转换图中,xsave/xrstor是图片的一部分。

仅当您的代码在此之后很长一段时间内不使用任何 256b 指令时才值得考虑为此提供额外的vzeroupper,因为理想情况下,在下次使用 256 位向量之前,您不会有任何上下文切换/CPU 迁移。

这可能不适用于AVX512 CPU:vzeroupper/vzeroall不要碰ZMM16。31,只有ZMM0..15.所以vzeroall后你仍然可以有很多脏状态。


(理论上合理):肮脏的上半部分可能会占用物理寄存器(尽管 IDK 在任何实际 CPU 上都证明这是正确的)。 如果是这样,它将限制 CPU 查找指令级并行性的无序窗口大小。 (ROB尺寸是另一个主要限制因素,但PRF尺寸可能是瓶颈。

在 Zen2 之前的 AMD CPU 上可能也是如此,其中 256b 操作被分成两个 128b 操作。 YMM 寄存器在内部作为两个 128 位寄存器处理,例如vmovaps ymm0, ymm1以零延迟重命名低 128,但上半部分需要一个 uop。 (参见Agner Fog的microarch pdf)。 不过,目前尚不清楚vzeroupper是否真的可以放弃上半部分的重命名。 AMD Zen 上的归零习语(与 SnB 系列不同)仍然需要一个后端 uop 来写入寄存器值,即使是 128b 低半部分;只有消除移动才能避免后端 UOP。 因此,可能没有可以重命名上层的物理零寄存器。

不过,ROB 大小/PRF 大小博客文章中的实验表明,FP 物理寄存器文件条目在 Sandybridge 中是 256 位的。vzeroupper不应该在采用 AVX/AVX2 的主流英特尔 CPU 上释放更多寄存器。 Haswell 风格的过渡惩罚足够慢,以至于可能会耗尽 ROB 以保存或恢复鞋面到未重命名的单独存储,而不会耗尽有价值的 PRF 条目。

Silvermont不支持AVX。 它使用单独的报废寄存器文件作为架构状态,因此无序 PRF 仅保存推测执行结果。 因此,即使它确实支持具有 128 位半部分的 AVX,带有脏上半部分的陈旧 YMM 寄存器可能不会占用重命名寄存器文件中的额外空间。

KNL(Knight's Landing/Xeon Phi)是专门为运行AVX512而设计的,因此大概它的FP寄存器文件具有512位条目。 它基于 Silvermont,但内核的 SIMD 部分是不同的(例如,根据 Agner Fog,它可以对 FP/矢量指令进行重新排序,而 Silvermont 只能推测性地执行它们,而不能在 FP/矢量管道中对它们进行重新排序)。 尽管如此,KNL也可能使用单独的退休寄存器文件,因此即使能够拆分512位条目来存储两个256位向量,脏的ZMM鞋面也不会占用额外的空间。 这不太可能,因为KNL上仅AVX1/AVX2的更大无序窗口不值得花费晶体管。

vzeroupperKNL 比主流英特尔 CPU 慢得多(在 64 位模式下每 36 个周期一个),因此您可能不想使用,尤其是对于微小的上下文切换优势。


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

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

@Mysticial报告了YMM与ZMM的长FP依赖链的代码意外变慢,但其他方面代码相同,但后来的实验不同意以下结论:当上面的256位脏时,SKX使用2x 256位寄存器作为ZMM寄存器。

<小时 />

AVX-512和冰湖/蓝宝石急流上的物理寄存器

https://chipsandcheese.com/2023/01/15/golden-coves-vector-register-file-checking-with-official-spr-data/

[...]
虽然在服务器Ice Lake上进行测试表明英特尔的机制并没有那么复杂。相反,内核只是记住上组ZMM寄存器是否正在使用中。如果使用AVX-512引入的任何额外寄存器(即ZMM16至31),Ice Lake将保留另外16个寄存器以保持已知良好的状态。无论您触摸其中一个还是全部都没有关系。Golden Cove是Ice Lake的继任者,可以使用类似的机制。

。 因此,Zen 4 没有采用与 Ice Lake 相同的寄存器保存优化。

但不幸的是,我认为vzeroupper/vzeroall对此没有帮助; 它不会影响ZMM16。31,因此它无法将它们恢复到"干净"状态并为无序执行释放额外的 16 个物理寄存器。

如果我理解正确,手动异或归零阻止它们使用物理寄存器(vpxord xmm16, xmm16, xmm16到 xmm31);要么有一个额外的位来指示全零,要么有一个物理零寄存器,重命名器可以将它们指向。 但是,可能仍有 16 个额外的 PRF 条目保留用于退休状态,即使实际的 RAT 条目没有指向它们。

将它们归零后,上下文切换上的xsave/xrstor可能会恢复到 zmm16-31 未使用状态。 CPU 可能必须能够以某种方式恢复到该状态,而不是冷启动或进入深度睡眠状态。

这篇文章还有其他一些有趣的发现,比如 220 个矢量 PRF 条目中只有 320 个能够保存 512 位结果。 因此,只要足够,就使用 256 位指令(例如,从缩小到 256 开始水平减少)可以帮助无序执行者看得更远。

最新更新