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

cpjpxq1n  于 2022-11-13  发布在  其他
关注(0)|答案(1)|浏览(216)

我知道在混合SSE和AVX代码时使用VZEROUPPER很重要,但是如果我只使用AVX(和普通的x86-64代码)而不使用任何传统SSE指令呢?
如果我从来没有在代码中使用过一条SSE指令,那么是否有任何性能原因需要使用VZEROUPPER
这是假设我没有调用任何外部库(可能正在使用SSE)。

pgpifvop

pgpifvop1#

另请参阅First use of AVX 256-bit vectors slows down 128-bit vector and AVX scalar ops re:如果任何上层是脏的,则将128位AVX操作隐式扩展到256位。这可能是使用vzeroupper的一个原因,特别是当程序的某些区域使用256位矢量时(特别是“轻量级”指令,如乘法以外的整数填充),以及其他大量使用128位FMA的指令。如果没有vzeroupper,128位FP数学指令可能会降低最大加速,就像您一直在使用繁重的256位指令一样。(如果您正在这样做,可能没什么大不了的)。
如果您的整个程序不使用 * 任何 * 写入xmm寄存器的非VEX指令,您就不需要vzeroupper来避免状态转换惩罚,这一点是正确的。
请注意,非VEX指令可能潜伏在CRT启动代码和/或动态链接器中,或其他非常不明显的位置。
也就是说,非VEX指令在运行时只能造成一次性惩罚。反之则不然:一条VEX-256指令可以使非VEX指令在程序其余部分中总体上(或仅对于寄存器)变慢。
这里有no penalty when mixing VEX and EVEX,所以不需要使用vzeroupper

在Skylake-AVX 512上:vzerouppervzeroall是弄脏ZMM寄存器后恢复max-turbo的唯一方法,前提是您的程序仍然使用xmm/ymm0..15上的任何SSE*、AVX 1或AVX 2指令。

