没有任何惩罚可以混合使用VEX 128/256或EVEX 128/256/512在当前的CPU上,并且未来的CPU也不会有任何惩罚。
所有的VEX和EVEX编码指令都被定义为将目标向量寄存器的高字节清零,直到CPU支持的最大向量宽度。这使得它们对于任何未来更宽的向量都是具备未来性的,而不需要像vzeroupper
那样的丑陋操作。
vzeroupper
,如果您明确地写入ZMM寄存器(而不是通过相应的YMM或XMM寄存器的隐式零扩展)。这使得每个较窄的向量指令表现得像Turbo频率限制下的512位指令一样。vpaddd xmm/ymm
。我目前的解释猜测是,也许有一个距调度程序更远的upper256 PRF,或者只是额外的宽度共享主矢量PRF中的相同索引。如果存在这样的情况,光速传播延迟可能会限制最大睿频当upper256 PRF被启用时。这种硬件设计假设无法通过软件进行测试,但与仅使用vzeroupper / vzeroall退出错误状态(如果我正确,那么让PRF的upper256部分关闭,因为该指令使我们知道它未使用)兼容。我不确定zmm16..31为什么不重要。
CPU会跟踪是否有任何上256位部分为非零,因此如果可能的话,xsaveopt
可以使用更紧凑的块。在中断处理程序中与内核的xsaveopt / restore交互是可能的,但我大多数情况下提到这一点只是作为另一个原因,为什么CPU会跟踪这一点。
请注意,这个ZMM dirty-upper问题不是由于混合VEX和EVEX造成的。如果您对所有128位和256位指令都使用EVEX编码,那么您将遇到相同的问题。该问题源于在第一代AVX512 CPU上混合512位和较窄向量,其中512位有点过长,它们更适用于较短的向量。(端口1关闭以及端口5 FMA的更高延迟)。VEX相对于EVEX可以节省代码大小。有时,在解包或在元素宽度之间转换时,您可能会得到更窄的向量。
(即使考虑到将512位与较短的向量混合的上述问题,128/256位指令也不劣于它们的512位等效物。它们在不应该降低最大睿频时保持最大睿频,但这就是全部。)
使用 VEX 编码的vpxor xmm0,xmm0,xmm0
已经是将 ZMM 寄存器清零的最高效方式,相比于vpxord zmm0,zmm0,zmm0
节省了 2 个字节,并且运行速度至少与后者一样快。MSVC 已经使用这种方式一段时间了,而 clang 6.0(trunk)在我报告了优化问题之后也开始使用。 (gcc vs. clang on godbolt。
除了代码大小之外,它在将512b指令拆分为两个256b操作的未来CPU上可能更快。 (请参见Agner Fog在Is vxorps-zeroing on AMD Jaguar/Bulldozer/Zen faster with xmm registers than ymm?中的回答)。请参考Agner Fog在Intel论坛上2008年的帖子以及其余评论AVX设计的帖子。他正确地指出,如果英特尔在首次设计SSE时计划扩展到更宽的向量,并提供一种无论宽度如何都可以保存/恢复完整向量的方法,这个问题就不会存在。
另外有趣的是,Agner在2013年对AVX512的评论以及在Intel论坛上引发的讨论:AVX-512是一个重大进步,但重复了过去的错误!
vzeroupper
和保存上部状态(或虚假依赖)。Windows有类似的未来证明的保存/恢复机制,KeSaveExtendedProcessorState
,这使您可以在内核代码中使用SSE / AVX代码(但不能用于中断处理程序)。我不知道为什么驱动程序没有始终使用它;也许它很慢或者一开始不存在。如果它已经可用了足够长的时间,那么这纯粹是二进制驱动程序编写者/分发者的责任,而不是微软自己。
(对于OS X,我不确定。如果二进制驱动程序手动保存/恢复xmm寄存器而不是告诉操作系统下一个上下文切换需要还原FP状态以及整数,那么他们也是问题的一部分。)
vzeroupper
通过清除每个向量寄存器的上半部分解决了这个问题。 - fuz