AVX-512掩码寄存器,再一次

2020-05-26 10:05:10

几个帖子之前,我们查看了AVX-512面具注册器。具体地说,八个建筑一下的物理语域的数量,以及其他一些行为,如归零习语。最近,一张SKX的高分辨率冲模出现了,我想通过肉眼检查来核实我们的登记数量会很酷。当我试图做到这一点时,我发现了另一件有趣的事情,而不是…。

我们对这张Fritzchens Fritz最近在Flickr上发布的死亡照片很感兴趣。我们将重点关注突出显示的区域,该区域似乎包含芯片上的所有寄存器堆。如果你想要了解内核的完整细节,你可以在Twitter、RWT和英特尔的论坛(线程)上查看这里的猜测。

下面是该部分的特写,各种寄存器文件的假定用途标记为1。

通用寄存器堆具有正确的相对宽度(64位),并且位于整数执行单元下方的正确位置。

通过比较SKL 2(无AVX-512)和SKX(具有AVX-512)管芯,并注意SKL中不存在底部文件(SKL中的该点存在较大的空白区域),可以确定512位ZMM寄存器(特写中标记为ZMM)的上部256位。

这会将未知块保留为红色。该块位于矢量执行单元下方的主要位置。会不会是掩码寄存器(Kregs)?在第一篇文章中,我们发现掩码既不与标量寄存器共享,也不与SIMD寄存器共享,因此我们希望它们有自己的物理寄存器堆。也许就是这个了?

让我们将神秘寄存器堆与整数寄存器堆进行比较,因为它们在大小上应该是相似的,并且看起来实现方式也是相似的:

查看左侧的通用寄存器文件,每个块(其中6个在通用文件上编号)似乎实现了16位,就好像放大您会看到一个由16个元素组成的重复结构,而4个块总共为64位,这是文件的预期宽度。从公布的数字中我们知道整数寄存器堆具有180个条目,并且由于有4个块的6行,我们期望每行实现180/6=30个寄存器。

现在我们把注意力转向这个神秘的文件,我们认为它可能是Kreg文件。一共有30个街区。查看通用寄存器,我们确定每个块可以保存来自30个寄存器的16位。因此,30个块就是:30个块*30个寄存器/块*16位/64位=225个寄存器。嗯,上次我们计算了一下,大约有142个物理掩码寄存器,所以这个数字似乎太高了。

还有另一个问题:我们只有三列16位块,总共48位,水平方向。但是,我们知道掩码寄存器必须最多64位(当对完整的512位矢量寄存器使用逐字节时)。此外,虽然我们上面的计算结果是一个整数,但是块的数量(30)不能被4整除,所以即使您假设块的排列无关紧要,也不可能从每个寄存器映射到4个不同的块。相反,我们需要一些奇怪的东西,比如2个块提供15个寄存器(而不是30个),但是64位宽(而不是32个)。这似乎不太可能。

现在让我们先看一下左边的两个成对的列。如果我们以SIMD寄存器为例,则寄存器的全宽度不必水平地出现在单行中:SIMD寄存器在一行中具有256位(分成两条128行),但是512位ZMM寄存器中的其它256位垂直地出现在图中由ZMM构成的寄存器堆中。

由于掩码寄存器与ZMM寄存器的元素相关联,也许它们是以AME的方式分开的?也就是说,64位掩码寄存器使用来自上半部分的一个2x16(32位)块和来自下半部分的一个块来组成64位?这是总共20个块,按照上面的相同计算给出了150个寄存器。这与我们从实验中发现的142个更接近。

静止…。那种烦人的感觉。142不等于150,那么第三列块呢?我开始怀疑这到底是不是掩码寄存器堆。那会是什么呢?我意识到有一个寄存器文件没有说明:用于传统x87浮点和MMX的文件。x87浮点和MMX是同一个文件,因为MMX寄存器在架构上别名为x87寄存器3。那么这个文件在哪里呢?我看了看周围的死胡同。没有好的候选人。

