超标量处理器之分支预测(四)

四、分支预测

1. 什么是分支预测

   程序中有一种叫做分支指令的指令,如果在取指令阶段就可以预知本周期所取指令中是否存在分支指令,并且知道其方向(跳转/不跳转)以及目标地址,就可以从在下个周期从分支指令的目标地址开始取指,让流水线正确进行,提高处理器的执行效率。

  • 静态分支预测:预测分支指令总是不执行的,处理器总是顺序地取指令;
  • 动态分支预测:并不简单预测分支指令一直跳转或不跳转,而是根据分支指令在过去一段时间的执行情况来决定预测结果。
       分支预测的最好时机是在当前周期得到取指令地址的时候,在取指令的同时进行分支预测,这样在下个周期就可以根据预测的结果继续取指令。

2. RISC分支指令类型

   根据分支指令是否执行的条件判断,分为无条件跳转分支指令与条件跳转分支指令;
   根据分支指令跳转地址的计算方式,分为直接跳转分支指令与间接跳转分支指令。

  • 无条件/条件:无条件跳转分支指令一定会发生跳转,条件跳转分支指令需要根据条件判断是否跳转。
  • 直接/间接:直接指跳转的目标地址可以根据指令编码的立即数和当前地址直接计算,间接指跳转的目标地址需要根据从寄存器索引的操作数计算。
    组合来看,可以细分为四种跳转指令:
    • 无条件直接跳转分支指令,如RISC-V中的jal(jump and link)指令;
    • 无条件间接跳转分支指令,如RISC-V中的jalr(jump and link-register)指令;
    • 条件直接跳转分支指令,如RISC-V中的beq等指令(RISC-V中有6条此类指令);
    • 条件间接跳转分支指令,RISC-V中没有此种指令。

