分支预测的地址跳转

分支指令的目标地址(targetaddress)可以分为两种,即直接跳转(PCGrelative)和间接跳转(absolute)

  • 直接跳转:
    • 偏移值(offset)是以立即数的形式固定在指令当中,所以它的目标地址也是固定的,只要记录下这条分支指令的目标地址就可以了;
    • 当再次遇到这条分支指令时,如果方向预测的结果是发生跳转,那么它的目标地址就可以使用以前记录下的那个值;
  • 间接跳转:
    • 而对于间接跳转的分支指令来说,由于它的目标地址来自于通用寄存器,而通用寄存器的值是会经常变化的,所以对这种类型的分支指令来说,进行目标地址的预测并不是一件很容易的事情;
    • 但是庆幸的是,程序中大部分间接跳转类型的分支指令是用来处理子程序调用的CALL和Return指令,而这两种指令的目标地址是有规律可循的,因此可以对其进行预测。
    • 大部分超标量处理器都会推荐编译器的设计者,除了使用CALL/Return指令之外,尽量减少使用其他间接跳转类型的分支指令,而多使用直接跳转类型的分支指令,这样有助于处理器提高分支预测的准确度,从而提高处理器的性能。

直接跳转的分支预测

  • 分支预测器(branch predictor)除了需要对分支指令的方向进行预测之外,还需要对目标地址也进行预测。
  • 对于RISC指令集来说,由于计算地址所需要的偏移值(offset)是在指令中以立即数的形式进行的携带,所以这个偏移值是不会发生变化的,这种分支指令的目标地址进行预测是很容易的
  • 只需要使用一个表格,记录下每条直接跳转类型的分支指令对应的目标地址,当再次对这条分支指令进行预测时,只需要查找这个表格就可以得到预测的目标地址了。
  • 由于分支预测是基于PC 值进行的,不可能对每一个PC值都记录下它的目标地址,所以一般都使用Cache的形式,使多个PC值共用一个空间来存储目标地址,这个Cache称为BTB(Branch Target Buffer)        

  • 使用PC值的一部分来寻址BTB(这部分称为index), PC值的其他部分作为Tag. 
  • BTB中存放着分支指令的目标地址(Branch Target Address,BTA)
  • 因为Index部分相同的多个PC值会查找到BTB中的同一个地方,所以使用Tag来进行区分;
  • 当这些PC值中存在多于一条的分支指令时,就产生了冲突,这样会造成BTB中对应的内容被频繁地替换,影响了分支预测的准确度。
  • 可以采用组相联的cache结构,防止被频繁替换;
  • 优化手段:核心都是减少tag的位宽,使得占用的面积减少,响应速度增加;
    • partial-tag BTB, 只使用PC的某一部分进行BTA查找;
    • 将PC中的tag域段,进行hash运算,生成新低位宽的tag;
  • 这种BTB的方式,适合直接跳转指令;对于间接跳转,因为它的目标地址是经常变化的,所以使用BTB对目标地址进行预测,是无法得到满意结果的,需要采用一些其他的方式来解决。
  • BTB的缺失

例如,多条指令的set相同,way已经用完了,此时会miss;

当miss时,有两种方式可以处理:

方式一:停止执行

        当一条分支指令预测会发生跳转,但是在BTB中没有找到对应的目标地址时,可以暂停流水线的取指令直到这条分支指令的目标地址被计算出来为止;

        不同类型的分支指令导致流水线停止的周期数是不同的,对于直接跳转类型的分支指令,在流水线的解码阶段就可以从指令中分离出偏移值(offset),此时就可以将这条分支指令的目标地址计算出来(PC+offset)。

    

解码阶段得到offset, 这种方式会有气泡产生;

整个流水线的过程如下图所示:

  • 直接跳转(PC-relative)类型的分支指令是比较有效率的,因为这种类型的分支指令的目标地址可以很快地计算出来;
  • 但是对于间接跳转(absolute)类型的分支指令,它的目标地址需要等到通用寄存器的值被计算出来,这个过程的时间可能会很长:
    • 例如这个通用寄存器来自于发生D-Cache缺失的load指令),这会导致流水线中产生大量的气泡降低了处理器的执行效率;

 方式二:继续执行

  • 当在流水线的后续阶段,将它的目标地址计算出来之后,如果发现计算出的地址和原来顺序的PC值不一样(这个可能性是非常高的),那么就将流水线中分支指令之后进入到流水线的所有指令都抹掉,使用计算出的目标地址开始取指令.
  • 对于间接跳转(absolute)类型的分支指令,要等到它的目标地址被真正地计算出来,需要的时间可能会很长,顺序执行总比暂停流水线要更“聪明”一些,因为毕竟存在正确的可能性
  • “better later than never";

