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

Mar*_*tin 8 x86 assembly simd avx512

这是一个已知的问题是混合VEX编码的指令和非VEX指令有一个点球和程序员必须意识到这一点.

有一些像这样的问题和答案.解决方案取决于您编程的方式(通常您应该zeroupper在转换后使用.但我的问题是关于EVEX编码的方案.只要没有内在函数,例如_mm512_zeroupper()使用VEX编码和EVEX-时似乎没有惩罚但是,EVEX是4字节,VEX是3字节,矢量长度分别是512位和256位.

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

Pet*_*des 8

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

所有VEX和EVEX编码指令都被定义为将目标向量寄存器的高字节归零,直到CPU支持的最大向量宽度.这使得它们可以适应任何未来更广泛的载体,而不需要像丑陋的东西vzeroupper.


(但是有一个相关的减速:请参阅@ BeeOnRope关于编写一个完整的512位寄存器的注释,直到vzeroupper在SKX上,如果你明确写一个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位向量是相同的.(我们知道这是关于256位PRF大小的权利,基于英特尔发布的Skylake客户端信息和那里的实验.)因此我们可以排除存储架构ZMM寄存器需要2个PRF条目和两倍的模式读/写端口.

我目前对一个解释的猜测是,可能存在比主矢量PRF更远离调度器的上部256 PRF,或者只是在主矢量PRF中共享相同索引的额外宽度.当上部256 PRF上电时,光速传播延迟可能会限制最大涡轮增压,如果这是一个问题.这个硬件设计假设不能用软件测试,但它只与兼容vzeroupper/ vzeroall退出坏状态兼容(如果我是对的,让PRF的upper256部分断电,因为一条指令让我们知道它未被使用) .我不确定为什么zmm16..31对此无关紧要.

CPU会跟踪上256个部分是否为非零,因此xsaveopt如果可能,可以使用更紧凑的块.在中断处理程序中可以与内核的xsaveopt/restore进行交互,但大多数情况下我提到这只是CPU跟踪它的另一个原因.

请注意,此ZMM脏上层问题不是由于混合VEX和EVEX.如果对所有128位和256位指令使用EVEX编码,则会遇到同样的问题.问题在于将512位与较窄的矢量混合在第一代AVX512 CPU上,其中512位有点拉伸,并且它们针对较短的矢量进行了更优化.(端口1关闭,port5 FMA的延迟更高).

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



使用VEX时可能在AVX512代码是一个很好的事情.

VEX保存代码大小与EVEX.有时在元素宽度之间进行解包或转换时,最终可能会出现较窄的向量.

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

VEX编码vpxor xmm0,xmm0,xmm0已经是将ZMM寄存器归零的最有效方法,节省2个字节,vpxord zmm0,zmm0,zmm0并且运行至少同样快.MSVC已经做了一段时间了,而clang 6.0(trunk)在我报告错过的优化之后也做了.(关于Godbolt的gcc与clang.

即使除了代码大小之外,未来的CPU也可能将512b指令拆分为两个256b操作.(参见Agner Fog的回答是关于使用xmm寄存器比使用xmm寄存器更快地使用AMD Jaguar/Bulldozer/Zen进行vxorps调零吗?).

类似地,水平和应该缩小到256b,然后是128b作为第一步,因此它们可以使用更短的VEX指令,128b指令在一些CPU上更少的uop.通道内洗牌通常比车道穿越更快.



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

另见Agner Fog在英特尔论坛上发布的2008年帖子以及AVX设计首次发布时评论其余部分的帖子.他正确地指出,如果英特尔在设计SSE时计划扩展到更宽的向量,并提供了一种无论宽度如何保存/恢复完整向量的方法,这都不是问题.

同样有趣的是,Agner 2013年对AVX512的评论,以及英特尔论坛上的讨论:AVX-512向前迈出了一大步 - 但重复过去的错误!


当AVX首次引入时,他们可能已经定义了传统SSE指令的行为以使上部通道归零,这将避免需要vzeroupper并具有保存的上部状态(或错误的依赖性).

调用约定只会允许函数破坏向量regs的上层通道(就像当前的调用约定那样).

问题是内核中非AVX感知代码异步破坏上层通道.操作系统已经需要AVX感知才能保存/恢复完整的矢量状态,如果操作系统没有在承诺此支持的MSR中设置一个位,则 AVX指令会发生故障.所以你需要一个支持AVX的内核来使用AVX,那么问题是什么呢?

问题基本上是传统的仅二进制Windows设备驱动程序,它使用传统SSE指令"手动"手动保存/恢复某些XMM寄存器.如果这是隐式归零,这将破坏用户空间的AVX状态.

英特尔设计了AVX,而不是在使用此类驱动程序的Windows系统上启用AVX不安全,因此旧版SSE版本未经修改就离开了上层通道.让非AVX感知的SSE代码有效运行需要某种惩罚.

我们为Microsoft Windows提供了仅限二进制的软件分发,以感谢英特尔决定对SSE/AVX过渡处罚造成的痛苦.

Linux内核代码必须调用kernel_fpu_begin/ kernel_fpu_end围绕代码向量regs,它触发常规保存/恢复代码,该代码必须知道AVX或AVX512.因此,任何使用AVX支持构建的内核都会在每个想要使用SSE或AVX的驱动程序/模块(例如RAID5/RAID6)中支持它,即使是非AVX感知的仅二进制内核模块(假设它是正确编写的,而不是保存/恢复一对xmm或ymm注册自己).

Windows具有类似的面向未来的保存/恢复机制,KeSaveExtendedProcessorState允许您在内核代码中使用SSE/AVX代码(但不能使用中断处理程序).IDK为什么司机不总是使用它; 也许它很慢或者一开始就不存在.如果它已经足够长,那么它纯粹是二进制驱动程序编写者/分发者的错,而不是微软自己.

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


归档时间:

查看次数:

667 次

最近记录:

7 年,9 月 前