让我们仔细看看Cortex-A72微体系结构中的指令获取,解码和分配。这些是核心管道的“前端”阶段。流水线的“后端”由寄存器文件,执行单元和退出(重新排序)缓冲区组成。分支预测经常与前端相关联,因为它直接影响指令的提取。
前端有一项主要工作:提取ARMv8指令并保持后端执行单元尽可能繁忙。
对于想要为ARM Cortex-A72调整程序的Raspberry Pi 4(BCM2711,ARM Cortex-A72)程序员,必须阅读此页。对于在Raspberry Pi 4上使用PERF(Linux的性能事件)进行性能测量的程序员来说,这也是必要的背景信息。
Cortex-A72管线具有15个阶段。 [在线资源在长度上存在分歧;一些来源声称有14个阶段。]前端阶段是:
在冯·诺依曼(von Neumann)成立70多年后,我们仍然坚持线性的顺序程序模型。在程序员看来,即使执行单元可以并行执行某些预期的高级计算,程序计数器(PC)也会按顺序遍历指令。此外,我们强迫编译器以相同的线性顺序方式放置指令。
前端的工作是在传入的指令流中找到指令级并行性(ILP)。 Cortex-A72前端将ARMv8指令转换为微指令。它将微操作发送到后端功能单元,以执行和体系结构级退役。
这一切似乎都是低效而疯狂的。程序表示和执行需要根据曾经研究过的数据流体系结构进行重新思考。为什么要做然后撤消?
前端转换实际上是两个步骤:将ARMv8指令转换为宏操作,然后将宏操作转换为微操作。让我们检查一下细节。
首先,访存会获取一个16字节(128位四字)的访存窗口,并且它会识别该窗口内(以及跨窗口)的ARMv8指令。然后,解码阶段将ARMv8指令转换为一个或多个宏操作。如果可能的话,解码器会将多个ARMv8指令融合到一个宏操作中。建筑寄存器被重命名为内部寄存器文件,该文件临时保存中间结果。接下来,将宏操作转换为微操作,并将微操作分配到属于适当功能单元的发布队列。将微型操作分派到8个独立的发布队列(每个执行管道一个队列)。指令成功完成后,该指令将退出,其结果将提交给体系结构计算机状态。 [我将在一分钟内讨论“成功完成”的含义。]
在机器周期内处理的ARMv8指令,宏操作和微操作的数量受到限制:
在每个周期中,Cortex-A72可以解码3条ARMv8指令,最多生成3个宏运算,并分发最多5个微运算。可以同时分派的每种类型的微型操作的数量还有其他限制(引用Cortex-A72软件优化指南):
如果有更多微操作超出这些限制,则将按照从最老到最年轻的顺序进行发送。
根据ARM的说法,大多数ARMv8指令都转换为单个微操作(平均:每条指令1.08微操作)。
寄存器重命名允许微操作无序执行。记住,我们强迫编译器按顺序放置ARMv8指令。寄存器重命名允许微操作无序执行,而不会违反体系结构寄存器之间的数据依赖性。有128个物理重命名寄存器。
现在真正棘手的东西是投机执行。基本块是“直线”代码的序列,在块的末尾具有单个分支指令。程序控制流入一个基本块,并在最后将其重定向到相同的基本块或不同的基本块。
通常,基本块长约9条指令。最后的分支可以是条件分支。短块长度和条件分支的结合限制了可以在单个基本块内主动获取,解码和分派的ARMv8指令的数量。没有推测执行,处理器必须在每次需要确定条件分支时等待。
输入分支预测。当Cortex-A72碰到条件分支时,它会预测方向:已采用或未采用。 [有关分支预测的更多信息,请稍后。]前端继续沿预测的控制流路径进行获取,解码和调度。如果预测正确,请加油!预测变量已正确猜测,执行单元已经在计算有用的结果。当每条ARMv8指令沿一条已知的正确路径完成时,该路径上的指令将按体系结构顺序退出。这就是上面“成功完成”的含义。
如果没有正确预测条件分支(“错误预测”),则会丢弃错误路径上的中间结果,并告知提取阶段从正确的目标地址开始提取。如短语“重新引导前端”那样,这称为“重新引导”。从分支的错误预测中恢复需要进行管道刷新,是的,这很昂贵-至少需要15个周期。
回写/退出阶段会跟踪“退出指针”,即体系结构程序计数器。退役阶段是一些最难设计的硬件逻辑,必须正确。退役阶段维护一个重新排序缓冲区,该缓冲区用于保存有关指令和微操作状态的书籍。重排序缓冲区具有128个条目,最多可同时运行128条ARMv8指令。
高性能取决于分支预测的准确性。如果预测变量在大多数情况下都能正确猜测,那么投机执行将是一个成功。如果预测变量的猜测不正确,则内核必须丢弃无用的结果,并且执行管道停滞。
对分支预测的深入研究超出了本说明的范围。所以,这是一个草图。预测器维护一个模式历史表(PHT),该表保留核心遇到的(最近)分支的已采用或未采用状态。预测器还维护包含这些分支的目标地址的分支目标缓冲区(BTB)。预测器在再次遇到分支时使用模式历史记录来预测分支的方向。 BTB提供关联的目标地址。
Cortex-A72允许拆分BTB条目,同时容纳近分支(小目标地址)和远分支(大目标地址)。这就是为什么您会看到BTB容量引用为2K至4K条目的原因。 A72 BTB可以容纳多达2K个大目标地址(远)和4K个小目标地址(近)。 A72还具有一个微型BTB,它充当主BTB的缓存。微型BTB有64个条目。
分支预测窗口为16个字节,这对基本块布局有影响。根据Cortex-A72软件优化指南,分支目标应按四字对齐(即16字节边界),并且同一指令中与四字对齐的四字应包含不超过两个采用的分支。
编译器确实应该为您处理四字对齐和打包。作为高级语言程序员,您可以通过将流条件偏向正确的程序路径或相应的错误程序路径来改善分支预测。如果更频繁地执行给定路径(true if子句或false if子句),则分支预测器应该更好地预测编译器生成的基础条件分支。
Cortex-A72具有子程序返回和间接分支的预测器。预测变量维护一个8 [?]条目的调用/返回堆栈,该堆栈会记住最近的返回地址。测量表明,对于大多数高级语言工作负载而言,有8个条目(大约)就足够了,涵盖了最近调用的功能。
模式历史记录表(PHT)中存在三种干扰源:*冷误(强制别名)* PHT容量错(容量别名)*冲突错(冲突别名)可以通过对PHT进行分区或使用其他方式减少冲突错索引方案。
ARM Cortex-A15双模式预测器使用两个模式历史记录表和一个选择预测器,以减少不同程序模式下分支的负面干扰。模式历史不是一个大的PHT,而是分成两个半部分,即两个较小的PHT。选择预测器表选择两个PHT之一。选定的一方提供最终预测。
Cortex-A72不使用双模式预测器。如果您想知道,Cortex-A72也没有micro-op循环缓冲区。
如果您有早期的Raspberry Pi型号,请在此处阅读有关ARM11微体系结构的信息。 请不要忘记我的PERF(Linux性能事件)教程。