那么也许我们一直在看的东西实际上是X87/MMX寄存器堆?在某种程度上,它更适合:x87FP寄存器堆需要大约80位宽,因此可以解释额外的列:如果我们像以前一样假设每行都是寄存器的一半,那么就是96位。这足以保存80位扩展精度值,剩下的16位可能用于存储ftstw和相关指令访问的FPU状态字。此状态字在每次操作后更新,因此也必须重命名以获得合理的性能5。

这可能是X87/MMX寄存器堆的其他证据也来自FRITZ:

请注意,虽然寄存器堆的高256位被屏蔽(此芯片仅支持AVX2,不支持AVX-512,因此没有ZMM寄存器),但我们考虑的寄存器堆是整体存在的。

理论上很酷,兄弟,但我们不是又回到了原点吗?如果这是用于x87/MMX寄存器的文件,则屏蔽寄存器位于何处?

有一种可能性我们还没有讨论,尽管你们中的一些人现在可能已经在显示器上讨论过了:也许X87/MMX和KREG寄存器文件是共享的。即,将物理别名6物理地混叠到竞争共享的相同寄存器堆。

我们将使用亨利·黄(Henry Wong)最初描述的测试方法,我们在上一篇关于这个主题的帖子中也使用了这种方法。以下是该技术的快速描述,直接从该帖子复制和粘贴。

我们通过可变数量的填充指令来分隔两个高速缓存未命中加载指令,填充指令数量根据我们探测的CPU资源而有所不同。当填充指令的数量足够少时,两个高速缓存未命中并行执行,并且它们的等待时间重叠,因此>;总执行时间大致与单个未命中一样长。

然而,一旦填充指令的数量达到临界阈值,所有目标资源都会被消耗,并且指令分配会在发出第二次未命中之前停止,因此高速缓存未命中不能再并行运行。这会导致运行时达到约为>;基线缓存未命中延迟的两倍。

最后,我们确保每个填充指令恰好消耗一个我们感兴趣的资源,以便峰值的位置指示底层资源的大小。例如,常规GP指令通常消耗来自GP PRF的一个物理寄存器,因此是测量该资源大小的好选择。

我们用来查看两个寄存器堆是否共享的诀窍是,首先单独使用每个寄存器堆大小的测试,使用仅针对该寄存器堆的填充符的测试,然后运行第三个测试,该测试的填充符在使用每个寄存器堆的指令之间交替。如果寄存器堆是共享的,我们预计所有测试都会产生相同的结果,因为它们都是从同一池中提取的。如果寄存器堆未共享,则第三次(交替)测试应导致更高的表观资源限制,因为从两个不同的池中提取,因此将需要两倍的填充指令才能达到RF限制。

说够了,我们开始吧。首先,我们看测试38,它使用MMX指令7来确定x87/MMX寄存器堆的大小:

我们在128条指令上看到一个明显的峰值,因此推测的8X87/MMX寄存器堆的大小似乎是128个条目。

接下来,我们有测试43,它遵循与测试38相同的模式,但使用kaddd作为填充指令,因此以掩码(Kreg)寄存器堆为目标:

这与前面的图表几乎没有区别,我们得出结论,推测掩码寄存器文件的大小也是128。

让我们看看当替换MMX和另一种指令类型时会发生什么。测试39用整数SIMD指令替换MMX,测试40替换用通用标量指令:

这两者都显示出相同的效果:有效的资源限制要高得多:大约210条填充指令。这强烈表明x87/MMX寄存器不与SIMD或标量寄存器堆共享。

最后,我们来到这个故事的结尾,测试41。该测试混合了MMX和掩码寄存器指令9:

这一次绝对不同于其他几次。我们看到现在的资源限制是128,与单一类型的测试相同。我们可以立即得出结论,这意味着掩码寄存器和MMX寄存器是从相同的资源池中分配的:它们使用相同的物理寄存器文件。

这解开了丢失寄存器堆的谜团:没有丢失任何东西,而这一个寄存器堆只是起到双重作用。