call/return类型的分支预测

  • 在一般的程序中,CALL 指令用来调用子程序,使流水线从子程序中开始取指令执行
  • 而在子程序中,Return指令一般是最后一条指令,它将使流水线从子程序中退出,返回到主程序中的CALL指令之后,继续执行。
  • 对于很多RISC处理器来说,可能在指令集中并没有直接的 CALL/Return 指令,而是使用其他的指令来模拟这个功能,例如在 MIPS处理器中,使用JAL指令作为CALL指令,而使用"JR $31"指令作为Return指令。
  • 对于程序中的一条指定的CALL 指令来说,它每次调用的子程序都是固定的,
  • 也就是说,一条CALL指令对应的目标地址是固定的,因此可以使用BTB对CALL指令的目标地址进行预测

Return指令的目标地址总是等于最近一次执行的指令的下一条指令的地址

  • 根据Return指令的上述特点,可以设计一个存储器,保存最近执行的CALL指令的下一条指令的地址
  •  这个存储器是后进先出的(Last In First Out,LIFO),即最后一次进入的数据将最先被使用,这符合上面讲述的Return指令和CALL指令的特点
  • 这个存储器的工作原理和堆栈(stack)是一样的,称之为返回地址堆栈(Return Address Stack,RAS),

对于图 4.39 所示的例子,当执行完三条CALL 指令之后,RAS 如图 4.40 所示。

        因此综合看起来,可以使用BTB对CALL指令的目标地址进行预测,而使用RAS对指令的目标地址进行预测;

 

RAS想要正常工作,需要满足如下两个前提条件:

1. 遇到call指令的时候,需要能够将call指令的下一条指令的地址,放到RAS中;这就需要识别出哪条指令是CALL指令;

  • 正常情况下,只有到了decode阶段,才能够知道当前指令,是否是call指令,此时才能将PC+4放到RAS中;
  • 但是从I-Cache中取指令,需要几个周期,当这个CALL指令进入decode阶段时,指令后面已经有很多其他的指令,也进入了流水线,如果这些指令中有return, 因为前面的call指令还没有进行decode, 那么return将无法获得RAS中的目标地址;(多个decode单元?)

解决方式:期望在分支预测阶段,通过PC值,就可以知道是否是CALL指令,则保存PC+4到RAS;

  • 利用BTB, BTB中保存了所有发生跳转的分支指令,CALL/RETURN都保存在其中;
  • 在BTB中增加一项,用来记录分支指令的类型;
  • 当一条分支指令被写道BTB时,也会记录该指令的类型,当下次再遇到这个分支指令(PC值),查询其类型,就可以在分支预测阶段,识别出CALL, 从而保存PC+4到RAS;

2. 对return指令进行目标地址预测时,需要能够选择RAS的输出作为目标地址的值,而不是BTB的输出值,因此,我们需要知道指令的类型;

  • 可以在BTB中,查询到是return指令时,获取到当前是RETURN命令,从而从RAS中获取地址;

    

可能存在的问题:如果一个程序嵌套太深,可能会把RAS撑满;那这个时候怎么处理?

  • 丢掉最旧的那个跳转地址,更好一些,因为,如果是自己调用自己的函数,这样存在正确的可能性;

  • 不过,像图 4.47中这种递归的函数调用,RAS都被同一个CALL 指令的返回地址所占据了,RAS中保存的都是同一个重复的值,这样对RAS空间来说,其实是浪费的。
  • 对于连续执行的同一个 CALL 指令来说(此处的连续是指,两次相邻执行的CALL指令是同一条指令),完全可以将它们的返回地址都放到RAS中的同一个地方,并用一个计数器来标记CALL指令执行的次数,例如使用一个8位的计数器就可以最多标记256级的递归调用了,这样相当于扩展了 RAS 的容量,增大了预测的准确度。

非call/return类型的分支预测

  • 如果不是call/return指令,理论上,因为其跳转地址在寄存器中,所以对于32位系统,可能有2**30种可能性(一次指令4B);
  • 在实际的程序中,对于一个间接跳转(absolute)类型的分支指令来说,它的目标地址只有固定的几个,因此可以根据分支指令在过去的执行情况,对目标地址进行预测

    

对于间接跳转类型的分支指令来说,它的目标地址也可能是和过去的执行情况有关系的,因此可以利用基于局部历史的分支预测方法中使用的BHR对目标地址进行预测(将PHT换成Target Cache);

总结

因此,到目前为止,对于分支指令的目标地址的预测,有下面的三种方法。这些方式都是在取指阶段,利用pc值来进行;
(1)使用 BTB 对直接跳转(PC-relative)类型的分支指令和 CALL 指令进行预测;
(2)使用RAS对Return指令进行预测;
(3)使用 Target Cache 对其他类型的分支指令进行预测。
尤其是对于BTB和RAS, 几乎是现代的超标量处理器必须要使用的。 

        图4.49中使用了基于局部历史的方法对分支指令的方向进行预测,配合使用BTB、RAS 和 Target Cache 对分支指令的目标地址进行预测,这种将分支指令的方向预测独立于BTB的做法称为decoupled BTB;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值