3. 分支指令的方向预测

  1. 基于两位饱和计数器的分支预测
       核心理念:当一条分支指令连续两次执行的方向都相同时,第三次也会有相同的方向;如果一条指令只是偶尔发生一次方向的改变,那么这条分支指令的预测值不会马上跟着改变,分支方向偶尔的变化将会被过滤掉。
       算法过程:根据一条分支指令的前两次执行结果预测本次方向,用四个状态机表示:

    • Strongly taken:饱和状态,预测跳转,编码11;
    • Weakly taken:不饱和状态,预测跳转,编码10;
    • Weakly not taken:不饱和状态,预测不跳转,编码01;
    • Strongly not taken:饱和状态,预测不跳转,编码00;
      状态机处于饱和状态时,只有两次预测失败才会改变结果。适用于分支指令方向总是朝一个方向,状态机处于饱和状态,则正确率较高。
      在这里插入图片描述
      分支预测以PC值为基础,每一个PC(每一条指令)都对应一个计数器的话无法实现。因此使用PC的部分值寻址计数器。之所以可以这么做,是因为这样虽然会带来多个PC对应同一个计数器的问题,但并非所有指令都是分支指令,冲突的指令中如果只有一条是分支指令,则不影响。
    • PHT (Pattern History Table): 存放PC值的一部分对应的两位饱和计数器的值。
    • 别名(aliasing):PC值的k部分相同的两条指令会对应PHT的同一个表项。可以对PC值哈希处理后再寻址PHT,降低别名影响。
    • 更新PHT
      • 在取指阶段,进行分支预测时根据预测的结果更新PHT;
      • 在执行阶段,分支指令的方向被实际计算出时更新PHT;
      • 在提交阶段, 分支指令要离开流水线时更新PHT,最为可靠。
        在这里插入图片描述
  2. 基于局部历史的分支预测
       理论上任何有规律的事都能被预测,可以使用一个寄存器记录一条分支指令过去的历史状态,当历史状态有规律时,对其进行预测,这样的寄存器叫分支历史寄存器(Branch History Register,BHR)。一个位宽为n位的BHR可以记录过去n次的结果。这种方法也称自适应的两级分支预测

    • BHR位宽为n,记录指令过去n次的结果,PHT的地址宽度与之对应,用BHR寻址PHT;
    • PHT中每个表项其实并非饱和计数器,而只是存储了一个计数器的值,每次更新表项需要先读出计数器值, 然后放到统一的饱和计数状态机里获得下一个状态的值再写回。
    • 例子:
         一个n=2的BHR,对应一个有四个表项的PHT(entry0、entry1、entry2、entry3),假设分支序列是101010…,那么BHR只有两种值:10和01,当值为10时,下一次一定是1,即一定跳转,10寻址PHT第三个表项entry2,因为这个表项的计数器每次都跳转,因此会达到strongly taken状态,得到的预测结果为跳转。同理当BHR值为01时可预测为不跳转。
    • 规律:如果一个序列中,连续相同的数最多有p位,那么这个序列的循环周期为p。只要分支历史寄存器BHR的宽度n不小于序列的循环周期p,就可以对其进行完美预测
      在这里插入图片描述
         理论上,每个PC都需要一个BHR和一个PHT,但是所需要的存储空间过大,实际无法实现。所有的BHR组合在一起形成分支历史寄存器表(Branch History Register Table,BHRT/BHT)。可以使用PC值的一部分寻址BHT和PHTs,但PHT依旧占用了较大的空间,实际上对一个BHR而言只会用到PHT中的很少一部分,因此采用极端情况——PHTs中只有一个PHT。
      在这里插入图片描述
      在这里插入图片描述
      如此可以节约PHT的空间,但也会带来冲突:
         1> 两条分支指令的k部分相同,寻址到同一个BHR寄存器和PHT表项,结果互相干扰;
         2> 两条分支指令虽然对应不同的BHR,但BHR内容相同,寻址到同一个PHT表项。
      解决办法为:
         1> 不用PC的k bit寻址BHT,而是将PC经过Hash处理寻址,相比直接用k bit,产生冲突的可能性小(解决冲突1);
         2> 不用BHR寻址PHT,而是将BHR与部分PC组合寻址,即使两条指令的BHR相同,所用部分PC不同则不会产生冲突(解决冲突2)。拼接只是一种最简单的方法,还可以使用异或等方法得到更好的效果。
      在这里插入图片描述
  3. 基于全局历史的分支预测
       基于局部历史的分支预测是针对一条指令过去的执行情况实现的,而有些时候一条分支指令的跳转与否并不取决于自身过去的执行情况,而是和前面的分支指令的结果息息相关,这样就需要使用基于全局历史的分支预测。
      例如:如果b1和b2都执行了,那么b3就不会执行,这种情况只依靠b3的局部历史进行分支预测就无法发现这个规律,就需要在预测b3时将b1和b2考虑进去,即基于全局历史的分支预测。
    在这里插入图片描述

    • 全局历史寄存器(Global History Register,GHR):记录程序中最近执行的分支指令的结果。
    • 使用GHR的值寻址PHT,PHT仍由饱和计数器组成,以此捕捉GHR的规律。
    • 理想情况下,每个PC都设置一个PHT,组成PHTs,这样就算两条分支指令对应的GHR值相同也不会产生冲突。但是实际无法实现,因为这样的PHTs所耗空间非常大。因此一般会对PC进行Hash处理,减少PHTs的表项。
      在这里插入图片描述
    • 极端情况,只使用一个PHT,带来的问题是相同GHR值的两条分支指令会共用PHT的同一个饱和计数器,造成冲突。解决办法类似基于局部历史分支预测的处理,即将PC与GHR组合处理后寻址PHT,这样即使两条不同的分支指令对应相同的GHR值也不会产生冲突。
      在这里插入图片描述
    • 基于全局历史的分支预测也有其局限性,比如无法完美预测TNTNTNT…这种分支指令(基于局部历史可以),因此要根据指令类型选择合适的分支预测算法。
  4. 竞争的分支预测
       有些分支指令适合基于全局历史的预测方法,而有些适合基于局部历史的预测方法,如果根据实际情况对不同的分支指令使用不同的预测方法,就可以获得更优的效果,即竞争的分支预测。
    在这里插入图片描述

    • CPHT(Choice PHT):由PC寻址,由两位饱和计数器组成,当其中一种预测方法两次预测失败,而另一种两次预测成功时,会使状态机转向另一种预测方法。 状态机如下:
      在这里插入图片描述
    • 对于同一条分支指令,在执行过程中当GHR的内容不同时可能会导致使用不同的分支预测方法,因此可以将PC值与GHR值进行一定运算用于寻址CPHT。
      在这里插入图片描述
  5. 分支预测的更新
       理想情况下,根据PC值进行分支预测,使用预测的结果进行取值执行,等到真正得到分支的结果后更新分支预测器的内容。但实际情况下,流水线可能很深,得到分支结果后才更新预测器会不及时。分支预测的更新需要考虑更新的时间和更新的内容。

    • 历史寄存器的更新:在基于全局历史的分支预测中时GHR,在基于局部历史的分支预测中是BHR;
    • 两位饱和计数器更新: 基于全局历史和基于局部历史的分支预测中都需要使用PHT的饱和计数器来捕捉历史寄存器的规律。
    1. 更新全局历史寄存器
      有三个时间点可以更新GHR:
      • 取指阶段,根据分支预测结果更新GHR;
        在提交阶段更新最保守也最安全,但提交之前取指的所有分支指令都没有享受到这条分支指令的结果。
      • 分支指令方向被实际计算出来时,如执行阶段;
        在执行阶段更新相比提交阶段提前很多,这取决于执行和取指的间隔周期,但是执行阶段的指令可能本身就处在错误路径上,此时更新的也不一定准确。
      • 提交阶段,分支指令的方向已经完全确定;
        在取指阶段更新,当一条分支指令预测失败了,即使后续的分支指令都使用了错误的GHR也没有关系,因为后续的这些分支指令都处在错误的路径上,会被流水线冲刷掉,那这些指令使用的GHR正确与否都没有关系。

       取指阶段更新GHR也是推测的,更新GHR的方式在分支预测失败时需要一种机制对GHR进行修复,有两种修复方法:
       1> 提交阶段修复法:在流水线的提交阶段放置一个GHR,记录正确的结果。当发现分支预测失败时,表明前端取指所用的GHR错误,等待这条分支指令退休时将后端的GHR写到前端GHR(发现预测失败但不能及时更新,使得分支预测失败时惩罚加大)。
       2> Checkpoint修复法:对前段GHR更新时保存旧的GHR,保存内容即Checkpoint GHR,当分支指令的结果被计算出来(例如执行阶段),就可以知道预测正确与否,正确的表明前端GHR正确,继续执行,否则将Checkpoint GHR的内容恢复到前端GHR。
       对于乱序执行的流水线,执行阶段得到的结果也可能错,因为指令可能在错误的路径上,因此还是需要在提交阶段放置一个Retired GHR进行最后的检查和恢复。从本质上看Checkpoint方法相当于方法一的补充,增加在执行阶段进行的恢复,加快在分支预测失败时的修复时间。
    2. 更新局部历史寄存器
    在分支指令退休的时候更新BHR,简化设计的同时不会对处理器性能产生太大影响。因为一般只有在循环体很短的情况下,才会出现一条分支指令在流水线的提交阶段更新BHR时,流水线中又出现了使用BHR进行分支预测的情况。
    3. 更新饱和计数器
    当一条分支指令比较有规律时,对应的饱和计数器会处于饱和状态,即使在分支指令退休时更新饱和计数器也不会对分支预测的准确度产生很大影响。

