混合 EVEX 和 VEX 编码方案的惩罚是什么?

What is the penalty of mixing EVEX and VEX encoded scheme?

混合使用 VEX 编码指令和非 VEX 指令会受到惩罚,程序员必须意识到这一点。

有一些问题和答案如this。解决方案取决于您的编程方式(通常您应该在转换后使用 zeroupper。但我的问题是关于 EVEX 编码方案。就没有诸如 _mm512_zeroupper() 之类的内在函数而言,似乎有同时使用 VEX 编码和 EVEX 编码指令时没有惩罚。但是 EVEX 是 4 字节,VEX 是 3 字节,向量长度分别是 512 位和 256 位。

因为 AVX-512 不可用(至少对我而言)。想问下混用的时候有什么需要注意的吗

在任何当前 CPU 上混合 VEX 128 / 256 或 EVEX 128 / 256 / 512 中的任何一个都不会受到惩罚,并且没有理由期望未来 CPU 会受到任何惩罚。

所有 VEX 和 EVEX 编码指令都定义为将目标向量寄存器的高字节置零,直到 CPU 支持的最大向量宽度。这使得它们 future-proof 用于任何未来更广泛的向量,而不需要像 vzeroupper.

这样丑陋的东西

(虽然有一个相关的减速:参见 @BeeOnRope's comments 关于编写一个完整的 512 位寄存器具有永久影响,直到 vzeroupper 在 SKX, 如果你显式地写一个 ZMM 寄存器(而不是通过相应的 YMM 或 XMM 寄存器的隐式 zero-extension)。它使每个更窄的向量指令都像一个 512 位指令一样用于 Turbo 频率限制。

没有错误的依赖关系或额外的时钟周期,只是每个时钟周期不像全 turbo 那样短。端口 1 关闭:我们还有 3-per-clock vpaddd xmm/ymm.

这是一个"global" core-wide状态:一个被污染的zmm0..15寄存器会伤害整个内核,只有vzeroupper/all会恢复更高的睿频。 (但据报道写入 zmm16..31 不是问题)。简单地用正常的 zero-extending XMM YMM VEX 或 EVEX 指令写入受影响的 ZMM 寄存器的低半部分不会使您脱离 "mode" / 状态。即使像 VEX vpxor 或 EVEX vpxord 污染的寄存器这样的归零习惯也无济于事。 vpxord zmm0,zmm0,zmm0 实际上可以 导致 这个问题,这对于归零习语来说很奇怪。

用户 Mysticial 和 BeeOnRope(见评论)进行的两个不同实验表明 SKX 的物理寄存器文件有 512 位条目;一个微基准,它依赖于向量 PRF 大小来找到 ILP "a SIMD speculative PRF size of about 150 to 158",对于 256 位或 512 位向量也是如此。 (我们知道这对于 256 位 PRF 大小来说是正确的,基于英特尔发布的 Skylake-client 信息和那里的实验。)所以我们可以排除存储架构 ZMM 寄存器需要 2 个 PRF 条目和两次的模式read/write 个端口。

我目前的猜测是,可能有一个 upper256 PRF 在物理上比主向量 PRF 离调度程序更远,或者只是额外的宽度在主向量 PRF 中共享相同的索引。 Speed-of-light 当 upper256 PRF 通电时,传播延迟可能会限制最大涡轮增压,如果是这样的话。这个 hardware-design 假设无法用软件测试,但它仅与 vzeroupper / vzeroall 摆脱不良状态兼容(如果我是对的,让 PRF 的 upper256 部分断电,因为一条指令让我们知道它未被使用)。不过,我不确定为什么 zmm16..31 对此无关紧要。

CPU 确实跟踪是否有任何上层 256 部分是 non-zero,因此 xsaveopt 可以尽可能使用更紧凑的块。在中断处理程序中可以与内核的 xsaveopt / restore 交互,但我主要提到这只是作为 CPUs 跟踪这个的另一个原因。

请注意,由于混合了 VEX 和 EVEX,此 ZMM dirty-upper 问题是 而不是 。如果您对所有 128 位和 256 位指令使用 EVEX 编码,就会遇到同样的问题。问题是在 first-gen AVX512 CPUs 上将 512 位与较窄的向量混合,其中 512 位有点牵强,它们针对较短的向量进行了更优化。 (端口 1 关闭,端口 5 FMA 的延迟更高)。

我想知道这是故意的,还是设计错误。



在 AVX512 代码中尽可能使用 VEX 是一件很好的事情。

VEX 比 EVEX 节省了 code-size。有时在元素宽度之间解包或转换时,最终可能会得到更窄的向量。

(即使考虑到上述将 512 位与较短向量混合使用的问题,128/256 位指令并不比它们的 512 位等效指令差。它们在不应该降低最大 turbo 时保持降低,但那是全部.)

A VEX-coded vpxor xmm0,xmm0,xmm0 已经是将 ZMM 寄存器 置零的最有效方法,与 vpxord zmm0,zmm0,zmm0 和 [ 相比节省了 2 个字节=167=]宁至少一样快。 MSVC 已经这样做了一段时间,在我 reported the missed optimization. (gcc vs. clang on godbolt.

之后,clang 6.0 (t运行k) 也这样做了

即使在 code-size 之外,它在未来的 CPU 中可能会更快,将 512b 指令拆分为两个 256b 操作。 (参见 Agner Fog 在 Is vxorps-zeroing on AMD Jaguar/Bulldozer/Zen faster with xmm registers than ymm? 上的回答)。

同样,水平和应该缩小到 256b,然后作为第一步缩小到 128b,这样他们就可以使用更短的 VEX 指令,而 128b 指令在某些 CPUs 上的微指令更少。此外 in-lane 洗牌通常比 lane-crossing.

更快

为什么 SSE/AVX 是一个问题的背景

另请参阅 Agner Fog's 2008 post on the Intel forums 以及评论首次发布时对 AVX 设计的讨论帖的其余部分。他正确地指出,如果英特尔在设计 SSE 时首先计划扩展到更广泛的向量,那么 proided 一种方法来 save/restore 一个完整的矢量,不管宽度如何,这不会是一个问题。

同样有趣的是,Agner 2013 年对 AVX512 的评论,以及由此产生的英特尔论坛上的讨论:AVX-512 is a big step forward - but repeating past mistakes!


当 AVX 首次推出时,他们可以定义遗留 SSE 指令的行为以将上层通道归零,这将避免需要 vzeroupper 并具有 saved-upper 状态(或错误的依赖关系)。

调用约定只会让函数破坏向量 reg 的上层通道(就像当前的调用约定已经做的那样)。

问题是内核中的 non-AVX-aware 代码对上层通道的异步破坏。 OSes 已经需要 AVX-aware 到 save/restore 全矢量状态,AVX 指令错误 if the OS hasn't set a bit in an MSR that promises this support。所以你需要一个 AVX-aware 内核来使用 AVX,那么问题是什么?

问题基本上是旧版 binary-only Windows 设备 driver 使用旧版 SSE 指令手动 save/restore 某些 XMM 注册 "manually" 的设备。如果这样做隐式归零,这将破坏 user-space.

的 AVX 状态

Intel 设计了 ​​AVX,而不是让 AVX 在 Windows 系统上使用此类 driver 时不安全,因此遗留的 SSE 版本不修改上层通道。让 non-AVX-aware SSE 代码 运行 有效需要某种惩罚。

我们有 binary-only Microsoft Windows 的软件分发,以感谢英特尔决定造成 SSE/AVX 过渡惩罚的痛苦。

Linux 内核代码必须围绕代码向量 regs 调用 kernel_fpu_begin / kernel_fpu_end,这会触发必须了解 AVX 或 AVX512 的常规 save/restore 代码。因此,任何使用 AVX 支持构建的内核都将在每个想要使用 SSE 或 AVX 的 driver/module(例如 RAID5/RAID6)中支持它,甚至 non-AVX-aware binary-only 内核模块(假设它写得正确,而不是 saving/restoring 几个 xmm 或 ymm regs 本身)。

Windows has a similar future-proof save/restore mechanismKeSaveExtendedProcessorState,让您可以在内核代码中使用 SSE/AVX 代码(但不是中断处理程序)。 IDK 为什么 drivers 并不总是使用它;也许它很慢或一开始不存在。如果它已经可用了足够长的时间,那么这纯粹是 binary-only driver writers/distributors 的错,而不是微软自己的错。

(关于 OS X 的 IDK。如果二进制 drivers save/restore xmm regs "manually" 而不是告诉 OS 下一个上下文切换需要恢复 FP 状态和整数,那么它们也是问题的一部分。)