關於機器指令和微指令

最近在看《深入理解計算機系統》,真的很不錯,讓我對CPU又有了更深的瞭解。


我們都知道,實際上我們用高級語言編寫的程序,被編譯成可執行程序,存放可執行程序的文件實際就是一些機器碼,可以被硬件執行。在這一步,我們稱其爲機器指令(Machine Instruction),而到了這一步,往往也就以爲自己到達了所謂的“底層”。


前一段時間,我還在論壇提過一個疑問,就是Intel編譯器編譯出的代碼是針對Intel處理器優化的,在AMD處理器上執行效率一般,爲什麼會出現這個情況?因爲機器指令序列都是一樣的,每個機器指令按照硬件廠商給的參數,週期數(clock cycle)應該也差別不大啊。


現在我想,我可以嘗試來解答這個問題了。在我們看來,每一步機器指令都是一個原子操作,但是硬件爲了追求更高的吞吐率(through output),將這些指令又細分成一個個獨立的階段,稱之爲微指令(micro-operation,μ-op),這些微指令可以在一個流水線(pipeline)的不同階段中順序執行執行,當流水線頭部空了,就可以執行下一個機器指令的微指令。這樣“同時”執行的機器指令就很多,從而增加了吞吐率。


當然一味增加流水線的階段也是不行的,因爲從一個階段到另一階段有時間延遲(latency),並且這些階段公用時鐘信號,所以要用執行時間最長的階段所需時間作爲公共時鐘,並且很多操作無法切割成太多的微指令。下圖是奔4處理器的流水線示意圖:




所以,我之前的概念,mov reg reg這樣的操作只需要耗費1個clock,這是錯誤的,正確的說法是這個操作的均攤時間是1個clock,它必須走完流水線所有的步驟,而流水線的每個步驟至少是1個clock的。一條機器指令的執行,包括取值、譯碼、計算、訪存、更新PC等很多步驟,而微指令可以看成是譯碼以後的結果,它還可以緩存在buffer上,從而下次遇到相同的指令,減少譯碼時間。


還有一個問題就是相鄰的機器指令之間如果有依賴,上一個指令還沒在流水線出來,下一個就進去了,這樣下一個可能用了錯誤的值,這個問題可以用轉發(bypass)上個流水線的中間值給下個流水線,或暫停(stall)來解決,但是stall是不合理的,因爲這樣使得流水線的一部分處於停滯狀態,而還有一個方法,就是亂序執行(out of order),將後面沒有依賴關係的指令提到前面來,儘量使流水線填充滿。


所以,可以想象,Intel和AMD的微指令層面,流水線肯定是非常不一樣的。那麼如何安排機器指令順序(當然執行時CPU會判斷能否亂序,但是這時已經是後期,如果編譯器能更多的去除依賴當然就更好了),以及如何利用寄存器(有時同樣的功能也可以用不同的機器碼來實現)等等。所以Intel的編譯器能夠針對自家的CPU給出更高效的編譯結果。


例如:AMD文檔給編譯器開發者的一條建議是,當跳轉後面緊跟ret指令時,最好插入一個rep,從而在不引入錯誤的前提下,提高CPU效率。

......
je  .L33
rep
ret


擴展閱讀:

CPU流水線的探祕之旅 (這是翻譯後的版本,英文原版地址:A Journey Through the CPU Pipeline



參考文檔

《深入理解計算機系統》

http://en.wikipedia.org/wiki/Pipeline_(computing)

http://en.wikipedia.org/wiki/Micro-operation




發佈了99 篇原創文章 · 獲贊 98 · 訪問量 36萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章