通常情况下,共享寄存器堆在性能方面可能需要注意,但很难想象这会对任何非人工示例产生影响。谁将大量使用X87或MMX(两者都已过时)以及AVX-512掩码寄存器(与“过时”截然相反)?这似乎极不可能。在任何情况下,寄存器堆仍然相当大,因此无论如何都不太可能达到限制。

因此,共享这些文件是降低功耗和面积的一个很酷的技巧:寄存器文件并不都那么大,但它们位于靠近执行单元的黄金地段。

然而,这一次最酷的是,这是我第一次研究芯片(这对我来说甚至是可能的,这对我来说是非同寻常的),并提出了关于硬件的理论,我们可以用目标微基准来测试和确认。在这里,事情实际上是这样发生的。我已经意识到寄存器堆共享的可能性(Henry从一开始就在rosize中对这一权利进行了测试)-但是,尽管我考虑了其他共享方案,但我从未考虑过在x87/mmx和掩码寄存器之间共享,直到我尝试识别Franz的骰子上的寄存器堆。

感谢弗里茨·弗里茨(Fritzchens Fritz),他创造了这里分析的骰子镜头,并慷慨地将它们放入了公共领域。

Henry Wong撰写了向我介绍这项技术的原始文章,随后与我分享了他的工具的代码,该工具现在托管在GitHub上。

Nemez做了芯片击穿的细目,指出有问题的寄存器堆是某种类型的整数寄存器堆。

感谢Daniel Lemire,他提供了访问本文中使用的SKX硬件的权限。

ZMM、YMM和XMM寄存器在体系结构上全部重叠。也就是说,xmm0只是ymm0的底部128位,对于ymm0和zmm0也是如此。从物理上讲,实际上只有ZMM寄存器,其他两个寄存器只是那些较大寄存器的特定位范围。因此,芯片上标记为YMM的区域实际上意味着:不属于相应的XMM寄存器的YMM寄存器的上部。。↩

事实上,凯比湖,因为我们最好的死亡射击来自那个芯片,但这是一回事。↩。

我想,这是一个技巧,允许操作系统和其他不知道MMX寄存器存在的代码保存和恢复MMX寄存器。在从SSE到AVX的转换过程中也出现了类似的混乱,不知道AVX的代码可能会意外地使用SSE指令损坏AVX寄存器的上部(如果SSE将高位清零),因此我们反而会得到传统SSE和肮脏上游的持续问题。-↩。

这是一个谎言:我并没有真的环顾芯片周围:我在执行单元附近寻找,因为寄存器文件的可能性很高。-↩。

整数标志(所谓的EFLAGS寄存器)也需要重命名,我相信它们使用了类似的技巧:将它们的结果写入为结果分配的相同物理寄存器:我已经在缩放视图上标记了我认为包含所谓SPAZO组的文件,而C标志可以存储在相同的位置,也可以存储在较薄的位置(单比特?)。位于GP文件右侧的文件。↩。

我在这里谈论物理别名,是为了将其与逻辑/体系结构别名区分开来。逻辑混叠是软件可见的:YMM和XMM寄存器在逻辑上混叠,因为对xmm0的写入显示在ymm0的低位。类似地,MMX和X87寄存器堆具有别名,因为写入MMX寄存器修改FP寄存器堆栈中的值,尽管规则更为复杂。逻辑混叠通常意味着物理混叠,但不是反过来。因此,物理混叠意味着将两个寄存器集重命名到同一物理寄存器池中,但这通常对软件是不可见的(除非像我们在这里所做的那样进行仔细的性能测试)。↩。

我使用的是mmx而不是x87,所以我不必处理x87FP堆栈抽象,也不必理解它如何映射到重命名。-↩。

推测性寄存器堆,因为我们期望一些条目也用于保存架构寄存器的非推测值。稍后我们将回到这一点。--↩。

具体地说,它混合了我们在单类型测试Test38和Test43中使用的相同的kaddd和por指令。-↩