混合VEX编码指令和非VEX指令是一个known issue,并且程序员必须知道它。
有一些问题和答案,如this。解决方案取决于你编程的方式(通常你应该在转换后使用zeroupper
。但我的问题是关于EVEX编码的方案。只要没有内在函数,如_mm512_zeroupper()
似乎有使用VEX编码和EVEX编码指令时不会受到惩罚。但EVEX为4字节,VEX为3字节,矢量长度分别为512位和256位。
因为AVX-512不可用(至少对我而言)。我想问一下,当我们想要混合它们时,有什么需要注意的。
答案 0 :(得分:7)
在任何当前的CPU上混合任何VEX 128/256或EVEX 128/256/512都不会受到任何惩罚,并且没有理由对未来的CPU造成任何惩罚。
所有VEX和EVEX编码指令都被定义为将目标向量寄存器的高字节归零,直到CPU支持的最大向量宽度。这使得它们可以适应任何未来更广泛的向量,而不需要像vzeroupper
这样的丑陋的东西。
(但是有一个相关的减速:请参阅@BeeOnRope's comments关于写一个完整的512位寄存器,直到SKX上的vzeroupper
,,如果你写了一个ZMM明确地注册(不通过相应的YMM或XMM寄存器的隐式零扩展)。它使每个较窄的向量指令就像是一个用于Turbo频率限制的512位指令一样。
没有错误的依赖性或额外的时钟周期,只是每个时钟周期都不像完全turbo一样短。端口1 不关闭:我们仍然有3个时钟vpaddd xmm/ymm
。
这是一个全球性的"核心范围的状态:一个被污染的zmm0..15寄存器会损害整个核心,只有vzeroupper/all
才会恢复更高的turbo。 (但据报道zmm16..31写的不是问题)。只需用正常的零扩展XMM YMM VEX或EVEX指令写下受影响的ZMM寄存器的低半部分就不会让你走出那种"模式" /州。即使像VEX vpxor
或EVEX vpxord
这样的归零成语,污染的寄存器也无济于事。 vpxord zmm0,zmm0,zmm0
实际上可以导致问题,这对于归零习惯来说很奇怪。
用户Mysticial和BeeOnRope执行的两个不同的实验(参见注释)表明SKX的物理寄存器文件具有512位条目;取决于矢量PRF大小的微基准标记,以找到发现的ILP" SIMD推测PRF大小约为150到158"对于256位或512位矢量是相同的。 (根据英特尔发布的Skylake客户端信息和实验,我们知道这是关于256位PRF大小的权利。)因此,我们可以排除存储架构ZMM寄存器的模式需要2个PRF条目和两倍的读/写端口。
我目前对一个解释的猜测是,可能存在比主矢量PRF更远离调度程序的上部256 PRF,或者只是在主矢量PRF中共享相同索引的额外宽度。当上部256 PRF上电时,光速传播延迟可能会限制最大涡轮增压,如果这是一个问题。这个硬件设计假设不能通过软件测试,但它只与vzeroupper
/ vzeroall
处于不良状态时兼容(如果我正确的话,让PRF的上部256部分出现断电是因为一条指令让我们知道它未被使用过。我不确定为什么zmm16..31不会对此产生影响。
CPU会跟踪任何高256位是否为零,因此xsaveopt
可以使用更紧凑的块。在中断处理程序中可以与内核的xsaveopt / restore进行交互,但大多数情况下我提到这只是CPU跟踪它的另一个原因。
请注意,由于混合VEX和EVEX ,此ZMM脏上层问题不。如果对所有128位和256位指令使用EVEX编码,则会遇到同样的问题。问题在于将512位与较窄的矢量混合在第一代AVX512 CPU上,其中512位有点拉伸,并且它们针对较短的矢量进行了更优化。 (端口1关闭,port5 FMA的延迟更高)。
我想知道这是故意还是设计错误。
VEX保存代码大小与EVEX。有时在元素宽度之间进行解包或转换时,最终可能会出现较窄的向量。
(即使考虑到将512位与较短向量混合的上述问题,128/256位指令并不比它们的512位等效指令更差。当它们不应该时,它们会保持最大的turbo降低,但是&#t; #39; s all 。)
VEX编码vpxor xmm0,xmm0,xmm0
已经是将ZMM寄存器归零的最有效方法,相对于vpxord zmm0,zmm0,zmm0
保存2个字节并且运行速度至少一样快。 MSVC已经做了一段时间了,而clang 6.0(trunk)也是在我reported the missed optimization之后做的。 (gcc vs. clang on godbolt
即使除了代码大小之外,未来的CPU可能会将512b指令拆分为两个256b操作。 (参见Agner Fog对Is vxorps-zeroing on AMD Jaguar/Bulldozer/Zen faster with xmm registers than ymm?的回答)。
类似地,水平和应该缩小到256b,然后是128b作为第一步,因此它们可以使用更短的VEX指令,128b指令在某些CPU上的uop更少。通道内洗牌通常比车道穿越更快。
另见Agner Fog's 2008 post on the Intel forums以及AVX设计首次发布时评论的其余主题。他正确地指出,如果英特尔在设计SSE时计划扩展到更宽的向量,并提供了一种无论宽度如何保存/恢复完整向量的方法,这都不是问题。
同样有趣的是,Agner 2013年对AVX512的评论,以及英特尔论坛上的讨论:AVX-512 is a big step forward - but repeating past mistakes!
当AVX首次引入时,他们可能已经定义了传统SSE指令的行为以使上部通道归零,这将避免需要vzeroupper
并具有保存的上部状态(或错误的依赖性)。
调用约定只会让函数破坏向量regs的上层通道(就像当前的调用约定那样)。
问题是内核中非AVX感知代码对上层通道的异步破坏。操作系统已经需要AVX感知才能保存/恢复完整的矢量状态,而AVX指令错误if the OS hasn't set a bit in an MSR that promises this support。所以你需要一个支持AVX的内核来使用AVX,那么问题是什么呢?
问题基本上是传统的二进制Windows设备驱动程序,它们手动保存/恢复一些XMM寄存器"手动"使用传统的SSE指令。如果这是隐式归零,这将破坏用户空间的AVX状态。
英特尔设计AVX,而不是在使用此类驱动程序的Windows系统上启用AVX不安全,因此旧版SSE版本未经修改就离开了上层车道。让非AVX感知的SSE代码有效运行需要一些惩罚。
我们为Microsoft Windows提供仅限二进制的软件分发,以感谢英特尔决定造成SSE / AVX过渡处罚的痛苦。
Linux内核代码必须在代码向量regs周围调用kernel_fpu_begin
/ kernel_fpu_end
,这会触发必须了解AVX或AVX512的常规保存/恢复代码。因此,任何使用AVX支持构建的内核都会在每个想要使用SSE或AVX的驱动程序/模块(例如RAID5 / RAID6)中支持它,即使是非AVX感知的仅二进制内核模块(假设它是正确编写的,而不是保存/恢复一对xmm或ymm注册自己。)
Windows has a similar future-proof save/restore mechanism,KeSaveExtendedProcessorState
,允许您在内核代码中使用SSE / AVX代码(但不能使用中断处理程序)。 IDK为什么司机并不总是使用它;也许它起来很慢或者一开始并不存在。如果它可以使用的时间足够长,那么它纯粹是二进制驱动程序编写者/分发者的错,而不是微软自己。
(关于OS X的IDK也是。如果二进制驱动程序保存/恢复xmm regs"手动"而不是告诉操作系统下一个上下文切换需要恢复FP状态以及整数,那么他们' ;也是问题的一部分。)