一沮焕、CPU指令的執(zhí)行過程
幾乎所有的馮·諾伊曼型計(jì)算機(jī)的 CPU,其工作都可以分為 5 個(gè)階段:取指令拉宗、指令譯碼峦树、執(zhí)行指令、訪存取數(shù)簿废、結(jié)果寫回空入。
圖1 CPU指令的執(zhí)行階段
1.取指令階段
取指令(Instruction Fetch,IF)階段是將一條指令從主存中取到指令寄存器的過程族檬。 程序計(jì)數(shù)器 PC 中的數(shù)值允耿,用來指示當(dāng)前指令在主存中的位置系枪。當(dāng)一條指令被取出后本橙,PC 中的數(shù)值將根據(jù)指令字長度而自動遞增:若為單字長指令握础,則(PC)+1->PC;若為雙字長指令扫尖,則(PC)+2->PC白对,依此類推。
2.指令譯碼階段
取出指令后换怖,計(jì)算機(jī)立即進(jìn)入指令譯碼(Instruction Decode甩恼,ID)階段。 在指令譯碼階段沉颂,指令譯碼器按照預(yù)定的指令格式条摸,對取回的指令進(jìn)行拆分和解釋,識別區(qū)分出不同的指令類別以及各種獲取操作數(shù)的方法铸屉。在組合邏輯控制的計(jì)算機(jī)中钉蒲,指令譯碼器對不同的指令操作碼產(chǎn)生不同的控制電位,以形成不同的微操作序列彻坛;在微程序控制的計(jì)算機(jī)中顷啼,指令譯碼器用指令操作碼來找到執(zhí)行該指令的微程序的入口踏枣,并從此入口開始執(zhí)行。 在傳統(tǒng)的設(shè)計(jì)里钙蒙,CPU中負(fù)責(zé)指令譯碼的部分是無法改變的茵瀑。不過,在眾多運(yùn)用微程序控制技術(shù)的新型 CPU 中仪搔,微程序有時(shí)是可重寫的瘾婿。
3.執(zhí)行指令階段
在取指令和指令譯碼階段之后蜻牢,接著進(jìn)入執(zhí)行指令(Execute烤咧,EX)階段。 此階段的任務(wù)是完成指令所規(guī)定的各種操作抢呆,具體實(shí)現(xiàn)指令的功能煮嫌。為此,CPU 的不同部分被連接起來抱虐,以執(zhí)行所需的操作昌阿。 例如,如果要求完成一個(gè)加法運(yùn)算恳邀,算術(shù)邏輯單元 ALU 將被連接到一組輸入和一組輸出懦冰,輸入端提供需要相加的數(shù)值,輸出端將含有最后的運(yùn)算結(jié)果谣沸。
4.訪存取數(shù)階段
根據(jù)指令需要刷钢,有可能要訪問主存,讀取操作數(shù)乳附,這樣就進(jìn)入了訪存取數(shù)(Memory内地,MEM)階段。 此階段的任務(wù)是:根據(jù)指令地址碼赋除,得到操作數(shù)在主存中的地址阱缓,并從主存中讀取該操作數(shù)用于運(yùn)算。
5.結(jié)果寫回階段
作為最后一個(gè)階段举农,結(jié)果寫回(Writeback荆针,WB)階段把執(zhí)行指令階段的運(yùn)行結(jié)果數(shù)據(jù)“寫回”到某種存儲形式:結(jié)果數(shù)據(jù)經(jīng)常被寫到 CPU 的內(nèi)部寄存器中,以便被后續(xù)的指令快速地存劝湓恪航背;在有些情況下, 結(jié)果數(shù)據(jù)也可被寫入相對較慢滚停、但較廉價(jià)且容量較大的主存沃粗。許多指令還會改變程序狀態(tài)字寄存器中標(biāo)志位 的狀態(tài),這些標(biāo)志位標(biāo)識著不同的操作結(jié)果键畴,可被用來影響程序的動作最盅。
在指令執(zhí)行完畢突雪、結(jié)果數(shù)據(jù)寫回之后,若無意外事件(如結(jié)果溢出等)發(fā)生涡贱,計(jì)算機(jī)就接著從程序計(jì)數(shù)器 PC 中取得下一條指令地址咏删,開始新一輪的循環(huán),下一個(gè)指令周期將順序取出下一條指令问词。許多新型 CPU 可以同時(shí)取出督函、譯碼和執(zhí)行多條指令,體現(xiàn)并行處理的特性激挪。
二辰狡、CPU指令流水線
在任一條指令的執(zhí)行過程中,各個(gè)功能部件都會隨著指令執(zhí)行的進(jìn)程而呈現(xiàn)出時(shí)忙時(shí)閑的現(xiàn)象垄分。要加快計(jì)算機(jī)的工作速度宛篇,就應(yīng)使各個(gè)功能部件并行工作,即以各自可能的高速度同時(shí)薄湿、不停地工作叫倍,使得各部件的操作在時(shí)間上重疊進(jìn)行,實(shí)現(xiàn)流水式作業(yè)豺瘤。 從原理上說吆倦,計(jì)算機(jī)的流水線(Pipeline)工作方式就是將一個(gè)計(jì)算任務(wù)細(xì)分成若干個(gè)子任務(wù),每個(gè)子任務(wù)都由專門的功能部件進(jìn)行處理坐求,一個(gè)計(jì)算任務(wù)的各個(gè)子任務(wù)由流水線上各個(gè)功能部件輪流進(jìn)行處理 (即各子任務(wù)在流水線的各個(gè)功能階段并發(fā)執(zhí)行)蚕泽,最終完成工作。這樣瞻赶,不必等到上一個(gè)計(jì)算任務(wù)完成赛糟, 就可以開始下一個(gè)計(jì)算任務(wù)的執(zhí)行。 流水線的硬件基本結(jié)構(gòu)如圖2所示砸逊。流水線由一系列串聯(lián)的功能部件(Si)組成璧南,各個(gè)功能部件之間設(shè)有高速緩沖寄存器(L),以暫時(shí)保存上一功能部件對子任務(wù)處理的結(jié)果师逸,同時(shí)又能夠接受新的處理任務(wù)司倚。在一個(gè)統(tǒng)一的時(shí)鐘(C)控制下,計(jì)算任務(wù)從功能部件的一個(gè)功能段流向下一個(gè)功能段篓像。在流水線中动知, 所有功能段同時(shí)對不同的數(shù)據(jù)進(jìn)行不同的處理,各個(gè)處理步驟并行地操作员辩。
當(dāng)任務(wù)連續(xù)不斷地輸入流水線時(shí)盒粮,在流水線的輸出端便連續(xù)不斷地輸出執(zhí)行結(jié)果,流水線達(dá)到不間斷流水的穩(wěn)定狀態(tài)奠滑,從而實(shí)現(xiàn)了子任務(wù)級的并行丹皱。
當(dāng)指令流不能順序執(zhí)行時(shí)妒穴,流水過程會中斷(即斷流)。為了保證流水過程的工作效率摊崭,流水過程不應(yīng)經(jīng)常斷流讼油。在一個(gè)流水過程中,實(shí)現(xiàn)各個(gè)子過程的各個(gè)功能段所需要的時(shí)間應(yīng)該盡可能保持相等呢簸,以避免產(chǎn)生瓶頸矮台,導(dǎo)致流水線斷流。
流水線技術(shù)本質(zhì)上是將一個(gè)重復(fù)的時(shí)序過程分解成若干個(gè)子過程根时,而每一個(gè)子過程都可有效地在其專用功能段上與其他子過程同時(shí)執(zhí)行瘦赫。采用流水線技術(shù)通過硬件實(shí)現(xiàn)并行操作后,就某一條指令而言啸箫,其執(zhí)行速度并沒有加快耸彪,但就程序執(zhí)行過程的整體而言伞芹,程序執(zhí)行速度大大加快忘苛。
流水線技術(shù)適合于大量的重復(fù)性的處理。
前面我提到過CPU 中一個(gè)指令周期的任務(wù)分解唱较。假設(shè)指令周期包含取指令(IF)扎唾、指令譯碼(ID)、 指令執(zhí)行(EX)南缓、訪存取數(shù)(MEM)胸遇、結(jié)果寫回(WB)5 個(gè)子過程(過程段),流水線由這 5個(gè)串聯(lián)的過程段 組成汉形,各個(gè)過程段之間設(shè)有高速緩沖寄存器纸镊,以暫時(shí)保存上一過程段子任務(wù)處理的結(jié)果,在統(tǒng)一的時(shí)鐘信號控制下概疆,數(shù)據(jù)從一個(gè)過程段流向相鄰的過程段逗威。
非流水計(jì)算機(jī)的時(shí)空圖如下:
對于非流水計(jì)算機(jī)而言,上一條指令的 5 個(gè)子過程全部執(zhí)行完畢后才能開始下一條指令岔冀,每隔 5 個(gè)時(shí) 鐘周期才有一個(gè)輸出結(jié)果凯旭。因此,圖3中用了 15 個(gè)時(shí)鐘周期才完成 3 條指令使套,每條指令平均用時(shí) 5 個(gè)時(shí)鐘周期罐呼。 非流水線工作方式的控制比較簡單,但部件的利用率較低侦高,系統(tǒng)工作速度較慢嫉柴。
標(biāo)量流水計(jì)算機(jī)工作方式
標(biāo)量(Scalar)流水計(jì)算機(jī)是只有一條指令流水線的計(jì)算機(jī)。圖 4表示標(biāo)量流水計(jì)算機(jī)的時(shí)空圖奉呛。
對標(biāo)量流水計(jì)算機(jī)而言计螺,上一條指令與下一條指令的 5 個(gè)子過程在時(shí)間上可以重疊執(zhí)行期奔,當(dāng)流水線滿 載時(shí),每一個(gè)時(shí)鐘周期就可以輸出一個(gè)結(jié)果危尿。因此呐萌,圖4中僅用了 9 個(gè)時(shí)鐘周期就完成了 5 條指令,每條指令平均用時(shí) 1.8 個(gè)時(shí)鐘周期谊娇。
采用標(biāo)量流水線工作方式肺孤,雖然每條指令的執(zhí)行時(shí)間并未縮短,但 CPU 運(yùn)行指令的總體速度卻能成倍 提高济欢。當(dāng)然赠堵,作為速度提高的代價(jià),需要增加部分硬件才能實(shí)現(xiàn)標(biāo)量流水法褥。
超標(biāo)量流水計(jì)算機(jī)工作方式
一般的流水計(jì)算機(jī)因只有一條指令流水線茫叭,所以稱為標(biāo)量流水計(jì)算機(jī)。所謂超標(biāo)量(Superscalar)流 水計(jì)算機(jī)半等,是指它具有兩條以上的指令流水線揍愁。圖 5表示超標(biāo)量流水計(jì)算機(jī)的時(shí)空圖。
當(dāng)流水線滿載時(shí)杀饵,每一個(gè)時(shí)鐘周期可以執(zhí)行 2 條以上的指令莽囤。因此,圖5中僅用了 9 個(gè)時(shí)鐘周期就完成了 10 條指令切距,每條指令平均用時(shí) 0.9 個(gè)時(shí)鐘周期朽缎。 超標(biāo)量流水計(jì)算機(jī)是時(shí)間并行技術(shù)和空間并行技術(shù)的綜合應(yīng)用。
三谜悟、指令的相關(guān)性
指令流水線的一個(gè)特點(diǎn)是流水線中的各條指令之間存在一些相關(guān)性话肖,使得指令的執(zhí)行受到影響。要使流水線發(fā)揮高效率葡幸,就要使流水線連續(xù)不斷地流動最筒,盡量不出現(xiàn)斷流情況。然而礼患,由于流水過程中存在的相關(guān)性沖突是钥,斷流現(xiàn)象是不可避免的。
1.?dāng)?shù)據(jù)相關(guān)
在流水計(jì)算機(jī)中缅叠,指令的處理是重疊進(jìn)行的悄泥,前一條指令還沒有結(jié)束,第二肤粱、三條指令就陸續(xù)開始工 作弹囚。由于多條指令的重疊處理,當(dāng)后繼指令所需的操作數(shù)剛好是前一指令的運(yùn)算結(jié)果時(shí)领曼,便發(fā)生數(shù)據(jù)相關(guān)沖突鸥鹉。由于這兩條指令的執(zhí)行順序直接影響到操作數(shù)讀取的內(nèi)容蛮穿,必須等前一條指令執(zhí)行完畢后才能執(zhí)行后一條指令。在這種情況下毁渗,這兩條指令就是數(shù)據(jù)相關(guān)的践磅。因此,數(shù)據(jù)相關(guān)是由于指令之間存在數(shù)據(jù)依賴性而引起的灸异。根據(jù)指令間對同一寄存器讀和寫操作的先后次序關(guān)系府适,可將數(shù)據(jù)相關(guān)性分為寫后讀(Read-AfterWrite,RAW)相關(guān)肺樟、讀后寫(Write-After-Read檐春,WAR)相關(guān)、寫后寫(Write-After-Write么伯,WAW)相關(guān)三種類型疟暖。
解決數(shù)據(jù)相關(guān)沖突的辦法如下:
采用編譯的方法 編譯程序通過在兩條相關(guān)指令之間插入其他不相關(guān)的指令(或空操作指令)而推遲指令的執(zhí)行,使數(shù)據(jù)相關(guān)消失田柔,從而產(chǎn)生沒有相關(guān)性的程序代碼俐巴。這種方式簡單,但降低了運(yùn)行效率凯楔。
由硬件監(jiān)測相關(guān)性的存在窜骄,采用數(shù)據(jù)旁路技術(shù)設(shè)法解決數(shù)據(jù)相關(guān) 當(dāng)前一條指令要寫入寄存器而下一條指令要讀取同一個(gè)寄存器時(shí),在前一條指令執(zhí)行完畢摆屯、結(jié)果數(shù)據(jù)還未寫入寄存器前,由內(nèi)部數(shù)據(jù)通路把該結(jié)果數(shù)據(jù)直接傳遞給下一條指令糠亩,也就是說虐骑,下一條指令所需的 操作數(shù)不再通過讀取寄存器獲得,而是直接獲取赎线。這種方式效率較高廷没,但控制較為復(fù)雜。
2.資源相關(guān)
所謂資源相關(guān)垂寥,是指多條指令進(jìn)入流水線后在同一機(jī)器周期內(nèi)爭用同一個(gè)功能部件所發(fā)生的沖突颠黎。 例如,在圖 4所示的標(biāo)量流水計(jì)算機(jī)中滞项,在第 4 個(gè)時(shí)鐘周期時(shí)狭归,第 1 條指令處于訪存取數(shù)(MEM) 階段,而第 4 條指令處于取指令(IF)階段文判。如果數(shù)據(jù)和指令存放在同一存儲器中过椎,且存儲器只有一個(gè)端口,這樣便會發(fā)生這兩條指令爭用存儲器的資源相關(guān)沖突戏仓。 因?yàn)槊恳粭l指令都可能需要 2 次訪問存儲器(讀指令和讀寫數(shù)據(jù))疚宇,在指令流水過程中亡鼠,可能會有 2 條指令同時(shí)需要訪問存儲器,導(dǎo)致資源相關(guān)沖突解決資源相關(guān)沖突的一般辦法是增加資源敷待,例如增設(shè)一個(gè)存儲器间涵,將指令和數(shù)據(jù)分別放在兩個(gè)存儲器中。
3.控制相關(guān)
控制相關(guān)沖突是由轉(zhuǎn)移指令引起的榜揖。當(dāng)執(zhí)行轉(zhuǎn)移指令時(shí)浑厚,依據(jù)轉(zhuǎn)移條件的產(chǎn)生結(jié)果,可能順序取下一 條指令根盒,也可能轉(zhuǎn)移到新的目標(biāo)地址取指令钳幅。若轉(zhuǎn)移到新的目標(biāo)地址取指令,則指令流水線將被排空炎滞,并等待轉(zhuǎn)移指令形成下一條指令的地址敢艰,以便讀取新的指令,這就使得流水線發(fā)生斷流册赛。 為了減小轉(zhuǎn)移指令對流水線性能的影響钠导,通常采用以下兩種轉(zhuǎn)移處理技術(shù):
延遲轉(zhuǎn)移法 由編譯程序重排指令序列來實(shí)現(xiàn)。其基本思想是“先執(zhí)行再轉(zhuǎn)移”森瘪,即發(fā)生轉(zhuǎn)移時(shí)并不排空指令流水線牡属,而是繼續(xù)完成下幾條指令。如果這些后繼指令是與該轉(zhuǎn)移指令結(jié)果無關(guān)的有用指令扼睬,那么延遲損失時(shí)間片正好得到了有效的利用逮栅。
轉(zhuǎn)移預(yù)測法 用硬件方法來實(shí)現(xiàn)。依據(jù)指令過去的行為來預(yù)測將來的行為窗宇,即選擇出現(xiàn)概率較高的分支進(jìn)行預(yù)取措伐。通過使用轉(zhuǎn)移取和順序取兩路指令預(yù)取隊(duì)列以及目標(biāo)指令 Cache,可將轉(zhuǎn)移預(yù)測提前到取指令階段進(jìn)行军俊,以獲得良好的效果侥加。
四、指令的動態(tài)執(zhí)行技術(shù)
1.指令調(diào)度
為了減少指令相關(guān)性對執(zhí)行速度的影響粪躬,可以在保證程序正確性的前提下担败,調(diào)整指令的順序,即進(jìn)行指令調(diào)度镰官。 指令調(diào)度可以由編譯程序進(jìn)行提前,也可以由硬件在執(zhí)行的時(shí)候進(jìn)行,分別稱為靜態(tài)指令調(diào)度和動態(tài)指令調(diào)度朋魔。靜態(tài)指令調(diào)度是指編譯程序通過調(diào)整指令的順序來減少流水線的停頓岖研,提高程序的執(zhí)行速度;動態(tài) 指令調(diào)度用硬件方法調(diào)度指令的執(zhí)行以減少流水線停頓。
流水線中一直采用的有序(in-order)指令啟動是限制流水線性能的主要因素之一孙援。如果有一條指令在流水線中停頓了害淤,則其后的指令就都不能向前流動了,這樣拓售,如果相鄰的兩條指令存在相關(guān)性窥摄,流水線就將發(fā)生停頓,如果有多個(gè)功能部件础淤,這些部件就有可能被閑置崭放。消除這種限制流水線性能的因素從而提高指令執(zhí)行速度,其基本思想就是允許指令的執(zhí)行是無序的(out-of-order鸽凶,也稱亂序)币砂,也就是說,在保持指令間玻侥、數(shù)據(jù)間的依賴關(guān)系的前提下决摧,允許不相關(guān)的指令的執(zhí)行順序與程序的原有順序有所不同,這一思想是實(shí)行動態(tài)指令調(diào)度的前提凑兰。
2.亂序執(zhí)行技術(shù)
亂序執(zhí)行(Out-of-order Execution)是以亂序方式執(zhí)行指令掌桩,即 CPU 允許將多條指令不按程序規(guī)定的順序而分開發(fā)送給各相應(yīng)電路單元進(jìn)行處理。這樣姑食,根據(jù)各個(gè)電路單元的狀態(tài)和各指令能否提前執(zhí)行的具體情況分析波岛,將能夠提前執(zhí)行的指令立即發(fā)送給相應(yīng)電路單元予以執(zhí)行,在這期間不按規(guī)定順序執(zhí)行指令音半;然后由重新排列單元將各執(zhí)行單元結(jié)果按指令順序重新排列则拷。亂序執(zhí)行的目的,就是為了使 CPU 內(nèi)部電路滿負(fù)荷運(yùn)轉(zhuǎn)祟剔,并相應(yīng)提高 CPU 運(yùn)行程序的速度隔躲。
實(shí)現(xiàn)亂序執(zhí)行的關(guān)鍵在于取消傳統(tǒng)的“取指”和“執(zhí)行”兩個(gè)階段之間指令需要線性排列的限制,而使用一個(gè)指令緩沖池來開辟一個(gè)較長的指令窗口物延,允許執(zhí)行單元在一個(gè)較大的范圍內(nèi)調(diào)遣和執(zhí)行已譯碼的程序指令流。
3.分支預(yù)測
分支預(yù)測(Branch Prediction)是對程序的流程進(jìn)行預(yù)測仅父,然后讀取其中一個(gè)分支的指令叛薯。采用分支預(yù)測的主要目的是為了提高 CPU的運(yùn)算速度。 分支預(yù)測的方法有靜態(tài)預(yù)測和動態(tài)預(yù)測兩類:靜態(tài)預(yù)測方法比較簡單笙纤,如預(yù)測永遠(yuǎn)不轉(zhuǎn)移耗溜、預(yù)測永遠(yuǎn)轉(zhuǎn)移、預(yù)測后向轉(zhuǎn)移等等省容,它并不根據(jù)執(zhí)行時(shí)的條件和歷史信息來進(jìn)行預(yù)測抖拴,因此預(yù)測的準(zhǔn)確性不可能很高;動態(tài)預(yù)測方法則根據(jù)同一條轉(zhuǎn)移指令過去的轉(zhuǎn)移情況來預(yù)測未來的轉(zhuǎn)移情況。 由于程序中的條件分支是根據(jù)程序指令在流水線處理后的結(jié)果來執(zhí)行的阿宅,所以當(dāng) CPU 等待指令結(jié)果時(shí)候衍, 流水線的前級電路也處于等待分支指令的空閑狀態(tài),這樣必然出現(xiàn)時(shí)鐘周期的浪費(fèi)洒放。如果 CPU 能在前條指令結(jié)果出來之前就預(yù)測到分支是否轉(zhuǎn)移蛉鹿,那么就可以提前執(zhí)行相應(yīng)的指令,這樣就避免了流水線的空閑等待往湿,也就相應(yīng)提高了 CPU 的運(yùn)算速度妖异。但另一方面,一旦前條指令結(jié)果出來后證明分支預(yù)測是錯(cuò)誤的领追,那么就必須將已經(jīng)裝入流水線執(zhí)行的指令和結(jié)果全部清除他膳,然后再裝入正確的指令重新處理,這樣就比不進(jìn)行分支預(yù)測而是等待結(jié)果再執(zhí)行新指令還要慢了绒窑。
因此棕孙,分支預(yù)測的錯(cuò)誤并不會導(dǎo)致結(jié)果的錯(cuò)誤,而只是導(dǎo)致流水線的停頓回论,如果能夠保持較高的預(yù)測 準(zhǔn)確率散罕,分支預(yù)測就能提高流水線的性能。
五傀蓉、實(shí)例分析
前面的知識只是一個(gè)理論基礎(chǔ)鋪墊欧漱,下面我們就結(jié)合一款真實(shí)的CPU架構(gòu)進(jìn)行對應(yīng)分析,圖6和圖7分別是x86和ARM體系結(jié)構(gòu)的內(nèi)核架構(gòu)圖(都是具有OoOE特性的CPU架構(gòu))葬燎,可以看到他們基本的組成都是一樣的(雖然x86是CISC而ARM是RISC误甚,但是現(xiàn)代x86內(nèi)部也是先把CISC翻譯成RISC的),因此我在這里就只分析x86結(jié)構(gòu)谱净。
1.取指令階段(IF)
處理器在執(zhí)行指令之前窑邦,必須先裝載指令。指令會先保存在 L1 緩存的 I-cache (Instruction-cache)指令緩存當(dāng)中壕探,Nehalem 的指令拾取單元使用 128bit 帶寬的通道從 I-cache 中讀取指令冈钦。這個(gè) I-cache 的大小為 32KB,采用了 4 路組相連李请,在后面的存取單元介紹中我們可以得知這種比 Core 更少的集合關(guān)聯(lián)數(shù)量是為了降低延遲瞧筛。
為了適應(yīng)超線程技術(shù),RIP(Relative Instruction Point导盅,相對指令指針)的數(shù)量也從一個(gè)增加到了兩個(gè)较幌,每個(gè)線程單獨(dú)使用一個(gè)。
指令拾取單元包含了分支預(yù)測器(Branch Predictor)白翻,分支預(yù)測是在 Pentium Pro 處理器開始加入的功能乍炉,預(yù)測如 if then 這樣的語句的將來走向,提前讀取相關(guān)的指令并執(zhí)行的技術(shù),可以明顯地提升性能岛琼。指令拾取單元也包含了 Hardware Prefetcher底循,根據(jù)歷史操作預(yù)先加載以后會用到的指令來提高性能,這會在后面得到詳細(xì)的介紹衷恭。
當(dāng)分支預(yù)測器決定了走向一個(gè)分支之后此叠,它使用 BTB(Branch Target Buffer,分支目標(biāo)緩沖區(qū))來保存預(yù)測指令的地址随珠。Nehalem 從以前的一級 BTB 升級到了兩個(gè)級別灭袁,這是為了適應(yīng)很大體積的程序(數(shù)據(jù)庫以及 ERP 等應(yīng)用,跳轉(zhuǎn)分支將會跨過很大的區(qū)域并具有很多的分支)窗看。Intel 并沒有提及 BTB 詳細(xì)的結(jié)構(gòu)茸歧。與BTB 相對的 RSB(Return Stack Buffer,返回堆棧緩沖區(qū))也得到了提升显沈,RSB 用來保存一個(gè)函數(shù)或功能調(diào)用結(jié)束之后的返回地址软瞎,通過重命名的 RSB 來避免多次推測路徑導(dǎo)致的入口/出口破壞恩静。RSB 每個(gè)線程都有一個(gè)叹洲,一個(gè)核心就擁有兩個(gè)婶芭,以適應(yīng)超線程技術(shù)的存在大磺。
指令拾取單元使用預(yù)測指令的地址來拾取指令,它通過訪問 L1 ITLB 里的索引來繼續(xù)訪問 L1 ICache咬清,128 條目的小頁面 L1 ITLB 按照兩個(gè)線程靜態(tài)分區(qū)谢澈,每個(gè)線程可以獲得 64 個(gè)條目洒嗤,這個(gè)數(shù)目比 Core 2 的少院尔。當(dāng)關(guān)閉超線程時(shí)蜻展,單獨(dú)的線程將可以獲得全部的 TLB 資 源。除了小頁面 TLB 之外邀摆,Nehalem 還每個(gè)線程擁有 7 個(gè)條目的全關(guān)聯(lián)(Full Associativity) 大頁面 ITLB纵顾,這些 TLB 用于訪問 2M/4M 的大容量頁面,每個(gè)線程獨(dú)立栋盹,因此關(guān)閉超線程不會讓你得到 14 個(gè)大頁面 ITLB 條目施逾。
指令拾取單元通過 128bit 的總線將指令從 L1 ICache 拾取到一個(gè) 16Bytes(剛好就是 128bit)的預(yù)解碼拾取緩沖區(qū)。128 位的帶寬讓人有些迷惑不解例获,Opteron 一早就已經(jīng)使用 了 256bit 的指令拾取帶寬音念。最重要的是,L1D 和 L1I 都是通過 256bit 的帶寬連接到 L2 Cache 的躏敢。
由于一般的CISC x86指令都小于4Bytes(32位x86指令;x86指令的特點(diǎn)就是不等長), 因此一次可以拾取 4 條以上的指令整葡,而預(yù)解碼拾取緩沖區(qū)的輸出帶寬是 6 指令每時(shí)鐘周期件余, 因此可以看出指令拾取帶寬確實(shí)有些不協(xié)調(diào),特別是考慮到 64 位應(yīng)用下指令會長一些的情 況下(解碼器的輸入輸出能力是 4 指令每時(shí)鐘周期,因此 32 位下問題不大)啼器。
指令拾取結(jié)束后會送到 18 個(gè)條目的指令隊(duì)列旬渠,在 Core 架構(gòu),送到的是 LSD 循環(huán)流緩沖區(qū)端壳,在后面可以看到告丢,Nehalem 通過將 LSD 移動后更靠后的位置來提高性能。
2.指令譯碼階段(ID)
在將指令充填到可容納 18 條目的指令隊(duì)列之后损谦,就可以進(jìn)行解碼工作了岖免。解碼是類 RISC (精簡指令集或簡單指令集)處理器導(dǎo)致的一項(xiàng)設(shè)計(jì),從 Pentium Pro 開始在 IA 架構(gòu)出現(xiàn)照捡。 處理器接受的是 x86 指令(CISC 指令颅湘,復(fù)雜指令集),而在執(zhí)行引擎內(nèi)部執(zhí)行的卻不是x86 指令栗精,而是一條一條的類 RISC 指令闯参,Intel 稱之為 Micro Operation——micro-op,或者寫 為 μ-op悲立,一般用比較方便的寫法來替代掉希臘字母:u-op 或者 uop鹿寨。相對地,一條一條的 x86 指令就稱之為 Macro Operation或 macro-op薪夕。
RISC 架構(gòu)的特點(diǎn)就是指令長度相等脚草,執(zhí)行時(shí)間恒定(通常為一個(gè)時(shí)鐘周期),因此處理器設(shè)計(jì)起來就很簡單寥殖,可以通過深長的流水線達(dá)到很高的頻率玩讳,IBM 的 Power6 就可以輕松地達(dá)到 4.7GHz 的起步頻率。和 RISC 相反嚼贡,CISC 指令的長度不固定熏纯,執(zhí)行時(shí)間也不固定,因此 Intel 的 RISC/CISC 混合處理器架構(gòu)就要通過解碼器 將 x86 指令翻譯為 uop粤策,從而獲得 RISC 架構(gòu)的長處樟澜,提升內(nèi)部執(zhí)行效率。
和 Core 一樣叮盘,Nehalem 的解碼器也是 4 個(gè)(3 個(gè)簡單解碼器加 1 個(gè)復(fù)雜解碼器)秩贰。簡單解碼器可以將一條 x86 指令(包括大部分 SSE 指令在內(nèi))翻譯為一條 uop,而復(fù)雜解碼器則將一些特別的(單條)x86 指令翻譯為 1~4 條 uops——在極少數(shù)的情況下柔吼,某些指令需要通過 額外的可編程 microcode 解碼器解碼為更多的 uops(有些時(shí)候甚至可以達(dá)到幾百個(gè)毒费,因?yàn)?一些 IA 指令很復(fù)雜,并且可以帶有很多的前綴/修改量愈魏,當(dāng)然這種情況很少見)觅玻,下圖 Complex Decoder 左方的 ucode 方塊就是這個(gè)解碼器想际,這個(gè)解碼器可以通過一些途徑進(jìn)行升級或者擴(kuò)展,實(shí)際上就是通過主板 Firmware 里面的 Microcode ROM 部分溪厘。
之所以具有兩種解碼器胡本,是因?yàn)槿匀皇顷P(guān)于 RISC/CISC 的一個(gè)事實(shí): 大部分情況下(90%) 的時(shí)間內(nèi)處理器都在運(yùn)行少數(shù)的指令,其余的時(shí)間則運(yùn)行各式各樣的復(fù)雜指令(不幸的是畸悬, 復(fù)雜就意味著較長的運(yùn)行時(shí)間)侧甫,RISC 就是將這些復(fù)雜的指令剔除掉,只留下最經(jīng)常運(yùn)行的指令(所謂的精簡指令集)蹋宦,然而被剔除掉的那些指令雖然實(shí)現(xiàn)起來比較麻煩披粟,卻在某些領(lǐng)域確實(shí)有其價(jià)值,RISC 的做法就是將這些麻煩都交給軟件妆档,CISC 的做法則是像現(xiàn)在這樣: 由硬件設(shè)計(jì)完成僻爽。因此 RISC 指令集對編譯器要求很高,而 CISC 則很簡單贾惦。對編程人員的要求也類似胸梆。
3、循環(huán)流檢測
在解碼為 uop 之后 Nehalem 會將它們都存放在一個(gè)叫做 uop LSD Buffer 的緩存區(qū)须板。在Core 2 上碰镜,這個(gè) LSD Buffer 是出現(xiàn)在解碼器前方的,Nehalem 將其移動到解碼器后方习瑰,并相對加大了緩沖區(qū)的條目绪颖。Core 2 的 LSD 緩存區(qū)可以保存 18 個(gè) x86 指令而 Nehalem 可以保 存 28 個(gè) uop,從前文可以知道甜奄,大部分 x86 指令都可以解碼為一個(gè) uop柠横,少部分可以解碼 為 1~4 個(gè) uop,因此 Nehalem 的 LSD 緩沖區(qū)基本上可以相當(dāng)于保存 21~23 條x86 指令课兄,比 Core 2 要大上一些牍氛。
LSD 循環(huán)流監(jiān)測器也算包含在解碼部分,它的作用是: 假如程序使用的循環(huán)段(如 for..do/do..while 等)少于 28 個(gè) uops烟阐,那么 Nehalem 就可以將這個(gè)循環(huán)保存起來搬俊,不再需要重新通過取指單元、分支預(yù)測操作蜒茄,以及解碼器唉擂,Core 2 的 LSD 放在解碼器前方,因此無法省下解碼的工作檀葛。
Nehalem LSD 的工作比較像 NetBurst 架構(gòu)的 Trace Cache玩祟,其也是保存 uops屿聋,作用也是部分地去掉一些嚴(yán)重的循環(huán),不過由于 Trace Cache 還同時(shí)擔(dān)當(dāng)著類似于 Core/Nehalem 架構(gòu)的 Reorder Buffer 亂序緩沖區(qū)的作用,容量比較大(可以保存 12k uops黑忱,準(zhǔn)確的大小 是 20KB)甫煞,因此在 cache miss 的時(shí)候后果嚴(yán)重(特別是在 SMT 同步多線程之后抚吠,miss 率加 倍的情況下)楷力,LSD 的小數(shù)目設(shè)計(jì)顯然會好得多萧朝。不過筆者認(rèn)為 28 個(gè) uop 條目有些少,特 別是考慮到 SMT 技術(shù)帶來的兩條線程都同時(shí)使用這個(gè) LSD 的時(shí)候竖配。
在 LSD 之后用爪,Nehalem 將會進(jìn)行 Micro-ops Fusion项钮,這也是前端(The Front-End)的最后一個(gè)功能烁巫,在這些工作都做完之后亚隙,uops 就可以準(zhǔn)備進(jìn)入執(zhí)行引擎了诊霹。
4.亂序執(zhí)行指令階段(OoOE)
OoOE— Out-of-Order Execution 亂序執(zhí)行也是在 Pentium Pro 開始引入的脾还,它有些類似于多線程的概念入愧。亂序執(zhí)行是為了直接提升 ILP(Instruction Level Parallelism)指令級并行化的設(shè)計(jì)棺蛛,在多個(gè)執(zhí)行單元的超標(biāo)量設(shè)計(jì)當(dāng)中,一系列的執(zhí)行單元可以同時(shí)運(yùn)行一些沒有數(shù)據(jù)關(guān)聯(lián)性的若干指令桦踊,只有需要等待其他指令運(yùn)算結(jié)果的數(shù)據(jù)會按照順序執(zhí)行籍胯,從而總體提升了運(yùn)行效率芒炼。亂序執(zhí)行引擎是一個(gè)很重要的部分本刽,需要進(jìn)行復(fù)雜的調(diào)度管理。
首先笋除,在亂序執(zhí)行架構(gòu)中鲜屏,不同的指令可能都會需要用到相同的通用寄存器(GPR洛史,General Purpose Registers),特別是在指令需要改寫該通用寄存器的情況下——為了讓這些指令們能并行工作忆嗜,處理器需要準(zhǔn)備解決方法。一般的 RISC 架構(gòu)準(zhǔn)備了大量的GPR闪湾, 而x86 架構(gòu)天生就缺乏 GPR(x86具有8個(gè)GPR响谓,x86-64 具有 16 個(gè),一般 RISC 具有 32 個(gè)跋炕,IA64 則具有 128 個(gè)),為此 Intel 開始引入重命名寄存器(Rename Register)纠修,不同的指令可以通過具有名字相同但實(shí)際不同的寄存器來解決扣草。
此外辰妙,為了 SMT 同步多線程密浑,這些寄存器還要準(zhǔn)備雙份,每個(gè)線程具有獨(dú)立的一份懒构。
亂序執(zhí)行從Allocator定位器開始,Allocator 管理著RAT(Register Alias Table赞赖,寄存器別名表)、ROB(Re-Order Buffer辕近,重排序緩沖區(qū))和 RRF(Retirement Register File,退回寄存器文件)漏峰。在 Allocator 之前浅乔,流水線都是順序執(zhí)行的,在 Allocator 之后贤壁,就可以進(jìn)入亂序執(zhí)行階段了脾拆。在每一個(gè)線程方面,Nehalem 和 Core 2 架構(gòu)相似包帚,RAT 將重命名的渴邦、虛擬的寄存器(稱為 Architectural Register 或 Logical Register)指向ROB 或者RRF。RAT 是一式兩份,每個(gè)線程獨(dú)立瓮床,每個(gè) RAT 包含了 128 個(gè)重命名寄存器踢步。RAT 指向在 ROB 里面的最近的執(zhí)行寄存器狀態(tài)获印,或者指向RRF保存的最終的提交狀態(tài)兼丰。
ROB(Re-Order Buffer,重排序緩沖區(qū))是一個(gè)非常重要的部件蟆技,它是將亂序執(zhí)行完畢的指令們按照程序編程的原始順序重新排序的一個(gè)隊(duì)列旺聚,以保證所有的指令都能夠邏輯上實(shí)現(xiàn)正確的因果關(guān)系唧躲。打亂了次序的指令們(分支預(yù)測弄痹、硬件預(yù)取)依次插入這個(gè)隊(duì)列,當(dāng)一條指令通過 RAT 發(fā)往下一個(gè)階段確實(shí)執(zhí)行的時(shí)候這條指令(包括寄存器狀態(tài)在內(nèi))將被加入 ROB 隊(duì)列的一端蚓让,執(zhí)行完畢的指令(包括寄存器狀態(tài))將從 ROB 隊(duì)列的另一端移除(期間這些指令的數(shù)據(jù)可以被一些中間計(jì)算結(jié)果刷新)历极,因?yàn)檎{(diào)度器是 In-Order 順序的,這個(gè)隊(duì)列(ROB)也就是順序的锄列。從 ROB 中移出一條指令就意味著指令執(zhí)行完畢了琼稻,這個(gè)階段叫做 Retire 回退,相應(yīng)地 ROB 往往也叫做 Retirement Unit(回退單元)嘀掸,并將其畫為流水線的最后一部分睬塌。
在一些超標(biāo)量設(shè)計(jì)中,Retire 階段會將 ROB 的數(shù)據(jù)寫入 L1D 緩存(這是將MOB集成到ROB的情況)贪磺,而在另一些設(shè)計(jì)里劫映, 寫入 L1D 緩存由另外的隊(duì)列完成。例如祖今,Core/Nehalem 的這個(gè)操作就由 MOB(Memory Order Buffer,內(nèi)存重排序緩沖區(qū))來完成大渤。
ROB 是亂序執(zhí)行引擎架構(gòu)中都存在的一個(gè)緩沖區(qū)耕捞,重新排序指令的目的是將指令們的寄存器狀態(tài)依次提交到RRF退回寄存器文件當(dāng)中俺抽,以確保具有因果關(guān)系的指令們在亂序執(zhí)行中可以得到正確的數(shù)據(jù)。從執(zhí)行單元返回的數(shù)據(jù)會將先前由調(diào)度器加入ROB 的指令刷新數(shù)據(jù)部分并標(biāo)志為結(jié)束(Finished),再經(jīng)過其他檢查通過后才能標(biāo)志為完畢(Complete)侣颂,一旦標(biāo)志為完畢憔晒,它就可以提交數(shù)據(jù)并刪除重命名項(xiàng)目并退出ROB 了。提交狀態(tài)的工作由 Retirement Unit(回退單元)完成澎蛛,它將確實(shí)完畢的指令包含的數(shù)據(jù)寫入RRF(“確實(shí)” 的意思是,非猜測執(zhí)性、具備正確因果關(guān)系阴挣,程序可以見到的最終的寄存器狀態(tài))畔咧。和 RAT 一樣誓沸,RRF 也同時(shí)具有兩個(gè)拜隧,每個(gè)線程獨(dú)立。Core/Nehalem 的 Retirement Unit 回退單元每時(shí)鐘周期可以執(zhí)行 4 個(gè) uops 的寄存器文件寫入,和 RAT 每時(shí)鐘 4 個(gè) uops 的重命名一致洪添。
由于 ROB 里面保存的指令數(shù)目是如此之大(128 條目)垦页,因此一些人認(rèn)為它的作用是用來從中挑選出不相關(guān)的指令來進(jìn)入執(zhí)行單元,這多少是受到一些文檔中的 Out-of-Order Window 亂序窗口這個(gè)詞的影響(后面會看到ROB 會和 MOB 一起被計(jì)入亂序窗口資源中)干奢。
ROB 確實(shí)具有 RS 的一部分相似的作用薄啥,不過赘艳,ROB 里面的指令是調(diào)度器(dispacher)通過 RAT發(fā)往 RS 的同時(shí)發(fā)往ROB的(里面包含著正常順序的指令和猜測執(zhí)行的指令掰曾,但是亂序執(zhí)行并不是從ROB中亂序挑選的)秒梅,也就是說辆它,在“亂序”之前洞辣,ROB 的指令就已經(jīng)確定了。指令并不是在 ROB 當(dāng)中亂序挑選的(這是在RS當(dāng)中進(jìn)行)材原,ROB 擔(dān)當(dāng)?shù)氖橇魉€的最終階段: 一個(gè)指令的 Retire回退單元;以及擔(dān)當(dāng)中間計(jì)算結(jié)果的緩沖區(qū)威酒。
RS(Reservation Station箫津,中繼站): 等待源數(shù)據(jù)到來以進(jìn)行OoOE亂序執(zhí)行(沒有數(shù)據(jù)的指令將在 RS 等待)赡模, ROB(ReOrder Buffer欺缘,重排序緩沖區(qū)): 等待結(jié)果到達(dá)以進(jìn)行 Retire 指令回退 (沒有結(jié)果的指令將在 ROB等待)丛肢。
Nehalem 的 128 條目的 ROB 擔(dān)當(dāng)中間計(jì)算結(jié)果的緩沖區(qū)杠步,它保存著猜測執(zhí)行的指令及其數(shù)據(jù),猜測執(zhí)行允許預(yù)先執(zhí)行方向未定的分支指令。在大部分情況下外冀,猜測執(zhí)行工作良好——分支猜對了脑沿,因此其在 ROB 里產(chǎn)生的結(jié)果被標(biāo)志為已結(jié)束,可以立即地被后繼指令使用而不需要進(jìn)行 L1 Data Cache 的 Load 操作(這也是 ROB 的另一個(gè)重要用處,典型的 x86 應(yīng)用中 Load 操作是如此頻繁,達(dá)到了幾乎占 1/3 的地步,因此 ROB 可以避免大量的Cache Load 操作纳鼎,作用巨大)映九。在剩下的不幸的情況下,分支未能按照如期的情況進(jìn)行檬姥,這時(shí)猜測的分支指令段將被清除,相應(yīng)指令們的流水線階段清空崇堵,對應(yīng)的寄存器狀態(tài)也就全都無效了,這種無效的寄存器狀態(tài)不會也不能出現(xiàn)在 RRF 里面幸逆。
重命名技術(shù)并不是沒有代價(jià)的,在獲得前面所說的眾多的優(yōu)點(diǎn)之后电媳,它令指令在發(fā)射的時(shí)候需要掃描額外的地方來尋找到正確的寄存器狀態(tài)继阻,不過總體來說這種代價(jià)是非常值得的。RAT可以在每一個(gè)時(shí)鐘周期重命名 4 個(gè) uops 的寄存器,經(jīng)過重命名的指令在讀取到正確的操作數(shù)并發(fā)射到統(tǒng)一的RS(Reservation Station,中繼站,Intel 文檔翻譯為保留站點(diǎn)) 上。RS 中繼站保存了所有等待執(zhí)行的指令童叠。
和 Core 2 相比呢蛤,Nehalem 的 ROB 大小和 RS 大小都得到了提升励翼,ROB 重排序緩沖區(qū)從 96 條目提升到 128 條目(鼻祖 Pentium Pro 具有 40 條)敞葛,RS 中繼站從 32 提升到 36(Pentium Pro 為 20),它們都在兩個(gè)線程(超線程中的線程)內(nèi)共享策精,不過采用了不同的策略:ROB 是采用了靜態(tài)的分區(qū)方法舰始,而 RS 則采用了動態(tài)共享,因?yàn)橛袝r(shí)候會有一條線程內(nèi)的指令因 等待數(shù)據(jù)而停滯咽袜,這時(shí)另一個(gè)線程就可以獲得更多的 RS 資源丸卷。停滯的指令不會發(fā)往 RS,但是仍然會占用 ROB 條目酬蹋。由于 ROB 是靜態(tài)分區(qū)及老,因此在開啟 HTT 的情況下,每一個(gè)線程只能 分到 64 條范抓,不算多骄恶,在一些極少數(shù)的應(yīng)用上,我們應(yīng)該可以觀察到一些應(yīng)用開啟 HTT 后會 速度降低匕垫,盡管可能非常微小僧鲁。
5、執(zhí)行單元
在為 SMT 做好準(zhǔn)備工作并打亂指令的執(zhí)行順序之后(指的是分支預(yù)測、硬件預(yù)饶骸)斟叼,uops 通過每時(shí)鐘周期 4 條的速度進(jìn)入 Reservation Station 中繼站(保留站),總共 36 條目的中繼站 uops 就開始等待超標(biāo)量(Superscaler)執(zhí)行引擎亂序執(zhí)行了春寿。自從 Pentium 開始朗涩,Intel 就開始在處理器里面采用了超標(biāo)量設(shè)計(jì)(Pentium 是兩路超標(biāo)量處理器),超標(biāo)量的意思就是多個(gè)執(zhí)行單元绑改,它可以同時(shí)執(zhí)行多條沒有相互依賴性的指令谢床,從而達(dá)到提升 ILP 指令級并行化的目的。Nehalem 具備 6 個(gè)執(zhí)行端口厘线,每個(gè)執(zhí)行端口具有多個(gè)不同的單元以執(zhí)行不同的任務(wù)识腿,然而同一時(shí)間只能有一條指令(uop)進(jìn)入執(zhí)行端口,因此也可以認(rèn)為 Nehalem 有 6 個(gè)“執(zhí)行單元”造壮,在每個(gè)時(shí)鐘周期內(nèi)可以執(zhí)行最多 6 個(gè)操作(或者說渡讼,6 條指令),和 Core 一樣耳璧。
36 條目的中繼站指令在分發(fā)器的管理下成箫,挑選出盡量多的可以同時(shí)執(zhí)行的指令(也就是亂序執(zhí)行的意思)——最多 6 條——發(fā)送到執(zhí)行端口。 這些執(zhí)行端口并不都是用于計(jì)算旨枯,實(shí)際上伟众,有三個(gè)執(zhí)行端口是專門用來執(zhí)行內(nèi)存相關(guān)的操作的,只有剩下的三個(gè)是計(jì)算端口召廷,因此凳厢,在這一點(diǎn)上 Nehalem 實(shí)際上是跟 Core 架構(gòu)一 樣的,這也可以解釋為什么有些情況下竞慢,Nehalem 和 Core 相比沒有什么性能提升先紫。
計(jì)算操作分為兩種: 使用 ALU(Arithmetic Logic Unit,算術(shù)邏輯單元)的整數(shù)(Integer) 運(yùn)算和使用 FPU(Floating Point Unit筹煮,浮點(diǎn)運(yùn)算單元)的浮點(diǎn)(Floating Point)運(yùn)算遮精。SSE 指令(包括 SSE1 到 SSE4)是一種特例,它雖然有整數(shù)也有浮點(diǎn)败潦,然而它們使用的都是 128bit 浮點(diǎn)寄存器本冲,使用的也大部分是 FPU 電路。在 Nehalem 中劫扒,三個(gè)計(jì)算端口都可以做整數(shù)運(yùn)算(包括 MMX)或者SSE 運(yùn)算(浮點(diǎn)運(yùn)算不太一樣檬洞,只有兩個(gè)端口可以進(jìn)行浮點(diǎn) ADD 和 MUL/DIV 運(yùn)算,因此每時(shí)鐘周期最多進(jìn)行 2 個(gè)浮點(diǎn)計(jì)算沟饥,這也是目前 Intel 處理器浮點(diǎn)性能不如整數(shù)性能突出的原因)添怔,不過每一個(gè)執(zhí)行端口都不是完全一致:只有端口 0 有浮點(diǎn)乘和除功能湾戳,只有端口 5 有分支能力(這個(gè)執(zhí)行單元將會與分支預(yù)測單元連接),其他 FP/SSE 能力也不盡相同广料,這些不對稱之處都由統(tǒng)一的分發(fā)器來理解砾脑,并進(jìn)行指令的調(diào)度管理。沒有采用完全對稱的設(shè)計(jì)可能是基于統(tǒng)計(jì)學(xué)上的考慮艾杏。和 Core 一樣韧衣,Nehalem 的也沒有采用 Pentium 4 那樣的 2 倍頻的 ALU 設(shè)計(jì)(在 Pentium 4购桑,ALU 的運(yùn)算頻率是 CPU 主頻的兩倍元旬, 因此整數(shù)性能明顯要比浮點(diǎn)性能突出)耗帕。
不幸的是,雖然可以同時(shí)執(zhí)行的指令很多,然而在流水線架構(gòu)當(dāng)中運(yùn)行速度并不是由最 “寬”的單元來決定的,而是由最“窄”的單元來決定的雳殊。這就是木桶原理箫措,Opteron的解碼器后端只能每時(shí)鐘周期輸出 3 條 uops友驮,而 Nehalem/Core2 則能輸出 4 條耻瑟,因此它們的實(shí)際最大每時(shí)鐘運(yùn)行指令數(shù)是 3/4框都,而不是 6。同樣地餐塘,多少路超標(biāo)量在這些亂序架構(gòu)處理器中也不再按照運(yùn)算單元來劃分不翩,Core Duo 及之前(到 Pentium Pro 為止)均為三路超標(biāo)量處理器,Core 2/Nehalem 則為四路超標(biāo)量處理器疆瑰“饶可見在微架構(gòu)上,Nehalem/Core 顯然是 要比其他處理器快一些减响。順便說一下靖诗,這也是 Intel 在超線程示意圖中,使用 4 個(gè)寬度的方 塊來表示而不是 6 個(gè)方塊的原因支示。
6刊橘、存取單元
運(yùn)算需要用到數(shù)據(jù),也會生成數(shù)據(jù)颂鸿,這些數(shù)據(jù)存取操作就是存取單元所做的事情促绵,實(shí)際 上,Nehalem 和 Core 的存取單元沒什么變化嘴纺,仍然是 3 個(gè)败晴。
這三個(gè)存取單元中,一個(gè)用于所有的 Load 操作(地址和數(shù)據(jù))栽渴,一個(gè)用于 Store 地址尖坤,一個(gè)用于 Store 數(shù)據(jù),前兩個(gè)數(shù)據(jù)相關(guān)的單元帶有 AGU(Address Generation Unit闲擦,地址生成單元)功能(NetBurst架構(gòu)使用快速 ALU 來進(jìn)行地址生成)慢味。
在亂序架構(gòu)中场梆,存取操作也可以打亂進(jìn)行。類似于指令預(yù)取一樣纯路,Load/Store 操作也可以提前進(jìn)行以降低延遲的影響或油,提高性能。然而感昼,由于Store操作會修改數(shù)據(jù)影響后繼的Load 操作装哆,而指令卻不會有這種問題(寄存器依賴性問題通過ROB解決),因此數(shù)據(jù)的亂序操作更為復(fù)雜定嗓。
如上圖所示蜕琴,第一條 ALU 指令的運(yùn)算結(jié)果要 Store 在地址 Y(第二條指令),而第九條 指令是從地址 Y Load 數(shù)據(jù)宵溅,顯然在第二條指令執(zhí)行完畢之前凌简,無法移動第九條指令,否則將會產(chǎn)生錯(cuò)誤的結(jié)果恃逻。同樣雏搂,如果CPU也不知道第五條指令會使用什么地址,所以它也無法確定是否可以把第九條指令移動到第五條指令附近寇损。
內(nèi)存數(shù)據(jù)相依性預(yù)測功能(Memory Disambiguation)可以預(yù)測哪些指令是具有依賴性的或者使用相關(guān)的地址(地址混淆凸郑,Alias),從而決定哪些 Load/Store 指令是可以提前的矛市, 哪些是不可以提前的芙沥。可以提前的指令在其后繼指令需要數(shù)據(jù)之前就開始執(zhí)行浊吏、讀取數(shù)據(jù)到ROB當(dāng)中而昨,這樣后繼指令就可以直接從中使用數(shù)據(jù),從而避免訪問了無法提前 Load/Store 時(shí)訪問 L1 緩存帶來的延遲(3~4 個(gè)時(shí)鐘周期)找田。
不過歌憨,為了要判斷一個(gè) Load 指令所操作的地址沒有問題,緩存系統(tǒng)需要檢查處于 in-flight 狀態(tài)(處理器流水線中所有未執(zhí)行的指令)的 Store 操作墩衙,這是一個(gè)頗耗費(fèi)資源的過程务嫡。在 NetBurst 微架構(gòu)中,通過把一條 Store 指令分解為兩個(gè) uops——一個(gè)用于計(jì)算地址漆改、一個(gè)用于真正的存儲數(shù)據(jù)植袍,這種方式可以提前預(yù)知 Store 指令所操作的地址,初步的解決了數(shù)據(jù)相依性問題籽懦。在 NetBurst 微架構(gòu)中于个,Load/Store 亂序操作的算法遵循以下幾條 原則:
如果一個(gè)對于未知地址進(jìn)行操作的 Store 指令處于 in-flight 狀態(tài),那么所有的 Load 指令都要被延遲
在操作相同地址的 Store 指令之前 Load 指令不能繼續(xù)執(zhí)行
一個(gè) Store 指令不能移動到另外一個(gè) Store 指令之前(指的是在RS中不能先挑選執(zhí)行后面的一條store指令暮顺,注意這只是說某一種架構(gòu)不允許重排store厅篓,其實(shí)還是有很多架構(gòu)如Alpha等是松散內(nèi)存模型秀存,允許不相關(guān)的store重排序的.)
這種原則下的問題也很明顯,比如第一條原則會在一條處于等待狀態(tài)的 Store 指令所操作的地址未確定之前羽氮,就延遲所有的 Load 操作或链,顯然過于保守了。實(shí)際上档押,地址沖突問題是極少發(fā)生的澳盐。根據(jù)某些機(jī)構(gòu)的研究,在一個(gè)Alpha EV6 處理器中最多可以允許 512 條指令處于 in-flight 狀態(tài)令宿,但是其中的 97%以上的 Load 和 Store 指令都不會存在地址沖突問題叼耙。
基于這種理念,Core 微架構(gòu)采用了大膽的做法粒没,它令 Load 指令總是提前進(jìn)行筛婉,除非新加入的動態(tài)混淆預(yù)測器(Dynamic Alias Predictor)預(yù)測到了該 Load 指令不能被移動到 Store 指令附近婿脸。這個(gè)預(yù)測是根據(jù)歷史行為來進(jìn)行的唾糯,據(jù)說準(zhǔn)確率超過 90%。
在執(zhí)行了預(yù) Load 之后归敬,一個(gè)沖突監(jiān)測器會掃描 MOB 的 Store 隊(duì)列响蓉,檢查該是否有Store操作與該 Load 沖突硕勿。在很不幸的情況下(1%~2%),發(fā)現(xiàn)了沖突枫甲,那么該 Load 操作作廢源武、 流水線清除并重新進(jìn)行 Load 操作。這樣大約會損失 20 個(gè)時(shí)鐘周期的時(shí)間言秸,然而從整體上看软能, Core 微架構(gòu)的激進(jìn) Load/Store 亂序策略確實(shí)很有效地提升了性能迎捺,因?yàn)長oad 操作占據(jù)了通常程序的 1/3 左右举畸,并且 Load 操作可能會導(dǎo)致巨大的延遲(在命中的情況下,Core 的 L1D Cache 延遲為 3 個(gè)時(shí)鐘周期凳枝,Nehalem 則為 4 個(gè)抄沮。L1 未命中時(shí)則會訪問 L2 緩存,一般為 10~12 個(gè)時(shí)鐘周期岖瑰。訪問 L3 通常需要 30~40 個(gè)時(shí)鐘周期叛买,訪問主內(nèi)存則可以達(dá)到最多約 100 個(gè)時(shí)鐘周期)。Store 操作并不重要蹋订,什么時(shí)候?qū)懭氲?L1 乃至主內(nèi)存并不會影響到執(zhí)行性能率挣。
如上圖所示,我們需要載入地址 X 的數(shù)據(jù)露戒,加 1 之后保存結(jié)果;載入地址 Y 的數(shù)據(jù)椒功,加1 之后保存結(jié)果;載入地址 Z 的數(shù)據(jù)捶箱,加 1 之后保存結(jié)果。如果根據(jù) Netburst 的基本準(zhǔn)則动漾, 在第三條指令未決定要存儲在什么地址之前丁屎,處理器是不能移動第四條指令和第七條指令的。實(shí)際上旱眯,它們之間并沒有依賴性晨川。因此,Core 微架構(gòu)中則“大膽”的將第四條指令和第七條指令分別移動到第二和第三指令的并行位置删豺,這種行為是基于一定的猜測的基礎(chǔ)上的“投機(jī)”行為共虑,如果猜測的對的話(幾率在 90%以上),完成所有的運(yùn)算只要5個(gè)周期吼鳞,相比之前的9個(gè)周期幾乎快了一倍看蚜。
和為了順序提交到寄存器而需要 ROB 重排序緩沖區(qū)的存在一樣,在亂序架構(gòu)中赔桌,多個(gè)打亂了順序的 Load 操作和Store操作也需要按順序提交到內(nèi)存供炎,MOB(Memory Reorder Buffer, 內(nèi)存重排序緩沖區(qū))就是起到這樣一個(gè)作用的重排序緩沖區(qū)(介于 Load/Store 單元 與 L1D Cache 之間的部件疾党,有時(shí)候也稱之為LSQ)音诫,MOB 通過一個(gè) 128bit 位寬的 Load 通道與一個(gè) 128bit 位寬的 Store 通道與雙口 L1D Cache 通信。和 ROB 一樣雪位,MOB的內(nèi)容按照 Load/Store 指令實(shí)際的順序加入隊(duì)列的一端竭钝,按照提交到 L1 DCache 的順序從隊(duì)列的另一端移除。ROB 和 MOB 一起實(shí)際上形成了一個(gè)分布式的 Order Buffer 結(jié)構(gòu)雹洗,有些處理器上只存在 ROB香罐,兼?zhèn)淞?MOB 的功能(把MOB看做ROB的一部分可能更好理解)。
和ROB 一樣时肿,Load/Store 單元的亂序存取操作會在 MOB 中按照原始程序順序排列庇茫,以提供正確的數(shù)據(jù),內(nèi)存數(shù)據(jù)依賴性檢測功能也在里面實(shí)現(xiàn)(內(nèi)存數(shù)據(jù)依賴性的檢測比指令寄存器間的依賴性檢測要復(fù)雜的多)螃成。MOB 的 Load/Store 操作結(jié)果也會直接反映到 ROB當(dāng)中(中間結(jié)果)旦签。
MOB還附帶了數(shù)據(jù)預(yù)取(Data Prefetch)功能,它會猜測未來指令會使用到的數(shù)據(jù)寸宏,并預(yù)先從L1D Cache 緩存 Load入MOB 中(Data Prefetcher 也會對 L2 至系統(tǒng)內(nèi)存的數(shù)據(jù)進(jìn)行這樣的操作)宁炫, 這樣 MOB 當(dāng)中的數(shù)據(jù)有些在 ROB 中是不存在的(這有些像 ROB 當(dāng)中的 Speculative Execution 猜測執(zhí)行,MOB 當(dāng)中也存在著“Speculative Load Execution 猜測載入”氮凝,只不過失敗的猜測執(zhí)行會導(dǎo)致管線停頓羔巢,而失敗的猜測載入僅僅會影響到性能,然而前端時(shí)間發(fā)生的Meltdown漏洞卻造成了嚴(yán)重的安全問題)。MOB包括了Load Buffers和Store Buffers竿秆。
亂序執(zhí)行中我們可以看到很多緩沖區(qū)性質(zhì)的東西: RAT 寄存器別名表炭臭、ROB 重排序緩沖 區(qū)、RS 中繼站袍辞、MOB 內(nèi)存重排序緩沖區(qū)(包括 load buffer 載入緩沖和 store buffer 存儲緩沖)鞋仍。在超線程的作 用下,RAT是一式兩份搅吁,包含了 128 個(gè)重命名寄存器; 128 條目的 ROB威创、48 條目的 LB 和 32 條目的 SB 都 每個(gè)線程 64 個(gè) ROB、24 個(gè) LB 和 16 個(gè) SB; RS 則是在兩個(gè)線程中動態(tài)共享谎懦《遣颍可見,雖然整體數(shù)量增加了界拦,然而就單個(gè)線程而言吸申,獲得的資源并沒有 提升。這會影響到 HTT 下單線程下的性能享甸。
六截碴、緩存(cache)
通常緩存具有兩種設(shè)計(jì):非獨(dú)占和獨(dú)占,Nehalem 處理器的 L3 采用了非獨(dú)占高速緩存 設(shè)計(jì)(或者說“包含式”蛉威,L3 包含了 L1/L2 的內(nèi)容)日丹,這種方式在 Cache Miss 的時(shí)候比獨(dú) 占式具有更好的性能,而在緩存命中的時(shí)候需要檢查不同的核心的緩存一致性蚯嫌。Nehalem 并 采用了“內(nèi)核有效”數(shù)據(jù)位的額外設(shè)計(jì)哲虾,降低了這種檢查帶來的性能影響。隨著核心數(shù)目的 逐漸增多(多線程的加入也會增加 Cache Miss 率)择示,對緩存的壓力也會繼續(xù)增大束凑,因此這 種方式會比較符合未來的趨勢。在后面可以看到栅盲,這種設(shè)計(jì)也是考慮到了多處理器協(xié)作的情況(此時(shí) Miss 率會很容易地增加)汪诉。這可以看作是 Nehalem 與以往架構(gòu)的基礎(chǔ)不同:之前的架構(gòu)都是來源于移動處理設(shè)計(jì),而 Nehalem 則同時(shí)為企業(yè)剪菱、桌面和移動考慮而設(shè)計(jì)摩瞎。
在 L3 緩存命中的時(shí)候(單處理器上是最通常的情況拴签,多處理器下則不然)孝常,處理器檢查內(nèi)核有效位看看是否其他內(nèi)核也有請求的緩存頁面內(nèi)容,決定是否需要對內(nèi)核進(jìn)行偵聽蚓哩。
在NUMA架構(gòu)中构灸,多個(gè)處理器中的同一個(gè)緩存頁面必定在其中一個(gè)處理器中屬于 F 狀態(tài)(可以修改的狀態(tài)),這個(gè)頁面在這個(gè)處理器中沒有理由不可以多核心共享(可以多核心共享就意味著這個(gè)能進(jìn)入修改狀態(tài)的頁面的多個(gè)有效位被設(shè)置為一)岸梨。MESIF協(xié)議應(yīng)該是工作在核心(L1+L2)層面而不是處理器(L3)層面喜颁,這樣同一處理器里多個(gè)核心共享的頁面稠氮,只有其中一個(gè)是出于 F 狀態(tài)(可以修改的狀態(tài))。見后面對 NUMA 和 MESIF 的解析半开。(L1/L2/L3 的同步應(yīng)該是不需要 MESIF 的同步機(jī)制)
在 L3 緩存未命中的時(shí)候(多處理器下會頻繁發(fā)生)隔披,處理器決定進(jìn)行內(nèi)存存取,按照 頁面的物理位置寂拆,它分為近端內(nèi)存存取(本地內(nèi)存空間)和遠(yuǎn)端內(nèi)存存取(地址在其他處理 器的內(nèi)存的空間):
七奢米、緩存Cache架構(gòu)原理
Cache的容量很小,它保存的內(nèi)容只是主存內(nèi)容的一個(gè)子集纠永,且Cache與主存的數(shù)據(jù)交換是以塊為單位的鬓长。為了把信息放到Cache中,必須應(yīng)用某種函數(shù)把主存地址定位到Cache中尝江,這稱為地址映射涉波。在信息按這種映射關(guān)系裝入Cache后,CPU執(zhí)行程序時(shí)炭序,會將程序中的主存地址變換成Cache地址啤覆,這個(gè)變換過程叫做地址變換。
Cache的地址映射方式有直接映射惭聂、全相聯(lián)映射和組相聯(lián)映射城侧。假設(shè)某臺計(jì)算機(jī)主存容量為l MB,被分為2048塊彼妻,每塊512B嫌佑;Cache容量為8KB,被分為16塊侨歉,每塊也是512B屋摇。下面以此為例介紹三種基本的地址映射方法。
直接映射
直接映射的Cache組織如圖3-14所示幽邓。主存中的一個(gè)塊只能映射到Cache的某一特定塊中去炮温。例如,主存的第0塊牵舵、第16塊柒啤、……、第2032塊畸颅,只能映射到Cache的第0塊担巩;而主存的第1塊、第17塊没炒、……涛癌、第2033塊,只能映射到Cache的第1塊……。
直接映射是最簡單的地址映射方式拳话,它的硬件簡單先匪,成本低,地址變換速度快弃衍,而且不涉及替換算法問題呀非。但是這種方式不夠靈活,Cache的存儲空間得不到充分利用镜盯,每個(gè)主存塊只有一個(gè)固定位置可存放姜钳,容易產(chǎn)生沖突,使Cache效率下降形耗,因此只適合大容量Cache采用哥桥。例如,如果一個(gè)程序需要重復(fù)引用主存中第0塊與第16塊激涤,最好將主存第0塊與第16塊同時(shí)復(fù)制到Cache中拟糕,但由于它們都只能復(fù)制到Cache的第0塊中去,即使Cache中別的存儲空間空著也不能占用倦踢,因此這兩個(gè)塊會不斷地交替裝入Cache中送滞,導(dǎo)致命中率降低。
全相聯(lián)映射
圖3-15 是全相聯(lián)映射的Cache組織辱挥,主存中任何一塊都可以映射到Cache中的任何一塊位置上犁嗅。
全相聯(lián)映射方式比較靈活,主存的各塊可以映射到Cache的任一塊中晤碘,Cache的利用率高褂微,塊沖突概率低,只要淘汰Cache中的某一塊园爷,即可調(diào)入主存的任一塊宠蚂。但是,由于Cache比較電路的設(shè)計(jì)和實(shí)現(xiàn)比較困難童社,這種方式只適合于小容量Cache采用求厕。
組相聯(lián)映射
組相聯(lián)映射實(shí)際上是直接映射和全相聯(lián)映射的折中方案,其組織結(jié)構(gòu)如圖3-16所示扰楼。主存和Cache都分組呀癣,主存中一個(gè)組內(nèi)的塊數(shù)與Cache中的分組數(shù)相同,組間采用直接映射弦赖,組內(nèi)采用全相聯(lián)映射项栏。也就是說,將Cache分成u組腾节,每組v塊忘嫉,主存塊存放到哪個(gè)組是固定的,至于存到該組哪一塊則是靈活的案腺。例如庆冕,主存分為256組,每組8塊劈榨,Cache分為8組访递,每組2塊。
主存中的各塊與Cache的組號之間有固定的映射關(guān)系同辣,但可自由映射到對應(yīng)Cache組中的任何一塊拷姿。例如,主存中的第0塊旱函、第8塊……均映射于Cache的第0組响巢,但可映射到Cache第0組中的第0塊或第1塊;主存的第1塊棒妨、第9塊……均映射于Cache的第1組踪古,但可映射到Cache第1組中的第2塊或第3塊。
常采用的組相聯(lián)結(jié)構(gòu)Cache券腔,每組內(nèi)有2伏穆、4、8纷纫、16塊枕扫,稱為2路、4路辱魁、8路烟瞧、16路組相聯(lián)Cache。組相聯(lián)結(jié)構(gòu)Cache是前兩種方法的折中方案染簇,適度兼顧二者的優(yōu)點(diǎn)燕刻,盡量避免二者的缺點(diǎn),因而得到普遍采用剖笙。
一次內(nèi)存訪問示意圖
注意事項(xiàng)
TLB采用組相聯(lián)
頁表采用兩級頁表
cache采用組相聯(lián)
cache僅考慮L1 d-cache卵洗,不考慮L1 i-cache、L2 cache和L3 cache
未考慮頁表缺頁
簡化了cache未命中情況
實(shí)際例子
下面展示了現(xiàn)代Intel處理器的CPU cache是如何組織的弥咪。有關(guān)cache的討論往往缺乏具體的實(shí)例过蹂,使得一些簡單的概念變得撲朔迷離。也許是我可愛的小腦瓜有點(diǎn)遲鈍吧聚至,但不管怎樣酷勺,至少下面講述了故事的前一半,即Core 2的 L1 cache是如何被訪問的:
1. 由索引揀選緩存組(行)
在cache中的數(shù)據(jù)是以緩存線(line)為單位組織的扳躬,一條緩存線對應(yīng)于內(nèi)存中一個(gè)連續(xù)的字節(jié)塊脆诉。這個(gè)cache使用了64字節(jié)的緩存線甚亭。這些線被保存在cache bank中,也叫路(way)击胜。每一路都有一個(gè)專門的目錄(directory)用來保存一些登記信息亏狰。你可以把每一路連同它的目錄想象成電子表格中的一列,而表的一行構(gòu)成了cache的一組(set)偶摔。列中的每一個(gè)單元(cell)都含有一條緩存線暇唾,由與之對應(yīng)的目錄單元跟蹤管理。圖中的cache有64 組辰斋、每組8路策州,因此有512個(gè)含有緩存線的單元,合計(jì)32KB的存儲空間宫仗。
在cache眼中够挂,物理內(nèi)存被分割成了許多4KB大小的物理內(nèi)存頁(page)。每一頁都含有4kb/64/bytes== 64條緩存線藕夫。在一個(gè)4KB的頁中下硕,第0到63字節(jié)是第一條緩存線,第64到127字節(jié)是第二條緩存線汁胆,以此類推梭姓。每一頁都重復(fù)著這種劃分,所以第0頁第3條緩存線與第1頁第3條緩存線是不同的嫩码。
在全相聯(lián)緩存(fully associative cache)中誉尖,內(nèi)存中的任意一條緩存線都可以被存儲到任意的緩存單元中。這種存儲方式十分靈活铸题,但也使得要訪問它們時(shí)铡恕,檢索緩存單元的工作變得復(fù)雜、昂貴丢间。由于L1和L2 cache工作在很強(qiáng)的約束之下探熔,包括功耗,芯片物理空間烘挫,存取速度等诀艰,所以在多數(shù)情況下,使用全相聯(lián)緩存并不是一個(gè)很好的折中饮六。
取而代之的是圖中的組相聯(lián)緩存(set associative cache)其垄。意思是,內(nèi)存中一條給定的緩存線只能被保存在一個(gè)特定的組(或行)中卤橄。所以绿满,任意物理內(nèi)存頁的第0條緩存線(頁內(nèi)第0到63字節(jié))必須存儲到第0組,第1條緩存線存儲到第1組窟扑,以此類推喇颁。每一組有8個(gè)單元可用于存儲它所關(guān)聯(lián)的緩存線漏健,從而形成一個(gè)8路關(guān)聯(lián)的組(8-way associative set)。當(dāng)訪問一個(gè)內(nèi)存地址時(shí)橘霎,地址的第6到11位(譯注:組索引)指出了在4KB內(nèi)存頁中緩存線的編號蔫浆,從而決定了即將使用的緩存組。舉例來說茎毁,物理地址0x800010a0的組索引是000010克懊,所以此地址的內(nèi)容一定是在第2組中緩存的忱辅。
但是還有一個(gè)問題七蜘,就是要找出一組中哪個(gè)單元包含了想要的信息,如果有的話墙懂。這就到了緩存目錄登場的時(shí)刻橡卤。每一個(gè)緩存線都被其對應(yīng)的目錄單元做了標(biāo)記(tag);這個(gè)標(biāo)記就是一個(gè)簡單的內(nèi)存頁編號损搬,指出緩存線來自于哪一頁碧库。由于處理器可以尋址64GB的物理RAM,所以總共有64GB/4KB == 224個(gè)內(nèi)存頁巧勤,需要24位來保存標(biāo)記嵌灰。前例中的物理地址0x800010a0對應(yīng)的頁號為524289。下面是故事的后一半:
2颅悉、在組中搜索匹配標(biāo)記
由于我們只需要去查看某一組中的8路沽瞭,所以查找匹配標(biāo)記是非常迅速的;事實(shí)上剩瓶,從電學(xué)角度講驹溃,所有的標(biāo)記是同時(shí)進(jìn)行比對的,我用箭頭來表示這一點(diǎn)延曙。如果此時(shí)正好有一條具有匹配標(biāo)簽的有效緩存線豌鹤,我們就獲得一次緩存命中(cache hit)。否則枝缔,這個(gè)請求就會被轉(zhuǎn)發(fā)的L2 cache布疙,如果還沒匹配上就再轉(zhuǎn)發(fā)給主系統(tǒng)內(nèi)存。通過應(yīng)用各種調(diào)節(jié)尺寸和容量的技術(shù)愿卸,Intel給CPU配置了較大的L2 cache拐辽,但其基本的設(shè)計(jì)都是相同的。比如擦酌,你可以將原先的緩存增加8路而獲得一個(gè)64KB的緩存俱诸;再將組數(shù)增加到4096,每路可以存儲256kb赊舶。經(jīng)過這兩次修改睁搭,就得到了一個(gè)4MB的L2 cache赶诊。在此情況下,需要18位來保存標(biāo)記园骆,12位保存組索引舔痪;緩存所使用的物理內(nèi)存頁的大小與其一路的大小相等。(譯注:有4096組锌唾,就需要lg(4096)==12位的組索引锄码,緩存線依然是64字節(jié),所以一路有4096*64B==256KB字節(jié)晌涕;在L2 cache眼中滋捶,內(nèi)存被分割為許多256KB的塊,所以需要lg(64GB/256KB)==18位來保存標(biāo)記余黎。)
如果有一組已經(jīng)被放滿了重窟,那么在另一條緩存線被存儲進(jìn)來之前,已有的某一條則必須被騰空(evict)惧财。為了避免這種情況巡扇,對運(yùn)算速度要求較高的程序就要嘗試仔細(xì)組織它的數(shù)據(jù),使得內(nèi)存訪問均勻的分布在已有的緩存線上垮衷。舉例來說厅翔,假設(shè)程序中有一個(gè)數(shù)組,元素的大小是512字節(jié)搀突,其中一些對象在內(nèi)存中相距4KB刀闷。這些對象的各個(gè)字段都落在同一緩存線上,并競爭同一緩存組描姚。如果程序頻繁的訪問一個(gè)給定的字段(比如涩赢,通過虛函數(shù)表vtable調(diào)用虛函數(shù)),那么這個(gè)組看起來就好像一直是被填滿的轩勘,緩存開始變得毫無意義筒扒,因?yàn)榫彺婢€一直在重復(fù)著騰空與重新載入的步驟。在我們的例子中绊寻,由于組數(shù)的限制花墩,L1 cache僅能保存8個(gè)這類對象的虛函數(shù)表。這就是組相聯(lián)策略的折中所付出的代價(jià):即使在整體緩存的使用率并不高的情況下澄步,由于組沖突冰蘑,我們還是會遇到緩存缺失的情況。然而村缸,鑒于計(jì)算機(jī)中各個(gè)存儲層次的相對速度祠肥,不管怎么說,大部分的應(yīng)用程序并不必為此而擔(dān)心梯皿。
一個(gè)內(nèi)存訪問經(jīng)常由一個(gè)線性(或虛擬)地址發(fā)起仇箱,所以L1 cache需要依賴分頁單元(paging unit)來求出物理內(nèi)存頁的地址县恕,以便用于緩存標(biāo)記。與此相反剂桥,組索引來自于線性地址的低位忠烛,所以不需要轉(zhuǎn)換就可以使用了(在我們的例子中為第6到11位)。因此L1 cache是物理標(biāo)記但虛擬索引的(physically tagged but virtually indexed)权逗,從而幫助CPU進(jìn)行并行的查找操作美尸。因?yàn)長1 cache的一路絕不會比MMU的一頁還大,所以可以保證一個(gè)給定的物理地址位置總是關(guān)聯(lián)到同一組斟薇,即使組索引是虛擬的师坎。在另一方面L2 cache必須是物理標(biāo)記和物理索引的,因?yàn)樗囊宦繁萂MU的一頁要大奔垦。但是屹耐,當(dāng)一個(gè)請求到達(dá)L2 cache時(shí)尸疆,物理地址已經(jīng)被L1 cache準(zhǔn)備(resolved)完畢了椿猎,所以L2 cache會工作得很好。
最后寿弱,目錄單元還存儲了對應(yīng)緩存線的狀態(tài)(state)犯眠。在L1代碼緩存中的一條緩存線要么是無效的(invalid)要么是共享的(shared,意思是有效的症革,真的J)筐咧。在L1數(shù)據(jù)緩存和L2緩存中,一條緩存線可以為4個(gè)MESI狀態(tài)之一:被修改的(modified)噪矛,獨(dú)占的(exclusive)量蕊,共享的(shared),無效的(invalid)艇挨。Intel緩存是包容式的(inclusive):L1緩存的內(nèi)容會被復(fù)制到L2緩存中残炮。
總結(jié)
內(nèi)存層次結(jié)構(gòu)的意義在于利用引用的空間局部性和時(shí)間局部性原理,將經(jīng)常被訪問的數(shù)據(jù)放到快速的存儲器中缩滨,而將不經(jīng)常訪問的數(shù)據(jù)留在較慢的存儲器中势就。
一般情況下,除了寄存器和L1緩存可以操作指定字長的數(shù)據(jù)脉漏,下層的內(nèi)存子系統(tǒng)就不會再使用這么小的單位了苞冯,而是直接移動數(shù)據(jù)塊,比如以緩存線為單位訪問數(shù)據(jù)侧巨。
對于組沖突舅锄,可以這么理解:與上文相似,假設(shè)一個(gè)緩存司忱,由512條緩存線組成皇忿,每條線64字節(jié)碉怔,容量32KB。
假如它是直接映射緩存禁添,由于它往往使用地址的低位直接映射緩存線編號撮胧,所以所有的32K倍數(shù)的地址(32K,64K老翘,96K等)都會映射到同一條線上(即第0線)芹啥。假如程序的內(nèi)存組織不當(dāng),交替的去訪問布置在這些地址的數(shù)據(jù)铺峭,則會導(dǎo)致沖突墓怀。從外表看來就好像緩存只有1條線了,盡管其他緩存線一直是空閑著的卫键。
如果是全相聯(lián)緩存傀履,那么每條緩存線都是獨(dú)立的,可以對應(yīng)于內(nèi)存中的任意緩存線莉炉。只有當(dāng)所有的512條緩存線都被占滿后才會出現(xiàn)沖突钓账。
組相聯(lián)是前兩者的折中,每一路中的緩存線采用直接映射方式絮宁,而在路與路之間梆暮,緩存控制器使用全相聯(lián)映射算法,決定選擇一組中的哪一條線绍昂。
如果是2路組相聯(lián)緩存啦粹,那么這512條緩存線就被分為了2路,每路256條線窘游,一路16KB唠椭。此時(shí)所有為16K整數(shù)倍的地址(16K,32K忍饰,48K等)都會映射到第0線贪嫂,但由于2路是關(guān)聯(lián)的,所以可以同時(shí)有2個(gè)這種地址的內(nèi)容被緩存喘批,不會發(fā)生沖突撩荣。當(dāng)然了,如果要訪問第三個(gè)這種地址饶深,還是要先騰空已有的一條才行餐曹。所以極端情況下,從外表看來就好像緩存只有2條線了敌厘,盡管其他緩存線一直是空閑著的台猴。
如果是8路組相聯(lián)緩存(與文中示例相同),那么這512條緩存線就被分為了8路,每路64條線饱狂,一路4KB曹步。所以如果數(shù)組中元素地址是4K對齊的,并且程序交替的訪問這些元素休讳,就會出現(xiàn)組沖突讲婚。從外表看來就好像緩存只有8條線了,盡管其他緩存線一直是空閑著的俊柔。
根據(jù)內(nèi)存計(jì)算cacde結(jié)構(gòu):
page是os的概念筹麸,而cache是cpu的概念。虛擬地址和物理地址以page為單位進(jìn)行操作的雏婶,由兩部分組成:page地址和page內(nèi)地址(偏移)物赶,所以,os的page和cpu的cache是沒任何必然關(guān)系的留晚。
【文章福利】小生推薦自己的Linux后臺/內(nèi)核技術(shù)交流群【 318652197】整理了一些個(gè)人覺得比較好的學(xué)習(xí)書籍酵紫,視頻資料共享在群文件里面,有需要的自行添加哦4砦=钡亍!前100名進(jìn)群領(lǐng)取需五,額外贈送一份價(jià)值699的內(nèi)核資料包(含視頻鹉动、電子書轧坎、實(shí)戰(zhàn)項(xiàng)目及代碼)