另请参阅Does Skylake need vzeroupper for turbo clocks to recover after a 512-bit instruction that only reads a ZMM register, writing a k mask?-仅阅读zmm不会导致此问题。
Posted by @BeeOnRope在聊天中发表:
AVX-512指令对周围代码有一个新的、相当糟糕的影响:一旦执行了512位指令核心进入“上部256脏状态”。在此状态中,任何后面的标量FP/SSE/AVX指令(任何使用xmm或ymm寄存器的寄存器)将在内部扩展到512位。这意味着处理器将被锁定为不高于AVX turbo(即所谓的“L1许可证”),直到颁发了vzeroupper或vzeroall。
与AVX和传统非VEX SSE(Skylake Xeon上仍然存在)早期的“脏的上部128”问题不同,由于频率较低,这将降低所有代码的速度,但不会出现“合并微操作”或错误依赖关系或类似问题:只是为了实现零扩展行为,较小的操作被有效地视为512位宽。
关于“写入下半部分......”-不,这是一种全局状态,只有vzero 才能让您摆脱这种状态*。即使您弄脏了zmm寄存器,但对ymm和xmm使用了不同的寄存器,也会发生这种情况。即使唯一弄脏的指令是vpxord zmm0, zmm0, zmm0之类的置零习惯,也会发生这种情况。不过,写入zmm 16 -31时不会发生这种情况
他关于 * 实际上 * 将所有向量操作扩展到512位的描述并不完全正确,因为他后来证实了这并不会降低128位和256位指令的吞吐量。关闭端口1上向量ALU。(因此,通常可通过端口0和1访问的256位FMA单元可以合并成一个512位单元,用于所有FP数学运算、整数乘法可能还有一些其他的东西。一些SKX Xeons在端口5上有第二个512位FMA单元,一些没有。

对于仅使用AVX 1/AVX 2(包括Haswell等早期CPU)后的最大加速:如果执行单元的上半部分有一段时间没有被使用,则有机会关闭它们(有时允许更高的Turbo时钟速度),这取决于最近是否使用过YMM指令,而不是上半部分是否脏。因此,AFAIK,vzeroupper * 不会 * 帮助CPU在使用AVX 1/AVX 2后更快地取消对时钟速度的节流。对于256位的最大睿频加速较低的CPU。

这与英特尔的Skylake-AVX 512(SKX / Skylake-SP)不同,后者的AVX 512有些“硬接”。

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

因为CPU仍然知道ymm-upper状态是干净的还是脏的。
如果它是干净的,我认为xsaveoptxsavec可以更紧凑地写出FPU状态,而根本不存储全零的上半部分(只是设置一个位表示它们是干净的)。请注意,在SSE/AVX的状态转换图中,xsave/xrstor是图片的一部分。
仅当代码在此之后 * 很长 * 时间内不使用任何256 b指令时,才值得考虑为此使用额外的vzeroupper,因为理想情况下,在下次使用256位向量之前,您不会有任何上下文切换/ CPU迁移。
这可能不太适用于AVX 512 CPU:vzeroupper/vzeroall不要接触ZMM16..31,只接触ZMM0..15。因此,在vzeroall之后,您仍然可以有很多脏状态。
(理论上合理):脏的上半部分可能会占用物理寄存器(尽管IDK没有任何证据表明这在任何真实的CPU上都是正确的)。如果是这样,则会限制CPU查找指令级并行的无序窗口大小。(ROB大小是另一个主要限制因素,but PRF size can be the bottleneck。)

这在Zen 2之前的AMD CPU上可能是真的,其中256 b操作被分成两个128 b操作。YMM寄存器在内部被作为两个128位寄存器处理,并且例如vmovaps ymm0, ymm1以零延迟重命名低128,但是需要一个uop用于上半部分。(参见Agner Fog's microarch pdf)。不过,vzeroupper是否真的可以放弃对上半部分的重命名还不得而知。(与SnB系列不同)仍然需要后端微操作来写入寄存器值,即使是128 b的低半部分;只有mov-elimination避免了后端微操作。因此可能不存在可将uppers重命名到其上的物理零寄存器。
ROB大小/ PRF大小blog post的实验表明,在Sandybridge中,FP物理寄存器文件条目为256位。vzeroupper不应在采用AVX/AVX 2的主流英特尔CPU上释放更多寄存器。Haswell类型的转换代价非常慢,可能会耗尽ROB,以便将上层数据保存或恢复到未重命名的单独存储中,而不会耗尽宝贵的PRF条目。
Silvermont不支持AVX,而且它使用a separate retirement register file作为体系结构状态,因此乱序PRF只保存推测性执行结果。因此,即使它支持128位的AVX,一个上半部分为脏的陈旧YMM寄存器也可能不会占用重命名寄存器文件中的额外空间。
克诺尔(Knight 's Landing / Xeon Phi)是专为运行AVX 512而设计的,因此它的FP寄存器文件可能有512位条目。它基于Silvermont,但内核的SIMD部分有所不同(例如,它可以对FP/矢量指令进行重新排序,而Silvermont只能推测性地执行它们,但不能在FP/矢量流水线内对它们进行重新排序,根据Agner Fog的说法)。KNL还可以使用一个独立的退休寄存器文件,因此脏ZMM上层不会占用额外的空间,即使它能够将一个512位条目拆分为两个256位向量,但这是不可能的,因为KNL上仅用于AVX 1/AVX 2的更大的乱序窗口不值得花费晶体管。

vzeroupper在KNL上的运行速度比主流英特尔CPU(64位模式下每36个周期运行一个)要慢得多,因此您可能不想使用,尤其是仅考虑到上下文切换的微小优势。

在Skylake-AVX 512上,证据支持矢量物理寄存器文件为512位宽的结论。
一些未来的CPU可能会将物理寄存器文件中的条目配对以存储宽向量,即使它们通常不会像AMD对256位向量那样解码为单独的微操作。
@Mysticial报告了使用YMM和ZMM(其他方面相同的代码)时,使用长FP相关性链的代码出现了意外的速度减慢,但后来的实验不同意这样的结论:当高256位为脏时,SKX为ZMM寄存器使用2x 256位寄存器文件条目。

相关问题