静态分支预测的基本原理就是“Static”:遇到跳转指令后是否跳转由标志位 valid 是否为1来决定,采用静态分支预测的方法就是在代码运行前就决定好这个分支的 valid=1(始终跳转) or valid = 0 (始终不跳转)
程序中的分支指令大约占据20%,其中大约有70%会执行跳转
A c c u r a c y ( v a l i d = 1 ) ≈ 70 % C P I = 1 + 20 % ∗ 30 % = 1.06 Accuracy(valid = 1) \approx 70\% \quad CPI = 1+20\%*30\% =1.06 Accuracy(valid=1)≈70%CPI=1+20%∗30%=1.06
A c c u r a c y ( v a l i d = 0 ) ≈ 30 % C P I = 1 + 20 % ∗ 70 % = 1.14 Accuracy(valid = 0) \approx 30\% \quad CPI = 1+20\%*70\% = 1.14 Accuracy(valid=0)≈30%CPI=1+20%∗70%=1.14
编译器在对代码优化的时候,便可以设置分支处的valid的位(编译器使用Profile工具提前分析你的代码,预先判断分支的跳转情况),下面列举了一个简单的例子:
...
const = 1;
if( const == 1){...}
else {...}
..
Tips: 编译器的工作流程大致如下:
词法分析->语法分析->语义分析->生成机器无关代码->生成机器相关代码
在语法分析阶段,我们能够知道if
语句出现了,可以提前做一些准备(比如记下语句的位置之类的),然后语义分析阶段,我们可以知道代码有一个常量const = 1
,而且它是作为之前if
语句的判断条件,这个时候编译器知道了const = 1
而且if
的判断条件为const == 1
,那么这个判断条件就可以确定为真,于是在进行机器无关代码优化的时候将条件执行语句优化为了顺序语句。
那如果const不是一个常量呢?比如是一个需要从键盘输入值才能确定的变量。那么显然上述编译器的静态处理方法无法实现了。而且我们也不能保证编译器的分析一定正确(比如const之后被修改为2,但是编译器仍然认为其值为1)。
于是我们可以选择去增强编译器的分析能力,像运用启发式算法(Heuristic),不需要提前运行程序(Profile通常会随着优化程度而增加编译时间和计算资源的需求)。
我们也可以在代码编写就做出相应准备,比如asser之类的。
但是上面的方法都有缺陷,比如需要编译器、ISA以及程序员的相互配合相互支持。
那么有没有不需要那么多支持条件也能准确预测的方法呢?
动态分支预测的灵魂在于“Dynamic”:依靠程序运行时的信息来进行协助分支预测。
还记得文章-2-提到的BTB吗?其中决定跳转与否的state位便是由动态分支预测实现的,不同的state位数其实也对应了不同的动态分支预测方法。
1. sate宽度为1-bit :
当state位为1时,我们可以简单地根据该指令的上次跳转情况来设置其值,上次跳转则取值为1,本次也预测会进行跳转。
2状态转换图:
对于一般的LOOP语句,本次循环的最后一次(因为执行完成后需要跳出循环)和下次循环的第一次(之前跳出循环后state位置0了,此时循环还没有完成)将会预测错误,记循环次数为n,准确率估算为:
A c c = n − 2 n Acc = \frac{n - 2 }{n} Acc=nn−2
显然可以看出,准确率与循环大小密切相关。对于一般的LOOP语句,本次循环的最后一次(因为执行完成后需要跳出循环)和下次循环的第一次(之前跳出循环后state位置0了,此时循环还没有完成)将会预测错误,记循环次数为n,准确率估算为:
A c c = n − 2 n Acc = \frac{n - 2 }{n} Acc=nn−2
显然可以看出,准确率与循环大小密切相关。
优点:state位的状态转换实现简单,而且对某些代码结构比较实用(循环次数多):比如循环100次,如果初始state = 1,那么只有最后一次预测错误,准确率高达99%! CPI的估算值基本接近理想状况了。
C P I = 1 + 20 % ∗ 1 % = 1.002 CPI = 1 + 20\%*1\% = 1.002 CPI=1+20%∗1%=1.002
缺点: 对于某些代码结构(循环次数少):比如每跳转执行和顺序执行交叉,那么预测准确率将会糟糕到接近于0,效果将不如静态分支预测
C P I = 1 + 20 % ∗ 100 % = 1.2 CPI = 1 + 20\%*100\% = 1.2 CPI=1+20%∗100%=1.2
那么根据state位长为1的缺点,可以发现如果循环跳出的时候预测错误如果不改变state的值,那么下次进入循环的时候就不会预测失败了,于是我们尝试增加state的位长,调整state的状态转换方法。
2. sate宽度为2-bit :
我们可以简单的用"00,01,10,11"四个状态来进行预测。
4状态转换图:
10和11状态表示跳转,01和00表示不跳转:相对于2状态转换,在发生预测错误时,state位的变化更缓慢,只有连续发生预测错误时才会改变预测方向。
正如之前说的,2位state的性能也不一定优于1位state,正确率和实际运行的环境相关。(统计数据显示2位state准确率大概80%-90%)
现代处理的流水级数远远多于理想的MIPS的五级流水线(比如Intel Pentium4 就20级流水了),而且指令也更复杂,更有多发射存在。就算是20级单发射流水线有99%的准确率,100条指令错误1条也会损失20个周期去执行错误的指令,损失的周期数所占百分比为:
20 / 100 = 20 % 20 /100 = 20\% 20/100=20%
那么如何去进一步提高准确率呢?得到更多的信息。