现代 CPU 是否需要花费大量资源来保持标志更新?

rwa*_*ace 5 performance assembly cpu-architecture micro-optimization eflags

据我了解,在现代无序 CPU 上,最昂贵的事情之一是状态,因为必须在多个版本中跟踪该状态,并在许多指令中保持最新状态等。

一些像 x86 和 ARM 的指令集大量使用了标志,当成本模型不是今天的样子时引入了标志,而标志只需要几个逻辑门。诸如每个算术指令设置标志以检测零、进位和溢出之类的事情。

这些在现代乱序实现上保持更新是否特别昂贵?例如,一条 ADD 指令更新进位标志,这必须被跟踪,因为虽然它可能永远不会被使用,但有可能其他指令可以在 N 条指令后使用它,对 N 没有固定的上限?

在没有这些标志的指令集架构(如 MIPS)上,诸如加法和减法之类的整数运算是否更便宜?

har*_*old 6

这方面的各个方面都不是很公开,因此我将尝试将明确已知的事物与合理的猜测和猜想分开。

一种方法是使用操作产生的标志扩展(物理)整数寄存器(无论它们采用物理寄存器文件的形式 [例如 P4 和 SandyBridge+] 还是 ROB 结果 [例如 P3])这也产生了相关的整数结果。这只是关于算术标志(有时是 AFLAGS,不要与 EFLAGS 混淆),但我认为“奇怪的标志”不是这个问题的重点。有趣的是,有一项专利[1]暗示不仅仅存储 6 个 AFLAGS 本身,还在其中放置了一些“组合标志”,但谁知道这是否真的完成了 - 大多数消息来源说寄存器扩展了 6 位,但 AFAIK 我们(公众)并没有真正知道。例如,本专利[2] 中描述了将整数结果和相关标志集中在一起,这主要是为了防止标志可能意外不再由任何物理寄存器支持的某种情况。除了这些怪癖之外,在正常操作期间,它具有只需要为算术运算分配 1 个寄存器的良好效果,而不是单独的 main-result 和 flags-result,因此重命名通常不会因为存在标志。此外,寄存器别名表至少需要一个插槽来跟踪哪个整数寄存器包含最新标志,或者一个单独的标志重命名状态缓冲区跟踪最新的推测标志状态([2]建议英特尔选择将它们分开,这可能会简化主要的 RAT,但他们没有详细介绍)。多个狭槽可以使用[3]有效地实现指令只更新标志的子集(的NetBurst™著名缺乏此,导致现在陈旧的建议有利于add以上inc)。类似地,非推测体系结构状态(是否会的一部分的引退寄存器文件或为分离但类似再次不明确)需要在至少一个这样的槽。

一个单独的问题是首先计算标志。[1]建议将标志生成与主 ALU 分开可简化设计。不清楚它们将分离到什么程度:无论如何,主 ALU 必须计算 Adjust 和 Sign 标志,并且有一个加法器输出一个进位并没有什么要求(比从无到有重新计算)。溢出标志只需要一个额外的异或门来将进入高位的进位与高位的进位组合起来。零标志和奇偶校验标志不是免费的(它们取决于结果,而不是计算结果),如果存在部分分离,那么将它们分开计算是有意义的。也许它真的是完全分开的。在 NetBurst™ 中,标志计算需要一个额外的半周期(ALU 是双泵和交错的)[4],但这是否意味着所有标志是单独计算的还是它们的一个子集(甚至是超集[1]提示)不清楚 - 标志结果被视为整体,因此延迟测试无法区分标志是在第三个半周期中由标志单元计算还是由 ALU 传递给标志单元。在任何情况下,典型的 ALU 操作都可以背靠背执行,即使是相关的(意味着第一个操作的高半部分和第二个操作的低半部分并行运行),标志的延迟计算没有挡道。正如您所期望的那样,ADC并且SBB在 NetBurst 上效率不高,但也可能有其他原因(出于某种原因,涉及很多 µop)。

总的来说,我会得出结论,算术标志的存在需要花费大量的工程资源来防止它们对性能产生重大影响,但这种努力也是有效的,因此避免了重大影响。