去年在安寧莊的時候, 有個同事闡述了一個觀點:php中的if else 在執行時考慮到效率的緣由,不會按咱們的代碼的順序一條一條去試,而是隨機找出一個分支,執行,若是不對,再隨機找到一個分支php
當時因爲種種緣由,也沒過多去想這個問題,最近查了下資料,發現裏面的學問還挺大的html
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 是否 等於 1oop
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表含有2k個Entry組成,而每個Entry由兩位Saturating Counter組成。BHR和PHT的關係如圖3‑10所示。
假設分支預測單元在使用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表中的第00十、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