多年来,英特尔为何改变静态分支预测机制?

时间:2018-08-13 12:46:39

标签: x86 compiler-construction intel cpu-architecture branch-prediction

here起,我知道英特尔近年来实现了几种静态分支预测机制:

  • 80486年龄:永远不被接受

  • 奔腾4年龄:后退/未前进

  • 像Ivy Bridge,Haswell这样的较新CPU已变得越来越无形,请参阅Matt G's experiment here

英特尔似乎不想再谈论它了,因为我在英特尔文档中找到的最新材料是大约十年前写的。

我知道静态分支预测(远不如动态)重要,但是在很多情况下,CPU将会完全丢失,并且程序员(带有编译器)通常是最佳指南。当然,这些情况通常不是性能瓶颈,因为一旦频繁执行分支,动态预测器就会捕获它。

由于Intel不再在其文档中明确声明动态预测机制,因此GCC的buildin_expect()只能从热路径中删除不太可能的分支。

我对CPU设计并不熟悉,我也不知道英特尔现在使用什么确切的静态预测器机制,但是我仍然认为,英特尔的最佳机制应该是清楚地记录他的CPU,“我打算在什么时候去”。动态预测器失败,前进或后退”,因为通常程序员是当时的最佳指南。

更新:
我发现您提到的主题逐渐超出了我的知识范围。这里涉及一些动态预测机制和CPU内部细节,而我在两到三天内无法学习。因此,请允许我暂时退出您的讨论并重新充电。
这里仍然欢迎任何答案,也许会帮助更多人

3 个答案:

答案 0 :(得分:7)

之所以在现代设计中不赞成使用静态预测(甚至可能不存在)的主要原因是,与动态预测相比,静态预测在管道中出现得太晚了。基本问题是分支方向和目标位置必须先于之前获知,而静态预测只能在 解码后(即提取后)进行。

更多细节...

CPU流水线

简而言之,在执行过程中需要从内存中提取指令,解码这些指令,然后执行它们 1 。在高性能CPU上,这些阶段将通过 pipelined 进行流水线处理,这意味着它们通常都将并行发生-但在任何给定时刻都针对不同的指令。您可以阅读有关on Wikipedia的内容,但是请记住,现代CPU更复杂,通常具有更多的阶段。

在现代的x86上,具有复杂的可变长度的指令集,可能在获取和解码指令时可能涉及许多流水线“阶段”,可能是六个或更多。这样的指令也是superscalar,能够一次执行多条指令。这意味着当以最高效率执行时,将有许多指令处于飞行中,处于获取,解码,执行等各个阶段。

重定向提取

分支分支的效果在管道的整个初始部分(通常称为 front-end )上都可以感受到:当您跳转到新地址时,需要从该新地址获取地址,从该新地址进行解码等。我们说,一个采用分支需要重定向提取。这对分支预测可以使用以有效执行的信息施加了某些限制。

考虑静态预测的工作原理:它查看指令,如果是分支,则比较其目标以查看它是“向前”还是“向后”。所有这些必须很大程度上发生在 解码之后,因为那是知道实际指令的时间。但是,如果检测到分支并预测采用了分支(例如,向后跳转),则预测器需要重定向取回,这是较早的流水线阶段。到在解码指令N之后取回重定向的时间时,已经有许多后续指令在错误(未采用)的路径上进行取回和解码。这些必须扔掉。我们说 bubble 是在前端引入的。

所有这一切的结果是,即使静态预测是100%正确的,但由于前端流水线被取消,因此在采用分支的情况下效率非常低。如果在提取和解码结束之间有6个流水线级,则每个分支均会在流水线中引起第6个周期冒泡,并充分假设预测本身和清除不良路径指令的周期为“零周期”。

救援动态预测

但是,现代x86 CPU能够在每个周期最多执行1条分支,即使对于完美预测的静态执行,也必须优于限制。为此,预测器通常无法使用解码后可用的信息。它必须能够重定向每个周期的提取,并且仅使用在最后一次预测后延迟一个周期的可用输入。从本质上讲,这意味着预测器基本上是一个自包含的过程,仅将其自身的输出用作下一个周期的预测的输入。

这是大多数CPU上的动态预测器。它预测从下一个周期取回的位置,然后根据该预测来预测此后从该周期取回的位置,依此类推。它不使用有关已解码指令的任何信息,而仅使用分支的过去行为。它最终确实会从执行单元获得有关分支的 actual 方向的反馈,并根据该反馈更新其预测,但是在相关指令通过预测器后的许多周期中,这基本上都是异步发生的

添加

所有这些都可以消除静态预测的用处。

首先,预测来得太迟了,因此,即使工作正常,也意味着在现代英特尔上出现了6-8个周期的泡沫,这实际上是英特尔所谓的“前端恢复者”提供的数据。 )。这极大地改变了成本/收益等式,根本无法做出预测。当您在获取预测之前拥有动态预测器时,您或多或少希望进行一些预测,并且即使预测精度达到51%,也可能会有所回报。

但是,对于静态预测,如果要进行“采用”预测,则需要具有较高的准确性。例如,考虑8个周期的前端恢复成本,而16个周期的“完全错误预测”成本。假设在某些程序中,冷向后分支的使用频率是不使用分支的两倍。这应该是静态分支预测的胜利,它可以预测反向采用,对(相对于始终“预测” 2 不采用的默认策略)?

不是那么快!如果您假设8个周期的重新控制成本和16个周期的完全错误预测成本,那么它们最终将具有10.67个周期的相同混合成本-因为即使在正确预测的情况下,也存在8个周期的泡沫,但在静态情况下,没有相应的成本。

