zoukankan      html  css  js  c++  java
  • CPU 分支预测

       去年在安宁庄的时候, 有个同事阐述了一个观点:php中的if else  在执行时考虑到效率的原因,不会按我们的代码的顺序一条一条去试,而是随机找出一个分支,执行,如果不对,再随机找到一个分支

      当时由于种种原因,也没过多去想这个问题,最近查了下资料,发现里面的学问还挺大的

       php解释器是由c编写的,是个经编译生成的二进制文件, 我们编写的PHP代码相当于这个C程序的参数,只不过这个参数是个一个的文件, 这个C程序要解析这个php文件,产生相应的opcode,再去执行opcode对应的函数,每一部操作都是由C函数来实现

         查询opcode含义的利器: http://www.laruence.com/2008/11/20/640.html#ZEND_JMP_.28Opcode_42.29  

    <?php
    if($a == 1){
        echo "a is 1";
    }else if($a == 2){
        echo "a is 2";
    }else{
        echo "a is x";
    }

     对于上面的php代码来说,最终执行的opcode是

    -------------------------------------------------------------------------------------
       2     0  E >   IS_EQUAL                                         ~0      !0, 1
             1      > JMPZ                                                     ~0, ->4
       3     2    >   ECHO                                                     'a+is+1'
       4     3      > JMP                                                      ->9
             4    >   IS_EQUAL                                         ~1      !0, 2
             5      > JMPZ                                                     ~1, ->8
       5     6    >   ECHO                                                     'a+is+2'
       6     7      > JMP                                                      ->9
       7     8    >   ECHO                                                     'a+is+x'
       9     9    > > RETURN                                                   1

    可以看到在执行php时,  是一条一条去执行的

    1.先判断  $a  是否 等于 1

    2.如果不等于1,为false, 就JMPZ 到第4条命令,去比较  $a 是否 等于2

       如果等于1, echo "a is 1"; 然后 无条件跳转 JMP 第9行 return 了

    3.如果 $a 不等于2 ,即为false, 就JMPZ 到第8条命令, echo "a is x" 

       如果  $a 等于2,直接echo "a is 2", 然后执行 JMP 第9行 return 了

    所以,php编写的程序,对C函数来说,还是要一步的一步去执行的,关于具体php的分支实现,请点击这里

      如果这个文件被执行100次,有90次 $a=3, 那么解释器每次都要判断 $a 是否等于1和  $a 是否等于2, 尽管第三个分支是满足条件的,如果是C编写的程序, CPU会针对某种策略挑选一个分支来执行, 对应上面的分支来说,CPU会直接取出第三个分支的指令,然后执行。

         从486开始,CPU开始具备流水线这个特性,指令流水线由5,6个不同功能的工作单元组成,将一个x86指令也拆分成5,6个步骤,分别送往不同的工作单元,来达到同时执行多个指令的目的,现在的CPU支持30级的流水线,也就意味着流水线上有30个工作单元,对应的X86指令也拆分成30个步骤。

    注:CPU执行的是二进制数据,代码经过汇编编译后,生成一条条二进制指令

       例如 int a=1; 对应的汇编是mov $1, %eax; 对应的机器码可能是00011100011

    在执行文件时,根据局部性原理,想关的指令都要加载到CPU缓存中,

    一般一条指令的完成 分四个步骤:

    1.取指令

    2.翻译指令 (看是赋值,还是计算,从内存什么地方取数据)

    3.执行指令

    4.写指令结果 (要么写回内存,要么写到寄存器)

             取指令  翻译指令  执行指令  写指令结果

                        命令1      命令1

                        命令2

    取指令单元取出指令1后,翻译指令单元开始 翻译指令1时,取指令单元可 取出指令2了

    如果CPU不这么做,等到指令1完成上面四个步骤后,指令2才开始进行,那效率太低了

    流水化中的单元分的更详细, 更多的指令可以并行处理,但速度不见得快,因为有分支的出现,如果没有命中第一个分支,后面的指令将作废, 需要清空后面所有的指令, 然后中载命中地址的指令,再运行

          在有5个分支的情况下,若采取随机挑选一个分支 执行的话,每次赌该分支命中的概率只有五分之一, 于是CPU分支预测功能就出现了。

          分支预测分静态和动态

          静态分支预测:由编译器决定哪个分支可能被CPU命中,一般是第一个分支,即 if 后面的逻辑,而不是后面else的逻辑

          动态分支预测:在CPU硬件中开辟一块缓存,专门记录每个分支最近几次的命中情况,然后做出预测,显然这种方法能及时调整策略,有更好的远詹性,但CPU压力会大些,不过还好。

      

      分支地址只有在流水线指令执行阶段才能计算出来,为了避免等待,需要在译码阶段进行预测

    Two-Level分支预测方法使用了两种数据结构,一种是BHR(Branch History Register);而另一种是PHT(Pattern History Table)。其中BHR由k位组成(可理解为记录K次某个分支的执行结果),用来记录每一条转移指令的历史状态,而PHT表含有2kEntry组成,而每一个Entry由两位Saturating Counter组成。BHR和PHT的关系如图3‑10所示。

    3.4 <wbr>预读机制

    假设分支预测单元在使用Two-Level分支预测方法时,设置了一个PBHT表(Per-address Branch History Table)存放不同指令所对应的BHR。在PBHT表中所有BHR的初始值为全1,而在PHT表中所有Entry的初始值值为0b11。BHR在PBHT表中的使用方法与替换机制与Cache类似。

    当分支预测单元分析预测转移指令B的执行时,将首先从PBHT中获得与转移指令B对应的BHR,此时BHR为全1,因此CPU将从PHT的第11…11个Entry中获得预测结果0b11,即Strongly Taken。转移指令B执行完毕后,将实际执行结果Rc更新到BHR寄存器中,并同时更新PHT中对应的Entry。

    CPU再次预测转移指令B的执行时,仍将根据BHR索引PHT表,并从对应Entry中获得预测结果。而当指令B再次执行完毕后,将继续更新BHR和PHT表中对应的Entry。当转移指令的执行结果具有某种规律(Pattern)时,使用这种方法可以有效提高预测精度。如果转移指令B的实际执行结果为001001001….001,而且k等于4时,CPU将以0010-0100-1001这样的循环访问BHR,因此CPU将分别从PHT表中的第0010、0100和1001个Entry中获得准确的预测结果。

    由以上描述可以发现,Two-Level分支预测法具有学习功能,并可以根据转移指令的历史记录产生的模式,在PHT表中查找预测结果。该算法由T.Y. Yeh and Y.N. Patt在1991年提出,并在高性能处理器中得到了大规模应用。

    Two-Level分支预测法具有许多变种。目前x86处理器主要使用“Local Branch Prediction”和“Global Branch Prediction”两种算法。

    在“Local Branch Prediction”算法中,每一个BHR使用不同的PHT表,Pentium II和Pentium III处理器使用这种算法。该算法的主要问题是当PBHT表的Entry数目增加时,PHT表将以指数速度增长,而且不能利用其它转移指令的历史信息进行分支预测。而在“Global Branch Prediction”算法中,所有BHR共享PHT表,Pentium M、Pentium Core和Core 2处理器使用这种算法。

    在高性能处理器中,分支预测单元对一些特殊的分支指令如“Loop”和“Indirect跳转指令”设置了“Loop Prediction”和“Indirect Prediction”部件优化这两种分支指令的预测。此外分支预测单元,还设置了RSB(Return Stack Buffer),当CPU调用一个函数时,RSB将记录该函数的返回地址,当函数返回时,将从RSB中获得返回地址,而不必从堆栈中获得返回地址,从而提高了函数返回的效率。

    目前在高性能处理器中,动态分支预测的主要实现机制是CPU通过学习以往历史信息,并进行预测,因而Neural branch predictors机制被引入,并取得了较为理想的效果,本节对这种分支预测技术不做进一步说明。目前指令的动态分支预测技术较为成熟,在高性能计算机中,分支预测的成功概率在95%~98%之间,而且很难进一步提高。

     参考:http://blog.sina.com.cn/s/blog_6472c4cc0100qxd2.html

    http://tonysuo.blogspot.hk/2013/12/computer-architecture-5.html

    http://blog.hesey.net/2013/03/branch-prediction-in-pipeline.html

     http://wenku.baidu.com/view/48833667ddccda38376bafa2.html

    http://blog.sina.com.cn/s/blog_5a82024e0100e5lm.html

    //大话处理器

    http://blog.csdn.net/muxiqingyang/article/details/6677425

    http://cyukang.com/2012/07/11/branch_prediction.html 

    http://blog.csdn.net/wahaha_nescafe/article/details/8500094

    https://www.zhihu.com/question/23973128

    http://blog.sina.com.cn/s/blog_6556314c0100hamf.html

    http://blog.sina.com.cn/s/blog_6556314c0100hamt.html

    http://blog.sina.com.cn/s/blog_6556314c0100hamj.html

    http://blog.sina.com.cn/s/blog_6556314c0100hamh.html

    https://www.zhihu.com/question/23973128

  • 相关阅读:
    python爬虫
    RMQ算法
    组合数
    水池数目
    jQuery 拼接事件
    ORACLE
    day 75
    day74 vue框架
    day73 vue框架
    day 72 vue框架
  • 原文地址:https://www.cnblogs.com/taek/p/6060373.html
Copyright © 2011-2022 走看看