4. 分支指令的目标地址预测

  1. 直接跳转类型的分支预测
       直接跳转分支指令的的目标地址根据当前PC和偏移值确定,偏移值以立即数的形式编码在指令中,因此其目标地址固定,因此只要记录其目标地址,后续判断指令跳转只需要取出目的地址即可。
       由于分支预测是基于PC的,无法记录每个PC的目标地址,因此使用Cache的形式使多个PC共用一个空间存储目标地址,即BTB(Branch Target Buffer)。BTB中存放分支指令的目标地址(Branch Target Address,BTA), 使用PC值的部分(index)寻址BTB,由于index相同的不同指令会寻址到相同BTB,因此BTB中还有Tag(Branch Instruction Address,BIA)字段进行区分。
       当寻址到相同BTB同一处的PC中不止一条分支指令,就会产冲突,因此BTB采用组相连的结构,将这些PC的BTA放在不同的way中。实际中way的个数较小,因为这会增加BTB的设计难度和访问速度。
    在这里插入图片描述
       进一步优化,可以减小Tag的位数,比如只用PC值的部分作为Tag,更好的方法是将完整的Tag进行一定运算进行压缩,比如异或等。为了最大限度利用BTB,可以只将发生跳转的分支指令的目标地址放入BTB,因为预测不发生跳转的指令顺序取指,目标地址很容易得到。
       BTB缺失处理:当一条分支指令预测跳转,但BTB中没有对应的目标地址,就发生了BTB缺失。处理方法有两种:
    • 停止执行
      暂停流水,直到这条分支指令的目标地址被计算出来。
    • 继续执行
      继续使用顺序的PC值取指,如果后续计算出的目标地址与顺序取指不一致,就抹掉错误路径上的指令。因为预测可能是错的,相比暂停流水线,顺序取指也有可能是正确的,会有一定的收益。带来的问题是会增加功耗,需要权衡。
  2. 间接跳转指令的分支预测
       间接跳转分支指令的的目标地址来自于通用寄存器,无法通过BTB预测。所幸大部分分支跳转指令是CALL/Return指令,有规律可循。
    • CALL/Return指令的分支预测
         对于程序中的一条指定的CALL指令来说,每次调用的子程序是固定的,因此其对应的目标地址是固定的,可以使用BTB进行预测。
         Return指令的目标地址不确定,但总是等于最近一次执行的CALL指令的下一条指令的地址。设置一个类似堆栈的存储器,具有先进后出的特点,称之为返回地址堆栈(Return Address Stack,RAS),将CALL指令的下一条指令的地址压入栈中,即为对应Return指令的返回地址。
      在这里插入图片描述
         用BTB和RAS预测CALL/Return指令的还需要做到,在分支指令到来时识别出CALL/Return指令,因此需要在BTB中增加一个字段指示指令类型,否则等解码时才识别指令类型,会使得期间到来的Return指令无法从RSA得到正确地址,降低预测准确率。
         实际上RAS的容量是有限的,如果子程序嵌套过深,会超出RAS容量限制。可以采取继续顺序写入RAS的策略,将最旧的内容覆盖。而且对于连续写入的返回地址一致的情况,可以用计数的方式记录,出栈时改变计数即可,相当于扩展了RSA的容量。
    • 其他预测方法
         对于非CALL/Return类型的分支指令,理论上可能的目标地址很多,无法预测。但实际这种间接跳转分支指令的目标地址只有固定的几个,比如case语句。这种类型的分支指令的目标地址可能和过去的执行情况相关,可以采用基于局部历史的分支预测方法进行预测,PHT换成Target Cache即可。每次执行这个指令就将目标地址写入Target Cache。
      在这里插入图片描述

5. 分支预测失败时的恢复

   分支预测时一种预测技术,相应的需要有验证和恢复机制。预测错误发现越早,恢复越早,则造成的惩罚越小。
   在执行阶段不管什么类型的分支指令都可以被计算出结果,可以检查分支预测的结果。发现预测失败时,指令之后的所有指令都要被抹掉。但是在乱序执行的处理器中,可能在流水线中有一些指令是在这条分支指令之前进入的,不应该被抹除,因此要辨别哪些指令需要别抹除。

  • 指令本来的顺序在重排序缓存(ROB)里面进行了记录,可以借此恢复流水线。将预测失败的指令信息记录在ROB对应的表项中,暂停取指,使流水线继续执行,当这条指令成为流水线中最旧的指令时,冲刷流水线,这样可以保证所冲刷的指令都是处在错误路径上的。这种方法的弊端在于等待时间长,尤其是在分支指令之前存在D-Cache缺失的load指令,降低了处理器的性能,优点是实现简单。
    在这里插入图片描述
  • 另外一种方法是Checkpoint,即在发现分支指令且在分支指令之后的指令更改处理器的状态之前将处理器的状态保存起来(保存现场),主要是寄存器重命名中使用的映射表,以及预测跳转指令的下一条PC等。Checkpoint需要消耗更多的硬件资源,但这种方法能更快速地将处理器的状态恢复。关于如何抹掉错误路径上的指令的问题,首先需要知道哪些指令处在错误路径上,通过对分支指令编号实现,即这条分支指令之后进入的指令都具有与之相同的编号,直到遇到下一条分支指令,编号保存在一个FIFO中,成为编号列表(tag list)。
    在这里插入图片描述
    通过编号可以方便识别指令的顺序,发现预测失败时可以马上对流水线的指令进行有选择的抹掉,而不必等它成为流水线中最旧的指令。当执行阶段发现预测错误的分支指令,就需要抹掉之后进入的所有指令,包括两部分:
    • 在流水线发射阶段之前的所有指令,因为发射阶段之前仍然是顺序的,这些指令必定在错误路径上,一个周期即可全部抹掉;
    • 在流水线发射阶段及以后的指令,指令时乱序的,需要根据编号选择错误路径上的指令进行抹掉。

参考资料:
《超标量处理器设计》——姚永斌
《手把手教你RISC-V CPU处理器设计(上)》

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
分支预测是计算机中的一种技术,用于提高程序的执行效率。其中,BTB(Branch Target Buffer)和BHT(Branch History Table)是两种常见的分支预测方法。 BTB是一种缓冲区,用于存储分支指令的预测目标地址。在程序中,分支指令会根据特定的条件来选择跳转到不同的代码块。BTB通过记录过去的分支指令及其目标地址,以便在将来遇到相同的分支指令时可以快速预测其目标地址。这样,在预测正确的情况下,可以避免浪费时间等待分支判断的结果,并提前开始执行预测目标地址处的指令流。但是,当预测错误时,需要回退到正确的指令位置,这样会带来额外的开销。 BHT是一种记录分支历史的表格。它用来跟踪分支指令的历史模式,以便在将来遇到相同的分支预测其行为。BHT可以记录分支指令的条件判断结果,比如“是否进入循环”,然后基于历史记录来预测下一次分支指令的结果。如果历史模式具有一定的规律性,那么BHT能够准确地预测分支的行为,提高程序的性能。但是,当历史模式改变时,预测就会出错,需要进行修正。 综上所述,BTB和BHT是分支预测的两种常见方法。BTB通过存储过去的分支目标地址来预测分支指令的执行位置,而BHT通过记录历史模式来预测分支指令的行为。这些预测技术能够显著提高程序的执行效率,但也有一定的错误率,并且需要额外的硬件支持来实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值