除了无静态预测的情况下,静态预测的另一半已经正确(不使用前支分支的情况),静态预测的作用还没有想象的那么大。

>

为什么现在要更改?也许是因为管道的前端部分比其他部分要长,或者是因为动态预测器的性能和存储能力的增强意味着根本没有资格进行静态预测的冷支。改善静态预测器的性能还意味着,对于冷分支,反向预测的强度会降低,因为动态预测器会更频繁地记住循环(这是反向采样规则的原因)。

保存动态预测资源

变化也可能是由于与动态预测的交互作用:动态预测器的一种设计是完全不使用任何分支预测资源用于从未观察到的分支。由于此类分支是通用的,因此可以节省很多历史表和BTB空间。但是,这种方案与静态预测器不一致,该静态预测器将反向分支预测为已采用:如果从不采用反向分支,则您不希望静态预测器选择此分支并将其预测为已采用,从而弄乱了您的策略节省未使用分支机构的资源。


1 ...,然后再执行诸如 retire 之类的更多操作-但是执行后发生的情况对于我们此处的目的而言并不重要。

2 在这里,我在恐吓引号中添加了“预测”,因为从某种意义上说,它甚至还无法预测:在没有任何相反预测的情况下,fetch和解码的默认行为是不采用,因此如果您根本不进行任何静态预测,这就是您所得到的,而动态预测变量则不会告诉您。

答案 1 :(得分:4)

我的理解是,在当前设计中,现代TAGE分支方向预测器始终使用最近分支的已采用/未采用历史来索引条目。 (这可能会将单个分支的状态散布到很多内部状态中,从而可以预测非常复杂的模式,例如10个元素的BubbleSort。)

CPU不会尝试检测别名,而只是使用它发现的预测来决定条件分支的采用/不采用。即分支方向预测始终是动态的,永远不会是静态的。

但是在分支解码之前,仍然需要目标预测,以防止前端停滞。通常,对“分支目标缓冲区”进行标记,因为别名的其他某些分支的目标不太可能有用。

@Paul A Clayton points out一样,BTB丢失可能使CPU决定使用静态预测,而不是使用动态采用/未采用预测器中发现的内容。我们可能只是看到,要使动态预测变量经常错过以进行测量静态预测的频率,要困难得多。

(我可能会扭曲。现代TAGE预测器也可以预测间接分支的复杂模式,因此我不确定他们是否甚至尝试根据已采用/未采用或如果第一步总是总是试图预测下一个地址,那么这是否就是下一条指令。Indexed branch overhead on X86 64 bit mode。)


在正确预测的情况下,未采用的分支仍会便宜一些,因为前端可以更容易地在同一周期内从uop缓存中获取早先和晚些的指令。(uop Sandybridge系列中的缓存不是跟踪缓存; uop缓存行只能缓存来自x86机器代码的连续块中的uops。)在高吞吐量代码中,采用的分支可能是次要的瓶颈。他们通常还会将代码分布在更多的L1i和uop缓存行上。


对于间接分支,“默认”分支目标地址仍是下一条指令,因此在ud2之后放置jmp rax或其他内容以防止错误推测(尤其是特别有用)非常有用。转换成非代码),如果您不能简单地将真实的分支目标之一作为下一条指令。 (尤其是最常见的一个。)


分支预测是CPU供应商不发布有关细节的“秘密调味料”。

英特尔实际上是自己发布指令吞吐量/延迟/执行端口信息(通过IACA和一些文档),但是通过实验进行测试相当简单(例如https://agner.org/optimize/http://instlatx64.atw.hu/已经完成),因此并非如此就像英特尔即使愿意也可以保守秘密。

分支预测成功率很容易用perf计数器来衡量,但是要知道为什么为什么一个特定分支在一个特定执行中没有被正确预测;很难。除非单独使用rdtscrdpmc或其他工具来检测代码,否则即使对单个分支的执行也很难进行测量。

答案 2 :(得分:4)

《英特尔优化手册》第3.4.1.3节中讨论的静态分支预测如下:

  • 预测要采取的无条件分支。
  • 预测不接受条件转发分支。
  • 预测要执行的条件后向分支。
  • 预测不采用的间接分支。

编译器可以相应地组织代码。同一部分中的内容如下:

  

英特尔酷睿微体系结构未使用静态预测   启发式。但是,要在整个Intel 64和IA-32上保持一致性   处理器,软件应保持静态预测启发式   作为默认设置。

此声明表明3.4.1.3节已经很多年没有更新了。

如果动态预测器无法预测所提取的字节中是否存在分支指令,或者其缓冲区中未命中,则由于没有其他有意义的选择,因此提取单元将继续按顺序进行提取,从而有效地静态预测未采取。

但是,如果事实证明,在指令队列单元中,所提取的字节流中存在条件或间接分支指令,则此时进行静态预测可能会比不进行预测更好。拿了特别是,预测有条件的直接向后分支。这可以减少动态预测器和“未获取”获取单元失败的代价,尤其是前端的性能是如此关键。据我所知,优化手册中没有明确的陈述指出IQU上有这样的静态预测器,并且适用于现代处理器。但是,正如我在其他answer中所讨论的那样,对某些性能计数器的描述似乎暗示着IQU上可能存在这样的静态预测器。

总体而言,我认为这是英特尔不再记录的实现细节。

确实存在编译器辅助的动态分支预测技术,并且该技术可以如您所建议的那样非常有用,但是在当前的Intel处理器中并未使用它们。