前言
这篇文章讨论了几种分支预测的实现方式。具体内容如下:
内容
introduction
这篇文章只考虑预测分支跳转方向,不讨论跳转的目标地址。
Bimodal Branch Prediction
分支行为的特点:大多数程序中的分支指令并不是随机的,通常分支要么是“经常被跳转”(usually taken),要么是“经常不跳转”(usually not taken)。这种“二元”行为(即通常跳转或通常不跳转)是双模分支预测的基础。
双模分支预测:
- 双模分支预测利用了分支行为的二元分布。它试图将经常跳转的分支和经常不跳转的分支区分开。
- 一种简单的实现方法是使用一个计数器表,其中每个计数器由程序计数器(PC)地址的低位索引。每个计数器有两个比特位。
- 每次分支跳转(taken),对应的计数器增加;
- 每次分支不跳转(not-taken),对应的计数器减少。
- 计数器采用饱和机制,即计数器不会小于0或大于3。计数器的高位决定预测结果。
- 对于经常跳转的分支,预测为跳转;对于经常不跳转的分支,预测为不跳转。
预测器的行为:
- 由于使用了2位计数器,预测器可以容忍分支方向偶尔发生的变化(即分支偶尔出现反向跳转),仍然保持对通常方向的预测。
- 如果使用较大的计数器表,每个分支会映射到一个唯一的计数器;如果表较小,多个分支可能共享一个计数器,这会降低预测准确性。
替代实现:
- 一种替代的实现方法是为每个计数器存储一个标签,并使用集合关联查找(set-associative lookup)来匹配分支与计数器。这样可以提高性能,尤其是在固定数量的计数器时,集合关联预测器的性能更好。
- 然而,当考虑到标签的大小时,简单的计数器数组在给定预测器大小时往往具有更好的性能。这在需要支持分支目标缓冲区(Branch Target Buffer,BTB)时可能不成立。
性能评估:
- 使用SPEC’89基准测试(包括符号和数值应用程序)对各种分支预测策略进行了比较。实验模拟了每个基准测试前1000万条指令的执行过程。
- 结果显示,双模分支预测的准确性随着预测器大小的增加而提高,因为更多的分支可以映射到唯一的计数器。当每个分支都映射到一个唯一计数器时,预测准确率达到93.5%的饱和点,进一步增加预测器大小不会显著提升准确性。
- 如果使用集合关联预测器,准确率的饱和点也在93.5%左右。
Local Branch Prediction
循环分支行为:
- 许多分支(特别是循环控制分支)执行的行为是重复的。例如,在一个循环中,循环条件的分支可能会表现出规律性的模式(如:
1110 1110 1110 ...
),即通常是跳转一次后不跳转。通过分析历史分支行为,可以准确预测下一次分支的方向。
改进双模分支预测的方法:
- Yeh和Patt的局部分支预测方法该方法通过记录最近分支的历史来预测分支方向。具体来说,方法使用了两张表
- 第一张表:记录最近分支的历史,表的每个条目记录对应分支的历史行为。通常,这张表是基于分支地址的低位索引来实现的。
- 第二张表:是一个2位计数器数组,和双模预测器中的计数器相似。不同的是,这张表的索引是基于第一张表中的历史信息。计数器的值决定分支的预测结果(跳转或不跳转)。
局部预测的具体实现:
- 在该方法中,每个分支都有自己的历史记录,这个历史记录反映了最近多次执行该分支的方向。然后,第二张表通过历史记录来确定分支行为的预测。
- 举例来说,对于一个简单的循环,其历史表条目只存储当前循环分支的历史,而计数器表则反映该分支的行为。经过若干次执行后,预测器可以基于历史值正确预测分支行为。
局部分支预测中的冲突问题:
- 历史冲突:由于多个分支可能会映射到相同的历史表条目,导致不同分支的历史混合,这可能会影响预测准确性。
- 计数器冲突:所有分支共享同一个计数器数组,可能会导致不同分支模式之间的冲突。例如,一个分支和另一个分支可能有不同的行为模式(如:
1110
和0110
),在历史记录相同的情况下,它们会争夺相同的计数器。 - 解决冲突:通过增加历史位数(例如,使用4位历史)和增加计数器数量(如增加到24个计数器),可以减小这种冲突,从而提高预测准确性。
局部分支预测的性能:
- 性能随预测器大小的变化:随着预测器表的大小增加,局部分支预测器的性能逐渐超过双模预测器。特别是在预测器表大小超过128字节时,局部分支预测器的准确率显著提高,最终可以达到97.1%的准确率,预测错误次数不到双模预测器的一半。
- 小型预测器的劣势:对于非常小的预测器,由于历史记录条目过于拥挤(即多分支共享相同的条目),局部预测器的性能甚至不如双模预测器。
Global Branch Prediction
局部分支预测与全局分支预测的对比:
- 在局部分支预测中,只考虑当前分支的历史模式。每个分支有自己的历史记录,基于这些历史信息来预测该分支的行为。
- 全局分支预测(Global Branch Prediction)则考虑所有最近的分支的历史信息,而不仅仅是当前分支。这意味着一个单独的**移位寄存器(GR)**记录最近几个条件分支的执行方向。全局历史是针对所有分支共享的,这种方法利用全局历史来预测当前分支的行为。
全局分支预测的两种有效模式:
- 依赖其他分支的方向:全局历史可以帮助预测当前分支的执行方向,特别是当多个分支具有相关条件时。例如,若分支
if (x<1)
和if (x>1)
条件相关(一个分支的结果可能影响另一个分支的执行),全局预测可以更好地预测第二个分支的行为。 - 模仿局部分支预测的行为:全局历史也可以帮助模拟局部分支预测的效果。如果全局历史包含了足够的局部历史信息,能够做到和局部分支预测一样的准确。例如,在嵌套循环的情况下,全局历史可以区分出不同的循环条件,从而准确预测分支行为。
全局分支预测的性能比较:
- 图表比较了全局分支预测、局部分支预测和双模分支预测的性能。结果显示,在固定大小的预测器下,全局分支预测的效果远不如局部分支预测。只有当预测器大小超过1KB时,全局分支预测才比双模预测更有效。
- 小型预测器:对于较小的预测器,双模分支预测更有效,因为它能够通过分支地址的低位有效地区分不同的分支。
- 大规模预测器:随着预测器规模的增大,每个分支会映射到唯一的计数器,分支地址的效用降低,反而全局历史寄存器的表现会逐渐变好,因为它可以捕获更多的信息。
信息内容和效能:
- 双模预测器在小型预测器中效率较高,因为分支地址的低位能够有效地区分不同的分支。
- 全局历史寄存器在小型预测器中的信息内容相对较小,但随着预测器的增大,能捕获更多的历史信息,最终可以获得比双模预测更高的准确度。
- 图表显示,当全局历史寄存器足够大时,它能够捕捉到更多的分支行为信息,超过一定规模后,全局分支预测的表现优于双模预测。
Global Predictor with Index Selection
全局历史与分支地址结合:
- 之前的讨论指出,全局历史信息比直接使用分支地址来识别当前分支要低效。因此,gselect预测提出将全局历史信息和分支地址位结合使用。这种方法通过将这两者的位串联起来作为索引来查找计数器表,从而改善了分支预测的性能。
性能比较:
-
图9展示了不同预测表大小下gselect-best方法的性能,gselect-best代表的是根据不同基准选择的最佳位选择方案。
-
gselect-best的表现优于双模预测和全局预测。对于较小的预测器,gselect-best的表现与双模预测类似,但当地址位足够多以区分大多数分支时,它会更多地使用全局历史信息,从而使预测准确度大大提高。
-
gselect-best方法还显著优于简单的全局预测,因为分支地址位更有效地识别分支。
与局部分支预测的比较:
- 对于小于1KB的预测器,gselect-best的表现优于局部分支预测。全局历史所需的存储空间几乎可以忽略不计,因此即使在较大的预测器下,gselect和局部预测的准确度也接近。
存储和访问效率:
- 相比局部分支预测,gselect预测的优势在于它只需要访问一个数组,而局部分支预测则需要依次访问两个数组。因此,gselect预测可能具有更低的延迟,并且更容易进行流水线化处理。
Global History with Index Sharing
全局历史的冗余性:
- 在全局预测中提到,全局历史信息在识别当前分支时效率较低,因为它无法有效地区分不同的分支。使用gselect方法时,可能会有很多冗余信息。特别是当足够的地址位可以用来唯一标识分支时,全局历史的组合将会变得稀疏,从而可以利用这种稀疏性来改进预测。
gshare预测方法:
-
gshare是对gselect方法的改进,它通过对分支地址和全局历史进行异或(XOR)操作,来生成更具区分度的索引。相比于直接连接分支地址和全局历史(gselect方法),异或操作能够更好地利用两个部分的信息,生成更有效的索引。
-
例如,当只有两个分支,并且每个分支的全局历史只有两种可能时,gshare 8/8(使用8位分支地址与8位全局历史的异或操作)能够区分出所有四种情况,而gselect 4/4(直接将分支地址和全局历史的低4位连接)则无法区分出这四种情况。
gshare与gselect的比较:
- gshare在使用异或操作后,能够利用更多的信息,而不仅仅是简单地将地址和历史位连接。因此,对于较大的预测器(256字节及以上),gshare的表现优于gselect,两者的性能差距较小。
- 对于较小的预测器,gshare的表现不如gselect。这是因为在较小的预测表中,不同的分支可能会争夺同一个计数器,添加更多的全局信息反而可能会导致更糟的效果。
gshare的优势和局限:
- 优势:当预测器较大时,gshare能够比gselect更好地利用全局历史和分支地址的组合,提升预测准确性。
- 局限:对于小的预测器,由于计数器竞争问题,gshare反而会表现得比gselect差。
Combining Branch Predictors
组合预测器设计:
-
组合分支预测器包括两个单独的预测器P1和P2(可以是任何之前讨论的预测方法)。此外,还有一个额外的计数器数组,用于跟踪哪一个预测器对特定分支的预测更为准确。
-
计数器使用2位上下限饱和计数器(即计数器值在0和3之间),根据P1和P2预测器的准确性更新计数器。例如,当P1预测正确而P2预测错误时,计数器会递增,反之递减。具体根据P1c和P2c来确定,P1c表示预测器P1在当前分支上的预测是否正确。如果P1正确预测了分支的方向,P1c的值为1;如果P1错误预测了分支的方向,P1c的值为0。P2c:表示预测器P2在当前分支上的预测是否正确。如果P2正确预测了分支的方向,P2c的值为1;如果P2错误预测了分支的方向,P2c的值为0。用P1c-P2c来决定对计数器的变化:
-
选择器计数器通过增减的方式决定了接下来预测哪个预测器。假设:
- 计数器值为 2,表示倾向选择 P1(因为 P1 表现更好)。
- 计数器值为 1,表示 P1 和 P2 的表现相当,可能会交替选择。
- 计数器值为 0,表示倾向选择 P2(因为 P2 表现更好)。
**组合预测器实例 - **bimodal/gshare:
- bimodal/gshare组合是一个典型的例子,结合了bimodal预测器(使用分支地址)和gshare预测器(使用全局历史)。当全局信息有用时,使用gshare;否则,使用bimodal预测器。
- 这种组合方法充分利用了两者的优势,gshare在有足够的全局信息时表现更好,而bimodal则在较小的历史信息下仍然有效。gshare的优势在于它能利用更多的全局历史信息,而gselect则相对较差。
性能比较:
- SPEC’89基准测试结果显示,bimodal/gshare组合的表现始终优于单独的bimodal或gshare预测器。例如,在eqntott基准中,gshare显著优于bimodal,而组合预测器的性能与gshare相同。
- 组合预测器通过根据分支的不同动态选择预测器,能够针对不同分支使用不同的预测方法,提升了总体准确性。
使用情况:
- 在bimodal/gshare组合预测器中,bimodal预测器通常被更频繁地使用,尤其是在一些较不容易预测的基准(如gcc)。然而,对于某些特定的分支(如eqntott),gshare预测器会被更多使用。
预测器大小与性能:
- 组合预测器的大小是单独预测器的三倍,因为它包含了两个预测器和一个选择器数组。在不同大小的预测器中,bimodal/gshare组合的准确性逐渐提高,且其性能接近于更大单一预测器(如16KB的gselect)。
- 在更大的预测器(2KB及以上)中,local/gshare组合预测器表现优于bimodal/gshare,并且能够达到98.1%的预测准确率。
Suggestions for Future Work
如何通过不同的手段来改进分支预测器,使其更加准确且低成本。具体而言,文中提出了几种可能的扩展方向:
- 探索更多参数:目前的研究没有充分探索一些参数(如预测器的大小、关联度、流水线开销等)。通过深入研究这些参数的空间,可能会找到更好的分支预测器。
- 利用更多的信息:分支目标是向前还是向后可能是一个有用的信息,可以被添加进来以提高预测的准确性。例如,分支目标的方向(前向或后向)可能有助于预测分支的行为。
- 压缩分支历史:通常分支历史数据是稀疏的,可能通过压缩分支历史来减少需要的计数器数量,从而降低成本。
- 通过编译器优化减少分支预测的需要:通过使用带有配置文件支持的编译器,可能能够减少甚至消除对分支预测器的需求。例如,先前的研究表明,使用配置文件信息来设置“可能被采取”的位,可以使准确性接近于二元模式(bimodal)预测。对于经过这种优化的代码,bimodal/gshare 方案中的 bimodal 预测器可能变得不必要。
- 通过更多的优化消除 gshare 预测器:通过更仔细的分支条件检查或更精确的分支模式分析,可能能够消除 gshare 预测器。例如,利用语义分析或更精细的配置文件分析来动态检测分支之间的相关性,从而优化分支模式,进而简化预测方法。
- 循环结构的优化:如果分支模式由循环结构引起,可能可以通过适当的循环展开来利用已检测到的典型迭代次数,这种优化可以通过语义分析或更详细的配置文件分析来实现。
参考文献:
“Combining BRANCH Predictor”, McFarling , DEC WRL Technical Note TN-36,1993