混合EVEX和VEX编码方案的代价是什么?

时间:2017-09-06 16:35:32

标签: assembly x86 simd avx512

混合VEX编码指令和非VEX指令是一个known issue,并且程序员必须知道它。

有一些问题和答案,如this。解决方案取决于你编程的方式(通常你应该在转换后使用zeroupper。但我的问题是关于EVEX编码的方案。只要没有内在函数,如_mm512_zeroupper()似乎有使用VEX编码和EVEX编码指令时不会受到惩罚。但EVEX为4字节,VEX为3字节,矢量长度分别为512位和256位。

因为AVX-512不可用(至少对我而言)。我想问一下,当我们想要混合它们时,有什么需要注意的。

1 个答案:

答案 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的延迟更高)。

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

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

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更少。通道内洗牌通常比车道穿越更快。

SSE / AVX出现问题的背景

另见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 mechanismKeSaveExtendedProcessorState,允许您在内核代码中使用SSE / AVX代码(但不能使用中断处理程序)。 IDK为什么司机并不总是使用它;也许它起来很慢或者一开始并不存在。如果它可以使用的时间足够长,那么它纯粹是二进制驱动程序编写者/分发者的错,而不是微软自己。

(关于OS X的IDK也是。如果二进制驱动程序保存/恢复xmm regs"手动"而不是告诉操作系统下一个上下文切换需要恢复FP状态以及整数,那么他们' ;也是问题的一部分。)