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

标签 performance assembly x86 avx micro-optimization

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

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

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

最佳答案

如果您的整个程序不使用任何写入 xmm 的非 VEX 指令,那么您是对的。注册,你不需要 vzeroupper以避免状态转换惩罚。
请注意,非 VEX 指令可能潜伏在 CRT 启动代码和/或动态链接器中,或其他高度不明显的地方。
也就是说,非 VEX 指令在运行时只能导致一次性惩罚。反之则不然:一条 VEX-256 指令通常可以生成非 VEX 指令(或仅使用该寄存器)slow for the rest of the program .

no penalty when mixing VEX and EVEX ,所以不需要使用 vzeroupper那里。

在 Skylake-AVX512 上:vzerouppervzeroall是在弄脏 ZMM 寄存器后恢复 max-turbo 的唯一方法 ,假设您的程序仍然在 xmm/ymm0..15 上使用任何 SSE*、AVX1 或 AVX2 指令。
另见 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 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 位 uops 正在运行时,端口 1 上的向量 ALU 会关闭。 (因此,通常可通过端口 0 和 1 访问的 256 位 FMA 单元可以组合成一个 512 位单元,用于所有 FP 数学、整数乘法和可能的其他一些东西。一些 SKX Xeon 在端口上有第二个 512 位 FMA 单元5,有些没有。)

仅使用 AVX1/AVX2 后的 max-turbo (包括在像 Haswell 这样的早期 CPU 上):如果有一段时间没有使用执行单元的上半部分(有时允许更高的 Turbo 时钟速度),则机会性地关闭它们取决于最近是否使用过 YMM 指令,而不是取决于是否使用了 YMM 指令上半部分脏不脏。所以AFAIK,vzeroupper在使用 AVX1/AVX2 后,对于 CPU 的最大 turbo 较低的 256 位 CPU,这并不能帮助 CPU 更快地降低时钟速度。
这与 Intel 的 Skylake-AVX512 (SKX/Skylake-SP) 不同,其中 AVX512 有点“固定”。
VZEROUPPER可能会使上下文切换稍微便宜一些
因为 CPU 仍然知道 ymm-upper 状态是干净的还是脏的。
如果干净,我想 xsaveopt xsavec可以更紧凑地写出 FPU 状态,根本不存储全零的上半部分(只需设置一点表示它们是干净的)。通知in the state-transition diagram for SSE/AVX那个xsave/xrstor是图片的一部分。
一个额外的 vzeroupper如果您的代码在此之后很长一段时间内不会使用任何 256b 指令,那么仅值得考虑这一点,因为理想情况下,在下一次使用 256 位向量之前,您不会有任何上下文切换/CPU 迁移。
这可能不适用于 AVX512 CPU:vzeroupper/ vzeroall 不要碰ZMM16..31,只碰ZMM0..15。所以在 vzeroall 之后你仍然可以有很多脏状态.

(理论上可行):脏的上半部分可能占用了物理寄存器 (尽管 IDK 有任何证据表明这在任何真正的 CPU 上都是正确的)。如果是这样,它将限制 CPU 查找指令级并行性的乱序窗口大小。 (ROB 大小是另一个主要限制因素, but PRF size can be the bottleneck 。)
在 Zen2 之前的 AMD CPU 上可能是这样,其中 256b ops 被分成两个 128b ops。 YMM 寄存器在内部作为两个 128 位寄存器处理,例如vmovaps ymm0, ymm1以零延迟重命名低 128,但上半部分需要 uop。 (见 Agner Fog's microarch pdf)。不知道是否vzeroupper不过,实际上可以放弃对上半部分的重命名。 AMD Zen 上的归零习惯用法(与 SnB 系列不同)仍然需要后端 uop 来写入寄存器值,即使是 128b 低半部分;只有 mov-elimination 避免了后端 uop。因此,可能没有可以重命名上限的物理零寄存器。
ROB大小/PRF大小的实验blog post不过,显示 FP 物理寄存器文件条目在 Sandybridge 中是 256 位的。 vzeroupper不应使用 AVX/AVX2 在主流 Intel CPU 上释放更多寄存器。 Haswell 风格的转换惩罚足够慢,以至于它可能会耗尽 ROB 以保存或恢复鞋面到未重命名的单独存储,而不是使用有值(value)的 PRF 条目。
Silvermont 不支持 AVX。它使用 a separate retirement register file对于架构状态,因此乱序 PRF 仅保存推测执行结果。因此,即使它确实支持具有 128 位一半的 AVX,具有脏上半部分的陈旧 YMM 寄存器可能不会占用重命名寄存器文件中的额外空间。
KNL (Knight's Landing/Xeon Phi) 是专门为运行 AVX512 而设计的,所以大概它的 FP 寄存器文件有 512 位条目。它基于 Silvermont,但核心的 SIMD 部分是不同的(例如,它可以重新排序 FP/vector 指令,而 Silvermont 只能推测性地执行它们,但不能在 FP/vector 管道内重新排序,根据 Agner Fog 的说法)。尽管如此,KNL 也可能使用单独的引退寄存器文件,因此即使它能够拆分 512 位条目以存储两个 256 位向量,脏的 ZMM 上限也不会消耗额外的空间。这不太可能,因为 KNL 上仅 AVX1/AVX2 的更大乱序窗口不值得花费晶体管。
vzeroupper在 KNL 上比主流 Intel CPU 慢得多(64 位模式下每 36 个周期一个) ,所以你可能不想使用,特别是仅仅为了微小的上下文切换优势。

在 Skylake-AVX512 上,证据支持向量物理寄存器文件为 512 位宽的结论。
某些 future 的 CPU 可能会将物理寄存器文件中的条目配对以存储宽向量,即使它们通常不会像 AMD 对 256 位向量那样解码以分离 uops。
@Mysticial reports具有 YMM 与 ZMM 的长 FP 依赖链的代码意外减速,但在其他方面相同的代码,但后来的实验不同意 SKX 在高 256 位脏时为 ZMM 寄存器使用 2x 256 位寄存器文件条目的结论。

关于performance - 如果您的程序+库不包含 SSE 指令,使用 VZEROUPPER 有用吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49019614/

相关文章:

javascript - Google Maps Javascript API 移动性能问题

mysql - 用于日志分析的数据库类型?

c++ - 使用 thunk 从 cdecl 转到 thiscall (Linux x86)

c - 程序集如何访问/存储堆栈上的变量

assembly - JS和JL x86指令的区别

ruby-on-rails - rails 测试和开发数据库处于不同的模式中

c# - ReactiveUI - 查看定位器性能

assembly - 如何检查有符号整数是 neg 还是 pos?

.net - 在 x64 和 x86 上运行您的 .net 程序是否需要考虑任何因素?

c - 关于地址值和指针增量的问题?