您的位置:首页 > 其它

体系结构学习-3-基本分支预测方法

qq_40891899 2020-05-11 04:13 97 查看 https://blog.csdn.net/qq_40891

静态分支预测(Static Branch Prediction):

  静态分支预测的基本原理就是“Static”:遇到跳转指令后是否跳转由标志位 valid 是否为1来决定,采用静态分支预测的方法就是在代码运行前就决定好这个分支的 valid=1(始终跳转) or valid = 0 (始终不跳转)

  • 性能评估: 按之前文章-1-中提到过的 数据进行估算

程序中的分支指令大约占据20%,其中大约有70%会执行跳转

Accuracy(valid=1)≈70%CPI=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
Accuracy(valid=0)≈30%CPI=1+20%∗70%=1.14 Accuracy(valid = 0) \approx 30\% \quad CPI = 1+20\%*70\% = 1.14Accuracy(valid=0)≈30%CPI=1+20%∗70%=1.14

  • 缺陷:静态分支预测的准确率不够高(从上面准确率的估算值可以粗略看出)
  • 解决方案:从静态分支预测的缺陷入手,我们可以反思有没有提高预测准确率的方法呢?如果我们能够在遇见需要跳转的分支指令就把valid 置 1 ,不需要跳转的分支指令就把valid置0,这样仿佛就能够做到 100%准确率的预测了!——没错,我们确实这样尝试了。

  编译器在对代码优化的时候,便可以设置分支处的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 Branch Prediction)

  动态分支预测的灵魂在于“Dynamic”:依靠程序运行时的信息来进行协助分支预测。

  • 相对于静态分支预测的优势:减少了对编译器以及运行环境的依赖(如不需要编译器去做静态分析了),能够自我调节预测力度(就是不同于静态分析的固定预测方向,可以动态调整)。
  • 缺点:动态分支预测主要是基于硬件实现的,必然增加了硬件资源的消耗。

  还记得文章-2-提到的BTB吗?其中决定跳转与否的state位便是由动态分支预测实现的,不同的state位数其实也对应了不同的动态分支预测方法。

1. sate宽度为1-bit :
  当state位为1时,我们可以简单地根据该指令的上次跳转情况来设置其值,上次跳转则取值为1,本次也预测会进行跳转。

2状态转换图:

跳转不跳转跳转不跳转10

  对于一般的LOOP语句,本次循环的最后一次(因为执行完成后需要跳出循环)和下次循环的第一次(之前跳出循环后state位置0了,此时循环还没有完成)将会预测错误,记循环次数为n,准确率估算为:
Acc=n−2n Acc = \frac{n - 2 }{n}Acc=nn−2​

  显然可以看出,准确率与循环大小密切相关。对于一般的LOOP语句,本次循环的最后一次(因为执行完成后需要跳出循环)和下次循环的第一次(之前跳出循环后state位置0了,此时循环还没有完成)将会预测错误,记循环次数为n,准确率估算为:

Acc=n−2n Acc = \frac{n - 2 }{n}Acc=nn−2​

  显然可以看出,准确率与循环大小密切相关。

优点:state位的状态转换实现简单,而且对某些代码结构比较实用(循环次数多):比如循环100次,如果初始state = 1,那么只有最后一次预测错误,准确率高达99%! CPI的估算值基本接近理想状况了。
CPI=1+20%∗1%=1.002 CPI = 1 + 20\%*1\% = 1.002CPI=1+20%∗1%=1.002
缺点: 对于某些代码结构(循环次数少):比如每跳转执行和顺序执行交叉,那么预测准确率将会糟糕到接近于0,效果将不如静态分支预测
CPI=1+20%∗100%=1.2 CPI = 1 + 20\%*100\% = 1.2CPI=1+20%∗100%=1.2

  那么根据state位长为1的缺点,可以发现如果循环跳出的时候预测错误如果不改变state的值,那么下次进入循环的时候就不会预测失败了,于是我们尝试增加state的位长,调整state的状态转换方法。

2. sate宽度为2-bit :
  我们可以简单的用"00,01,10,11"四个状态来进行预测。

4状态转换图:

跳转跳转不跳转不跳转跳转不跳转不跳转跳转10110100

  10和11状态表示跳转,01和00表示不跳转:相对于2状态转换,在发生预测错误时,state位的变化更缓慢,只有连续发生预测错误时才会改变预测方向。

  正如之前说的,2位state的性能也不一定优于1位state,正确率和实际运行的环境相关。(统计数据显示2位state准确率大概80%-90%)

分支预测准确率99%足够了吗?

  现代处理的流水级数远远多于理想的MIPS的五级流水线(比如Intel Pentium4 就20级流水了),而且指令也更复杂,更有多发射存在。就算是20级单发射流水线有99%的准确率,100条指令错误1条也会损失20个周期去执行错误的指令,损失的周期数所占百分比为:
20/100=20% 20 /100 = 20\%20/100=20%
  那么如何去进一步提高准确率呢?得到更多的信息。

  • 收集程序运行的跳转信息:基于程序运行的上下文往往是相关的
  • 收集目标跳转指令更多的历史信息:构建更多位数的state,使用更精确(往往更复杂)的状态转移函数
淡定路过的我 原创文章 19获赞 1访问量 696 关注 私信
标签: