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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM