算法是基础,有了算法后,就可以在处理器中实现分支预测功能。Intel的分支预测模块包含了3个单元:
1. Branch Target Buffer(BTB)
2. The Static Predictor
3. Return Stack
基本的BTB结构如下:
BTB(Branch Target Buffer)
分支指令在执行后,会将这条指令的地址以及它的跳转信息记录在BTB中。BTB buffer不会太大,不能将所有的分支指令都存进去,通常采用Hash表的方式存入。在取指时,先将PC(程序指针)和BTB中的分支指令的地址进行比较,如果找到了,说明这条指令是分支指令,并且在BTB中有记录,就使用BTB预测出来的跳转地址。如果没有记录,就不能使用BTB的信息了,取指下一条指令。
Intel的Branch Target Buffer还包含了历史跳转信息,用于预测分支指令是否发生跳转。
The Static Predictor
当分支指令在BTB中记录了历史信息才能使用BTB进行预测,当分支在BTB中找不到记录时,可以使用The Static Predictor(静态预测器)。人们将分支指令的执行情况做了大量的统计,从中总结出一些特征,并将这些特征总结为一些固定的策略,这就是静态预测器。
当指令被解码后,它是不是分支指令,以及要跳转的地方就知道了,只是不知道是否该跳。一般来说,向上的跳转,常用来组织成循环,这个跳转应该被预测为执行。
向上跳,组成循环
静态预测器通常的策略是:向下跳转预测为不跳转,向上跳转预测为跳转。
Return Stack
函数调用在程序中大量出现,函数调用与返回也都是通过跳转来实现的。例如,有3个函数调用了printf函数,printf函数地址固定,调用时知道地方,但是在返回时,并不知道该返回到哪个地方,Return Stack(返回栈)可以用于解决这个问题。在函数调用时,将函数的返回地址压栈到Return Stack中,当遇到函数返回指令时,就从Return Stack中取出地址。
3个函数都调度printf函数
分支预测会消耗大量的资源,很多低功耗的处理器没有分支预测,如TI的DSP,它采用指令的条件执行来减少跳转指令。例如:
if ( flag ) { a = b; } else { a = c; } |
用DSP的汇编表示为:
[B0] ADD .L2 B8, 0, B6 [!B0] ADD .L1 A8, 0, B6 |
第1条语句在B0非0时执行,将b赋给 a,当B0为0时,这条语句为空,不执行。
第2条语句在B0为0时执行,将c赋给 a,当B0为非0时,这条语句为空,不执行。
在TI DSP中,只有几个寄存器能作为条件寄存器,对于短小的if、else语句,条件执行已经足够,对于复杂的if、else判断,条件执行就无能为力了,DSP并不擅长处理复杂的控制代码,这些还得需要分支预测。