当前位置: 首页 > 知识库问答 >
问题:

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

楚俊迈
2023-03-14

众所周知,混合使用凸编码指令和非凸指令会带来损失,程序员必须意识到这一点。

有一些这样的问题和答案。解决方案取决于您的编程方式(通常您应该在转换后使用zeroupper。但我的问题是关于EVEX编码方案的。只要没有_mm512_zeroupper()这样的内在函数,在一起使用VEX编码和EVEX编码指令时似乎没有任何损失。然而EVEX是4字节,VEX是3字节,向量长度分别是512位和256位。

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

共有1个答案

阴禄
2023-03-14

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

所有VEX和EVEX编码指令都被定义为目标向量寄存器的高字节为零,超出CPU支持的最大向量宽度。这使它们适用于任何未来的更宽向量,而不需要像vzeroupper这样丑陋的东西。

(不过,也有一个相关的放缓:请参阅@BeeOnRope关于在SKX上写入完整512位寄存器的评论,如果您显式地写入ZMM寄存器(而不是通过相应的YMM或XMM寄存器的隐式零扩展),则该寄存器在SKX上具有永久性影响。它使每个较窄的向量指令的行为就像是一个512位指令的Turbo频率限制。

没有错误的依赖关系或额外的时钟周期,只是每个时钟周期不像完整的turbo那样短。端口1没有关闭:我们仍然有3个时钟。

这是一个“全局”内核范围的状态:一个被污染的zmm0..15寄存器将损害整个内核,只有vzeroupper/all将恢复更高的turbo。(但据报道,写信给zmm16.31并不成问题)。简单地用正常的零扩展XMM-YMM-VEX或EVEX指令写入受影响的ZMM寄存器的下半部分不会使您脱离“模式”/状态。即使是像VEXvpxor或EVEXvpxord这样的调零习惯用法,被污染的寄存器也没有帮助<事实上,vpxord zmm0、zmm0、zmm0可能会导致问题,这对于归零习惯用法来说是奇怪的。

用户Mysticial和BeeOnRope进行的两个不同实验(见注释)表明,SKX的物理寄存器文件有512位条目;一个依赖于矢量重频大小来查找ILP的微基准发现“SIMD推测重频大小约为150到158”,对于256位或512位矢量也是如此。(根据英特尔发布的Skylake客户端信息和实验,我们知道这对于256位PRF大小来说是正确的。)因此,我们可以排除一种模式,即存储架构ZMM寄存器需要2个PRF条目和两倍的读/写端口。

我目前对一种解释的猜测是,可能有一个upper256 PRF在物理上比主向量PRF离调度器更远,或者只是在主向量PRF中共享相同索引的额外宽度。如果真是这样的话,当upper256 PRF通电时,光传播延迟的速度可能会限制max turbo。这个硬件设计假设不能用软件测试,但它只与摆脱坏状态的vzeroupper兼容(如果我是对的,让PRF的upper256部分断电,因为一条指令让我们知道它未使用)。不过,我不知道为什么zmm16.31对这件事无关紧要。

CPU确实会跟踪任何上256部分是否为非零,因此xsaveopt可以尽可能使用更紧凑的块。在中断处理程序中可以与内核的xsaveopt/恢复进行交互,但我主要提到这一点只是CPU跟踪它的另一个原因。

请注意,这个ZMM脏上层问题不是由于混合了VEX和EVEX。如果您对所有128位和256位指令使用EVEX编码,您会遇到同样的问题。问题是在第一代AVX512 CPU上将512位与更窄的向量混合,其中512位有点牵强,并且它们更适合更短的向量。(端口1关闭,端口5 FMA延迟更高)。

我想知道这是故意的,还是设计缺陷。

VEX保存代码大小而不是EVEX。有时在元素宽度之间解压或转换时,您最终可能会得到更窄的向量。

(即使考虑到上述512位与较短向量混合的问题,128/256位指令也不会比其512位等效指令差。它们在不应该的情况下保持最大turbo减小,但仅此而已。)

VEX编码的vpxor xmm0、xmm0、xmm0已经是将ZMM寄存器归零的最有效方法,与vpxord zmm0、zmm0、zmm0相比节省了2个字节,并且运行速度至少同样快。MSVC已经做了一段时间了,在我报告错过的优化后,clang 6.0(trunk)也做了。(gcc与锁紧螺栓的叮当声。

即使不考虑代码大小,在未来将512b指令拆分为两个256b操作的CPU上也可能更快。(参见Agner Fog关于vxorps在AMD捷豹/推土机/Zen上的归零速度是否比ymm更快?)。

类似地,作为第一步,水平求和应该缩小到256b,然后是128b,因此它们可以使用更短的凸指令,而128b指令在某些CPU上是更少的UOP。此外,在车道内洗牌通常比车道交叉更快。

另请参阅Agner Fog 2008年在英特尔论坛上的帖子,以及其他线程在AVX设计首次发布时对其进行的评论。他正确地指出,如果Intel在最初设计SSE时计划扩展到更宽的向量,并提供了一种不考虑宽度的保存/恢复完整向量的方法,那么这不会是一个问题。

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

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

调用约定只允许函数阻塞向量正则表达式的上层通道(就像当前的调用约定一样)。

问题是内核中的非AVX感知代码对上层通道的异步冲击。操作系统已经需要AVX感知来保存/恢复完整的向量状态,如果操作系统没有在promise支持这种支持的MSR中设置一点,AVX指令就会出错。所以你需要一个AVX感知内核来使用AVX,那么问题是什么?

问题基本上是传统的纯二进制Windows设备驱动程序,它们使用传统的SSE指令“手动”保存/还原一些XMM寄存器。如果这样做会隐式归零,这将破坏用户空间的AVX状态。

英特尔没有让AVX在使用此类驱动程序的Windows系统上不安全地启用,而是设计了AVX,使遗留的SSE版本保持了上层通道不变。让非AVX感知的SSE代码高效运行需要某种惩罚。

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

Linux内核代码必须围绕代码向量regs调用kernel\u fpu\u begin,这会触发必须了解AVX或AVX512的常规保存/还原代码。因此,任何使用AVX支持构建的内核都将在每个想要使用SSE或AVX的驱动程序/模块(例如RAID5/RAID6)中支持它,即使是一个不支持AVX的纯二进制内核模块(假设它编写正确,而不是保存/恢复一对xmm或ymm REG本身)。

Windows有一种类似的、经得起未来考验的保存/还原机制,即KeSaveExtendedProcessorState,它允许您在内核代码中使用SSE/AVX代码(但不包括中断处理程序)。IDK为什么司机不总是使用它;也许它很慢,或者一开始不存在。如果它的可用时间足够长,那么这纯粹是二进制驱动程序编写者/发行者的错,而不是微软自己的错。

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

 类似资料:
  • 问题内容: 我正在尝试使用Xuggler(我 相信 它是在幕后使用的)执行以下操作: 接受原始的MPJPEG视频比特流(来自小型TTL串行相机)并将其编码/转码为h.264;和 接受原始音频bitream(来自麦克风)并将其编码为AAC;然后 将两个(音频和视频)位流一起混合到MPEG-TS容器中 我已经看过/阅读了他们的一些出色的教程,到目前为止,这是我所拥有的: 首先,我想我已经很近了,但是还

  • 问题内容: 我对如何混合剃须刀和js感到很困惑。这是我坚持的当前功能: 如果我可以用声明c#代码,而其他所有内容都是JS代码,这就是我的追求: 实现此目的的最佳方法是什么? 问题答案: 用途:

  • 本文向大家介绍批处理与python代码混合编程的方法,包括了批处理与python代码混合编程的方法的使用技巧和注意事项,需要的朋友参考一下 批处理可以很方便地和其它各种语言混合编程,除了好玩,还有相当的实用价值,比如windows版的ruby gem包管理器就是运用了批处理和ruby的混合编写,bathome出品的命令工具包管理器bcn 使用了bat+jscript的混编实现的。 cn-dos和b

  • 问题内容: 当我使用MSTest Framework并复制Selenium IDE为我生成的代码时,MSTest无法识别和。有什么替代方法? 问题答案: 您将分别使用和。

  • 问题内容: 我们有一个类,其中包含应用程序的配置信息。它曾经是一个单身人士。经过一些体系结构审查后,我们被告知删除单例。我们确实看到了在单元测试中不使用单例的一些好处,因为我们可以一次测试所有不同的配置。 没有单例,我们必须在代码中的所有地方传递实例。变得太乱了,所以我们写了一个单例包装器。现在我们将相同的代码移植到PHP和.NET,我想知道是否有更好的模式可用于配置对象。 问题答案: 在谷歌测试

  • 我正在努力翻阅这本生锈的书。在“字符串”一章中,许多示例使用了_string(),我的编译器版本(rustc 1.0.0-dev)给出了以下警告 代码: 我从这个问题中了解到,这是因为API可能会更改,但我想知道如果我想转换字符串片段(str),我应该使用什么来代替它