探秘X86架構(gòu)CPU流水線(xiàn)
英文原文:A Journey Through the CPU Pipeline
本文引用地址:http://www.ex-cimer.com/article/201808/386844.htm作為程序員,CPU 在我們的工作中扮演了核心角色,因此了解處理器內(nèi)部的工作方式對(duì)程序員來(lái)說(shuō)不無(wú)裨益。
CPU 是如何工作的呢?一條指令執(zhí)行需要多長(zhǎng)時(shí)間?當(dāng)我們討論某個(gè)新款處理器擁有 12 級(jí)流水線(xiàn)還是 18 級(jí)流水線(xiàn),甚至是更深的 31 級(jí)流水線(xiàn)時(shí),這到些都意味著什么呢?
應(yīng)用程序通常會(huì)將 CPU 看作是黑盒子。程序中的指令按照順序依次進(jìn)入 CPU,執(zhí)行完之后再按順序依次從 CPU 中出來(lái),而內(nèi)部到底發(fā)生了什么,我們通常并不了解。
對(duì)我們程序員來(lái)說(shuō),尤其是對(duì)做程序性能調(diào)優(yōu)工作的程序員來(lái)說(shuō),學(xué)習(xí) CPU 內(nèi)部的細(xì)節(jié)非常必要。否則,如果你不知道 CPU 的內(nèi)部結(jié)構(gòu),那如何才能針對(duì) CPU 做性能優(yōu)化?
本文所關(guān)注的就是專(zhuān)門(mén)針對(duì) X86 處理器流水線(xiàn)的工作原理。
你需要掌握的預(yù)備知識(shí)
首先,閱讀本文你需要了解編程,最好了解一點(diǎn)匯編語(yǔ)言。如果你還不知道指令指針(instruction pointer)是什么,那么本文對(duì)你來(lái)說(shuō)可能有些難。你需要知道什么是寄存器,指令和緩存,如果不明白它們是什么,你需要盡快查找資料了解一下。
第二,CPU 的工作原理是一個(gè)非常龐大和復(fù)雜的話(huà)題,本文僅僅是匆匆一瞥,很難以用一篇文章詳盡敘述。如果我有什么疏漏,請(qǐng)通過(guò)評(píng)論告訴我。
第三,我僅僅關(guān)注英特爾處理器及其 X86 架構(gòu)。當(dāng)然除了 X86,還有很多其他架構(gòu)的處理器。雖然 AMD 公司引入了很多新特性到 X86 架構(gòu),但是 X86 架構(gòu)是 Intel 公司發(fā)明,并且創(chuàng)造了 X86 指令集,其中絕大多數(shù)特性是由 Intel 引入的。所以為了保持?jǐn)⑹龅暮?jiǎn)單和一致性,我僅關(guān)注 Intel 的處理器。
最后,當(dāng)你讀到這篇文章時(shí),它已經(jīng)是“過(guò)時(shí)”的了。更新款的處理器已經(jīng)設(shè)計(jì)出來(lái),其中一些會(huì)在未來(lái)幾個(gè)月之內(nèi)發(fā)布。我很高興技術(shù)能如此快速的發(fā)展,我希望有一天所有這些技術(shù)都會(huì)過(guò)時(shí),創(chuàng)造出擁有更驚人計(jì)算能力的 CPU.
處理器流水線(xiàn)基礎(chǔ)
從一個(gè)非常廣的角度來(lái)說(shuō),X86 處理器架構(gòu)在近 35 年來(lái)并沒(méi)有變化太多。雖然 X86 架構(gòu)被附加了很多新功能,但是最初的設(shè)計(jì)(包括幾乎所有最初的指令集)仍然基本上是完整保留的,即使在最新的處理器上仍然被支持。
最初的 8086 處理器支持 14 個(gè)寄存器,這些寄存器在如今最新的處理器中仍然存在。這 14 個(gè)寄存器中,有 4 個(gè)是通用寄存器:AX,BX,CX 和 DX;有 4 個(gè)是段寄存器,段寄存器用來(lái)輔助指針的實(shí)現(xiàn):代碼段(CS),數(shù)據(jù)段(DS),擴(kuò)展段(ES)和堆棧段(SS);有 4 個(gè)是索引寄存器,用來(lái)指向內(nèi)存地址:源引用(SI),目的引用(DI),基指針(BP),棧指針(SP);有 1 個(gè)寄存器包含狀態(tài)位;最后是最重要的寄存器:指令指針(IP)。
指令指針寄存器是一個(gè)擁有特殊功能的指針。指令指針的功能是指向?qū)⒁\(yùn)行的下一條指令。
所有的 X86 處理器都按照相同的模式運(yùn)行。首先,根據(jù)指令指針指向的地址取得下一條即將運(yùn)行的指令并解析該指令(譯碼)。在譯碼完成后,會(huì)有一個(gè)指令的執(zhí)行階段。有些指令用來(lái)從內(nèi)存讀取數(shù)據(jù)或者向內(nèi)存寫(xiě)數(shù)據(jù),有些指令用來(lái)執(zhí)行計(jì)算或者比較等工作。當(dāng)指令執(zhí)行完成后,這條指令會(huì)通過(guò)退出(retire)階段并將指令指針修改為下一條指令。
譯碼,執(zhí)行和退出三級(jí)流水線(xiàn)組成了 X86 處理器指令執(zhí)行的基本模式。從最初的 8086 處理器到最新的酷睿 i7 處理器都基本遵循了這樣的過(guò)程。雖然更新的處理器增加了更多的流水級(jí),但基本的模式?jīng)]有改變。
35 年來(lái)發(fā)生了什么改變
相較于現(xiàn)今的標(biāo)準(zhǔn),最初的處理器設(shè)計(jì)顯得太過(guò)簡(jiǎn)單。最初的 8086 處理器的執(zhí)行過(guò)程可以簡(jiǎn)述為從當(dāng)前指令指針取得指令,通過(guò)譯碼,執(zhí)行最后退出,然后繼續(xù)從指令指針指向的下一條指令處取得指令。
新的處理器增加了新的功能,有些增加了新的指令,有些增加了新的寄存器。我將主要關(guān)注和本文主題有關(guān)系的改變,這些改變影響了 CPU 指令執(zhí)行的流程。其他的一些變化比如虛擬內(nèi)存或者并行處理雖然都很有意義而且有趣,但是并不在本文主題的范圍內(nèi)。
指令緩存在 1982 年被加入到處理器中。通過(guò)指令緩存,處理器可以一次性從內(nèi)存讀取更多指令并放在指令緩存中,而不用每條指令都從內(nèi)存中取。指令緩存僅有幾個(gè)字節(jié)大小,只能容納數(shù)條指令,但是因?yàn)橄酥竺看稳≈竿祪?nèi)存和處理器的時(shí)間,極大的提高的效率
1985 年的 386 處理器引入了數(shù)據(jù)緩存,而且擴(kuò)展了指令緩存的設(shè)計(jì)。數(shù)據(jù)訪(fǎng)存請(qǐng)求通過(guò)一次性讀取更多的數(shù)據(jù)放在數(shù)據(jù)緩存中,從而提升了性能。而且,數(shù)據(jù)緩存和指令緩存都從幾個(gè)字節(jié)擴(kuò)大到幾千字節(jié)。
19巴久年推出的 i486 處理器引入了五級(jí)流水線(xiàn)。這時(shí),在 CPU 中不再僅運(yùn)行一條指令,每一級(jí)流水線(xiàn)在同一時(shí)刻都運(yùn)行著不同的指令。這個(gè)設(shè)計(jì)使得 I486 比同頻率的 386 處理器性能提升了不止一倍。五級(jí)流水線(xiàn)中的取指階段將指令從指令緩存中取出(i486 中的指令緩存為 8KB);第二級(jí)為譯碼階段,將取出的指令翻譯為具體的功能操作;第三級(jí)為轉(zhuǎn)址階段,用來(lái)將內(nèi)存地址和偏移進(jìn)行轉(zhuǎn)換;第四級(jí)為執(zhí)行階段,指令在該階段真正執(zhí)行運(yùn)算;第五級(jí)為退出階段,運(yùn)算的結(jié)果被寫(xiě)回寄存器或者內(nèi)存。由于處理器同時(shí)運(yùn)行了多條指令,大大提升了程序運(yùn)行的性能。
1993 年 Intel 推出了奔騰(Pentium)處理器。由于訴訟問(wèn)題,Intel 無(wú)法繼續(xù)沿用原來(lái)的數(shù)字編號(hào)。因此,用奔騰替代了 586 作為新款處理器的代號(hào)。奔騰處理器相對(duì) i486 處理器對(duì)流水線(xiàn)做出了更多修改。奔騰處理器架構(gòu)增加了第二條獨(dú)立的超標(biāo)量流水線(xiàn)。主流水線(xiàn)工作方式類(lèi)似于 i486,第二條流水線(xiàn)則并行的運(yùn)行一些較簡(jiǎn)單的指令,比如說(shuō)定點(diǎn)算術(shù),而且該流水線(xiàn)能更快的進(jìn)行該運(yùn)算。
1995 年 Intel 推出了奔騰 Pro (Pentium Pro)處理器。和之前的處理器相比,奔騰 Pro 采用了完全不同的設(shè)計(jì)。該處理器采用了諸多新特性以提高性能,包括亂序(Out-of-Order, OOO)執(zhí)行的部件以及猜測(cè)執(zhí)行。流水線(xiàn)擴(kuò)展到了 12 級(jí),而且引入了“超標(biāo)量流水線(xiàn)”的概念,使得許多指令可以被同時(shí)處理。我們稍后將詳盡的介紹亂序執(zhí)行的部件。
在 1995-2002 年之間,亂序執(zhí)行部件經(jīng)過(guò)了數(shù)次重大改進(jìn)。處理器中加入了更多的寄存器;單指令多數(shù)據(jù)(Single Instruction Multiple Data, or SIMD)的引入使得一條指令可以進(jìn)行多組數(shù)據(jù)運(yùn)算;現(xiàn)有的緩存變得更大而且引入了新的緩存;有些流水級(jí)被拆分成更多流水級(jí),有些流水級(jí)被合并,使得更加適合實(shí)際的應(yīng)用。這些改變對(duì)整體性能的提升有重要作用,但它們都沒(méi)有從根本影響數(shù)據(jù)在處理器中的流動(dòng)方式。
2002 年發(fā)布的奔騰 4 處理器引入了超線(xiàn)程技術(shù)。亂序執(zhí)行部件的設(shè)計(jì)使得指令被執(zhí)行的速度比處理器能夠提供指令的速度更快。因此對(duì)于大部分應(yīng)用,CPU 的亂序執(zhí)行部件在大部分時(shí)間處于空閑狀態(tài),甚至在高負(fù)載的情況下也不能充分利用。為了讓指令流能充分的流入亂序執(zhí)行部件,Intel 加入了第二套前端部件(譯注:在處理器結(jié)構(gòu)中,前端是指取指,譯碼,寄存器重命名等模塊,經(jīng)過(guò)前端部件的處理后,指令等待發(fā)射進(jìn)入亂序執(zhí)行部件)。雖然實(shí)際上只有一個(gè)亂序執(zhí)行部件,但對(duì)于操作系統(tǒng)來(lái)說(shuō),它能看到兩個(gè)處理器。前端部件包含兩組同樣功能的 X86 寄存器,兩個(gè)指令譯碼器根據(jù)兩個(gè)指令指針指向的地址分別處理。所有的指令被一個(gè)共享的亂序執(zhí)行部件執(zhí)行,但對(duì)應(yīng)用程序來(lái)說(shuō)并不知情。當(dāng)亂序執(zhí)行部件執(zhí)行完成,像之前一樣退出流水線(xiàn)后,最終結(jié)果返回虛擬的兩個(gè)處理器。
2006 年 Intel 發(fā)布了酷睿(Core)微架構(gòu)。為了品牌效應(yīng),它被稱(chēng)做酷睿2(二總比一好)。令人驚訝的是,處理器頻率不升反降,而且超線(xiàn)程也被去掉了。通過(guò)降低時(shí)鐘頻率,每一級(jí)流水線(xiàn)可以做更多工作。亂序執(zhí)行部件也被擴(kuò)展的更寬。各種不同的緩存和隊(duì)列都相應(yīng)做的更大。而且處理器被重新設(shè)計(jì),以適應(yīng)雙核和四核的共享緩存結(jié)構(gòu)。
2008 年,Intel 開(kāi)始用酷睿 i3, i5, i7 的方式來(lái)命名新的處理器。新處理器重新引入了超線(xiàn)程。這三個(gè)系列的處理器主要區(qū)別在于內(nèi)部緩存大小不同。
未來(lái)的處理器:Intel 的下一代微結(jié)構(gòu)被稱(chēng)為 Haswell.Haswell 據(jù)稱(chēng)將于 2013 年發(fā)布。目前已知的文檔說(shuō)明它將擁有 14 級(jí)流水級(jí)的亂序執(zhí)行部件,所以它仍然遵循從奔騰 Pro 以來(lái)的基本設(shè)計(jì)思路。
那么,流水線(xiàn)到底是什么?亂序執(zhí)行部件是什么?他們?nèi)绾翁嵘颂幚砥鞯男阅苣?
CPU 指令流水線(xiàn)
根據(jù)之前描述的基礎(chǔ),指令進(jìn)入流水線(xiàn),通過(guò)流水線(xiàn)處理,從流水線(xiàn)出來(lái)的過(guò)程,對(duì)于我們程序員來(lái)說(shuō),是比較直觀的。
I486 擁有五級(jí)流水線(xiàn)。分別是:取指(Fetch),譯碼(D1, main decode),轉(zhuǎn)址(D2, translate),執(zhí)行(EX, execute),寫(xiě)回(WB)。某個(gè)指令可以在流水線(xiàn)的任何一級(jí)。
但是這樣的流水線(xiàn)有一個(gè)明顯的缺陷。對(duì)于下面的指令代碼,它們的功能是將兩個(gè)變量的內(nèi)容進(jìn)行交換。
XOR a, b
XOR b, a
XOR a, b
從 8086 直到 386 處理器都沒(méi)有流水線(xiàn)。處理器一次只能執(zhí)行一條指令。再這樣的架構(gòu)下,上面的代碼執(zhí)行并不會(huì)存在問(wèn)題。
但是 i486 處理器是首個(gè)擁有流水線(xiàn)的 x86 處理器,它執(zhí)行上面的代碼會(huì)發(fā)生什么呢?當(dāng)你一下去觀察很多指令在流水線(xiàn)中運(yùn)行,你會(huì)覺(jué)得混亂,所以你需要回頭參考上面的圖。
第一步是第一條指令進(jìn)入取指階段;然后在第二步第一條指令進(jìn)入譯碼階段,同時(shí)第二條指令進(jìn)入取指階段;第三步第一條指令進(jìn)入轉(zhuǎn)址階段,第二條指令進(jìn)入譯碼階段,第三條指令進(jìn)入取指階段。但是在第四步會(huì)出現(xiàn)問(wèn)題,第一條指令會(huì)進(jìn)入執(zhí)行階段,而其他指令卻不能繼續(xù)向前移動(dòng)。第二條 xor 指令需要第一條 xor 指令計(jì)算的結(jié)果a,但是直到第一條指令執(zhí)行完成才會(huì)寫(xiě)回。所以流水線(xiàn)的其他指令就會(huì)在當(dāng)前流水級(jí)等待直到第一條指令的執(zhí)行和寫(xiě)回階段完成。第二條指令會(huì)等待第一條指令完成才能進(jìn)入流水線(xiàn)下一級(jí),同樣第三條指令也要等待第二條指令完成。
這個(gè)現(xiàn)象被稱(chēng)為流水線(xiàn)阻塞或者流水線(xiàn)氣泡。
另外一個(gè)關(guān)于流水線(xiàn)的問(wèn)題是有些指令執(zhí)行速度快,有些指令執(zhí)行速度慢。這個(gè)問(wèn)題在奔騰處理器的雙流水線(xiàn)架構(gòu)下顯得更加明顯。
奔騰 Pro 擁有 12 級(jí)流水線(xiàn)。當(dāng)這個(gè)數(shù)字被首次宣布后,所有的程序員都倒抽了一口氣,因?yàn)樗麄冎莱瑯?biāo)量流水線(xiàn)是如何工作的。如果 Intel 仍然按照以前的思路設(shè)計(jì)超標(biāo)量流水線(xiàn)的話(huà),流水線(xiàn)的阻塞和執(zhí)行速度慢的指令會(huì)嚴(yán)重影響執(zhí)行速度。但同時(shí),Intel 宣布了完全不同的流水線(xiàn)設(shè)計(jì),叫做亂序執(zhí)行部件(Out-of-Order core)。單從敘述上很難理解這些改變帶來(lái)的好處,但 Intel 確信這些改進(jìn)是令人激動(dòng)的。
讓我們來(lái)更深入的看看這個(gè)亂序執(zhí)行的部件吧!
亂序執(zhí)行流水線(xiàn)
在描述亂序執(zhí)行流水線(xiàn)時(shí),往往是一圖勝千言。所以我們主要以圖例進(jìn)行介紹。
CPU 流水線(xiàn)圖例
I486 處理器擁有 5 級(jí)流水線(xiàn)。這種設(shè)計(jì)在現(xiàn)實(shí)世界中的其他處理器中很常見(jiàn),而且效率不錯(cuò)。
而奔騰處理器的流水線(xiàn)比 i486 更好。兩條流水線(xiàn)可以并行運(yùn)行,而且每條流水線(xiàn)可以同時(shí)有多條指令在不同流水級(jí)執(zhí)行。它幾乎可以同時(shí)執(zhí)行比 i486 多一倍的指令。
能夠快速完成的指令需要等待前面執(zhí)行慢的指令即使在并行流水線(xiàn)中也仍然是一個(gè)問(wèn)題。流水線(xiàn)仍然是線(xiàn)性的,導(dǎo)致處理器面臨性能瓶頸難以逾越。
亂序執(zhí)行部件和之前處理器設(shè)計(jì)中的線(xiàn)性通路有很大不同,它增加了一些復(fù)雜度,引入了非線(xiàn)性的通路。
評(píng)論