一、緩存Cache
我們看到CPU參數(shù)經(jīng)常會提到1、2、3級緩存容量,有時還有L1$的標(biāo)識,這是什么呢。
TLB與兩級緩存
資料來源:互聯(lián)網(wǎng)
緩存(Cache)是指訪問速度比一般隨機存取存儲器(RAM)快的一種高速存儲器,通常緩存不像系統(tǒng)主存那樣使用DRAM技術(shù),而使用昂貴但較快速的SRAM技術(shù)。
緩存的工作原理是當(dāng)CPU要讀取一個數(shù)據(jù)時,首先從CPU緩存中查找,找到就立即讀取并送給CPU處理;沒有找到,就從速率相對較慢的內(nèi)存中讀取并送給CPU處理,同時把這個數(shù)據(jù)所在的數(shù)據(jù)塊調(diào)入緩存中,可以使得以后對整塊數(shù)據(jù)的讀取都從緩存中進行,不必再調(diào)用內(nèi)存。
正是這樣的讀取機制使CPU讀取緩存的命中率非常高(大多數(shù)CPU可達90%左右),也就是說CPU下一次要讀取的數(shù)據(jù)90%都在CPU緩存中,只有大約10%需從內(nèi)存讀取。這大大節(jié)省了CPU直接讀取內(nèi)存的時間,也使CPU讀取數(shù)據(jù)時基本無需等待。
總體而言,CPU讀取數(shù)據(jù)的順序是先緩存后內(nèi)存。
一級緩存即L1 Cache,因為Cache的發(fā)音與英語現(xiàn)金Cash的發(fā)音相同,因此有時寫做L1$。一級緩存分指令和數(shù)據(jù),二級緩存即L2 Cache,不分指令和數(shù)據(jù)。一級緩存是每個核獨有,少量多核如12核以上二級緩存是4個或多個核共享,大部分二級緩存是每個核獨有的,三級緩存即L3 Cache是通過總線與內(nèi)核連接的,是多個核共用的。
因為緩存使用SRAM,其晶體管密度低,所占面積大,這就意味著成本高,簡單地說,緩存用的越多,成本就越高,性能就越強。Cache存儲數(shù)據(jù)是固定大小為單位的,稱為一個Cache entry,這個單位稱為Cache line或Cacheblock。給定Cache容量大小和Cache line size的情況下,它能存儲的條目個數(shù)(number of cache entries)就是固定的。因為Cache是固定大小的,所以它從DRAM獲取數(shù)據(jù)也是固定大小。對于X86來講,它的Cache line大小與DDR3、4一次訪存能得到的數(shù)據(jù)大小是一致的,即64Bytes。
通常L1 Cache離CPU核心需要數(shù)據(jù)的地方更近,而L2 Cache則處于邊緩位置,訪問數(shù)據(jù)時,L2 Cache需要通過更遠的銅線,甚至更多的電路,從而增加了延時。
L1 Cache分為ICache(指令緩存)和DCache(數(shù)據(jù)緩存),指令緩存ICache通常是放在CPU核心的指令預(yù)取單元附近的,數(shù)據(jù)緩存DCache通常是放在CPU核心的load/store單元附近。而L2 Cache是置于CPU pipeline之外的。還有L3緩存,通常是多核共享的緩存,容量更大。
緩存是逐級的,1級緩存中找不到,就到2級緩存里找,然后是3級,最后是芯片外的存儲器。 由于內(nèi)存種類多,需要引入內(nèi)存管理單元,即MMU。MMU是硬件設(shè)備,它被保存在主存(main memory)的兩級頁表控制,MMU的主要作用是負責(zé)從CPU內(nèi)核發(fā)出的虛擬地址到物理地址的映射,并提供硬件機制的內(nèi)存訪問權(quán)限檢查。MMU使得每個用戶進程擁有自己的地址空間,并通過內(nèi)存訪問權(quán)限的檢查保護每個進程所用的內(nèi)存不被其他進程破壞。
處理器引入MMU后,讀取指令、數(shù)據(jù)需要訪問兩次內(nèi)存:首先通過查詢頁表得到物理地址,然后訪問該物理地址讀取指令、數(shù)據(jù)。為減少因MMU導(dǎo)致的處理器性能下降,引入了TLB,TLB是Translation Lookaside Buffer的簡稱,可翻譯為“地址轉(zhuǎn)換后緩沖器”,亦簡稱為“快表”。簡單地說,TLB就是頁表的Cache,其中存儲了當(dāng)前最可能被訪問到的頁表項,其內(nèi)容是部分頁表項的一個副本。只有在TLB無法完成地址翻譯任務(wù)時,才會到內(nèi)存中查詢頁表,這樣就減少了頁表查詢導(dǎo)致的處理器性能下降。
什么是頁表(Page Table)
這是操作系統(tǒng)里的一個術(shù)語,操作系統(tǒng)的一個主要任務(wù)是將程序彼此隔離。因此需要建立不同的內(nèi)存空間,也就是需要給內(nèi)存安排不同的地址。頁表通常是操作系統(tǒng)決定的。對于32位操作系統(tǒng),如果想支持32位的操作系統(tǒng)下的4GB進程虛擬地址空間,假設(shè)頁表大小為4K,則共有2的20次方頁面。
如果采用速度最快的1級頁表,對應(yīng)則需要2的20次方個頁表項。一個頁表項假如4字節(jié),那么一個進程就需要(1048576*4=)4M的內(nèi)存來存頁表項。這太大了,成本太高,因此需要分級,采用2級頁表,則創(chuàng)建進程時只需有一個頁目錄就可以了,占用(1024*4)=4KB的內(nèi)存。剩下的二級頁表項只有用到的時候才會再去申請。若是64位,則需要4級頁表,Linux在v2.6.11以后,最終采用的方案是4級頁表,分別是:
PGD:Page Global Directory (47-39), 頁全局目錄
PUD:Page Upper Directory (38-30),頁上級目錄
PMD:Page Middle Directory (29-21),頁中間目錄
PTE:Page Table Entry (20-12),頁表項
對于任何一條帶有地址的指令,其中的地址應(yīng)該認為是虛擬內(nèi)存地址而不是物理地址。假設(shè)寄存器a0中是地址0x1000,那么這是一個虛擬內(nèi)存地址。虛擬內(nèi)存地址會被轉(zhuǎn)到內(nèi)存管理單元(MMU,Memory Management Unit)并翻譯成物理地址。之后這個物理地址會被用來索引物理內(nèi)存,并從物理內(nèi)存加載,或者向物理內(nèi)存存儲數(shù)據(jù)。從CPU的角度來說,一旦MMU打開了,它執(zhí)行的每條指令中的地址都是虛擬內(nèi)存地址。為能夠完成虛擬內(nèi)存地址到物理內(nèi)存地址的翻譯,MMU會有一個表單,表單中,一邊是虛擬內(nèi)存地址,另一邊是物理內(nèi)存地址。
分頁技術(shù)的核心思想是將虛擬內(nèi)存空間和物理內(nèi)存空間視為固定大小的小塊,虛擬內(nèi)存空間的塊稱為頁面(pages),物理地址空間的塊稱為幀(frames),每一個頁都可以映射到一個幀上。每個page創(chuàng)建一條表單條目,所以每一次地址翻譯都是針對一個page。而RISC-V中,一個page是4KB,也就是4096Bytes。對于虛擬內(nèi)存地址,我們將它劃分為兩個部分,index和offset,index用來查找page,offset對應(yīng)的是一個page中的哪個字節(jié)。當(dāng)MMU在做地址翻譯的時候,通過讀取虛擬內(nèi)存地址中的index可以知道物理內(nèi)存中的page號,這個page號對應(yīng)了物理內(nèi)存中的4096個字節(jié)。之后虛擬內(nèi)存地址中的offset指向了page中的4096個字節(jié)中的某一個,假設(shè)offset是12,那么page中的第12個字節(jié)被使用了。將offset加上page的起始地址,就可以得到物理內(nèi)存地址。
頁表是存在內(nèi)存里的。那就是一次內(nèi)存I/O光是虛擬地址到物理地址的轉(zhuǎn)換就要去內(nèi)存查4次頁表,再算上真正的內(nèi)存訪問,最壞情況下需要5次內(nèi)存I/O才能獲取一個內(nèi)存數(shù)據(jù),這太費時間,也導(dǎo)致功耗增加,于是TLB誕生了,TLB就是頁表緩存。
當(dāng)CPU執(zhí)行機構(gòu)收到應(yīng)用程序發(fā)來的虛擬地址后,首先到TLB中查找相應(yīng)的頁表數(shù)據(jù),MMU從TLB中獲取頁表,翻譯成物理地址,如果TLB中正好存放著所需的頁表,則稱為TLB命中(TLB Hit),接下來CPU再依次看TLB中頁表所對應(yīng)的物理內(nèi)存地址中的數(shù)據(jù)是不是已經(jīng)在一級、二級緩存里了,若沒有則到內(nèi)存中取相應(yīng)地址所存放的數(shù)據(jù)。如果TLB中沒有所需的頁表,則稱為TLB失?。═LB Miss),接下來就必須訪問物理內(nèi)存中存放的頁表,同時更新TLB的頁表數(shù)據(jù)。 TLB也分指令和數(shù)據(jù)。
典型架構(gòu)Cortex-A78緩存指令流程
圖片來源:ARM
MOP緩存是一些經(jīng)過預(yù)處理指令的融合操作緩存。
二、超標(biāo)量
早期的計算機都是串行計算,隨著對吞吐量需求越來越高,并行計算出現(xiàn)了。常見的并行計算有三種指令并行、數(shù)據(jù)并行和任務(wù)并行。任務(wù)并行更多借助軟件才能實現(xiàn)。對硬件來說有指令并行(ILP)、線程并行和數(shù)據(jù)并行三種。
指令級并行是一種隱式并行,對用戶來說是完全透明的,用戶完全看不到也感覺不到,也就是寫程序的人不需要關(guān)注,通過流水線和超標(biāo)量,使得一個程序的指令序列中有多條同時亂序運行,順序提交。這依賴寄存器重命名、多個執(zhí)行單元、重排序緩沖和指令預(yù)測技術(shù)。
線程級并行是一種顯式并行,對用戶來說不透明,也就是說程序員要寫多線程程序。線程級并行主要指同時多線程(SMT)或超線程(HT)以及多核和多處理器。SMT是在指令級并行的基礎(chǔ)上的擴展,可以在一個核上運行多個線程,多個線程共享執(zhí)行單元,以便提高部件的利用率,提高吞吐量。SMT需要為每個線程單獨保持狀態(tài),如程序計數(shù)器(PC)、寄存器堆、重排序緩沖等。 數(shù)據(jù)級并行是一種顯式并行,主要指單指令多數(shù)據(jù)(SIMD),比如a,b和c都是相同大小的數(shù)組,要進行的計算是a的每一個元素與b的響應(yīng)元素進行運算,結(jié)果放入c的對應(yīng)元素中。如果沒有SIMD,就需要寫一個循環(huán)執(zhí)行多遍來完成,而SIMD中一條指令就可以并行地執(zhí)行運算。
線程的概念就是程序的執(zhí)行序,每個執(zhí)行序有執(zhí)行上下文需要保存。 傳統(tǒng)的通用處理器都是標(biāo)量處理器,一條指令執(zhí)行只得到一個數(shù)據(jù)結(jié)果。但對于圖像、信號處理等應(yīng)用,存在大量的數(shù)據(jù)并行性計算操作,這些數(shù)據(jù)都具備方向,一般稱為向量計算。
超標(biāo)量(Superscalar)是指在CPU中有一條以上的流水線,并且每時鐘周期內(nèi)可以完成一條以上的指令,這種設(shè)計就叫超標(biāo)量技術(shù)。其實質(zhì)是以空間換取時間。而超流水線是通過細化流水、提高主頻,使得在一個機器周期內(nèi)完成一個甚至多個操作,其實質(zhì)是以時間換取空間。
超標(biāo)量CPU的工作流程如下:
(1)Fetch(取指令):這部分負責(zé)從I-Cache中取指令,I-Cache負責(zé)存儲最近常用的指令;分支預(yù)測器用來決定下一條指令的PC值。
(2)Decode(解碼):識別指令的類型
(3)RegisterRenaming(寄存器重命名):解決WAW和WAR這兩種“偽相關(guān)性”,需要使用寄存器重命名的方法,將指令集中定義的邏輯寄存器重命名為處理器內(nèi)部使用的物理寄存器。物理寄存器的個數(shù)更多于邏輯寄存器,處理器可以調(diào)度更多可以并行執(zhí)行的指令。將存在RAW的寄存器進行標(biāo)記,后續(xù)通過旁路網(wǎng)絡(luò)(bypassing network)解決存在的“真相關(guān)性”。
(4)Dispatch(分發(fā)):被重命名之后的指令會按照程序中的規(guī)定順序,寫到發(fā)射隊列(Issue Queue)、重排序緩存(ROB)和Store Buffer等部件中
(5)Issue(發(fā)射):經(jīng)過流水線的分發(fā)(Dispatch)階段之后,指令被寫到了發(fā)射隊列(Issue Queue)中,仲裁(select)電路回從這個部件中挑選出合適的指令送到FU中執(zhí)行。
(6)RegisterFile Read(讀取寄存器):被仲裁電路選中的指令需要從物理寄存器堆(Physical Register File,PRF)中讀取操作數(shù)
(7)Execute(執(zhí)行):各種FU單元執(zhí)行
(8)Writeback(寫回):將FU計算的結(jié)果寫到物理寄存器(PRF)中,通過旁路網(wǎng)絡(luò)將計算結(jié)果送到需要的地方。
(9)Commit(提交):這個階段起主要作用的部件是重排序緩存(ROB),它將亂序執(zhí)行的指令拉回到程序中規(guī)定的順序。
目前絕大多數(shù)CPU都是超標(biāo)量結(jié)構(gòu)。
ARMNeoverse V1的微架構(gòu)
圖片來源:ARM
上圖是ARM Neoverse V1的架構(gòu),是目前ARM面向服務(wù)器領(lǐng)域最強的計算架構(gòu)。對于一個內(nèi)核架構(gòu)來說,通常分為三部分:前端、執(zhí)行引擎和存儲系統(tǒng),其中前端是著墨最多的地方,也是變化最多的地方。
圖中解碼器為5路,V1架構(gòu)最高可擴展到8路,也就是IPC為8。這是目前最高的IPC。ARM架構(gòu)的CPU增加性能最簡單的方法就是提高IPC。
每增加一路解碼器,系統(tǒng)的復(fù)雜程度就會大大增加,在內(nèi)核前端部分,解碼器所占的面積最大,也就是占芯片成本比例最高,要保障成本不大幅度增加同時提升性能,就需要先進的芯片制造工藝,盡量提高晶體管密度,在有限的面積內(nèi)塞入更多的晶體管。ARM架構(gòu)的升級和芯片制造工藝的升級是相輔相成的,沒有先進制造工藝,ARM架構(gòu)的芯片性能提升也帶來成本大幅度提升,沒有ARM架構(gòu)的升級,先進制造工藝也無用武之地。
流水線是為了提高時鐘頻率,將每個步驟再細分,分的越細,每一級執(zhí)行的時間就越短,運行頻率就可以提高。
但流水線并非越深越好。
(1)每一級流水線均由寄存器組成,更多的流水線級數(shù)意味著消耗更多的寄存器,產(chǎn)生更大的面積開銷,功耗也會增大,成本也會上升。
(2)每一級流水線需要握手,流水線的最后一級反壓信號可能會一直串?dāng)_到最前一級造成嚴重的時序問題。
(3)流水線的取指階段得知條件跳轉(zhuǎn)的結(jié)果是到底跳還是不跳,因此只能進行預(yù)測,而到了流水線的末端才能通過實際運算得知該分支是真的該跳還是不該跳。如果發(fā)現(xiàn)真實的結(jié)果與預(yù)期的結(jié)果不一致,意味著預(yù)測失敗,需要將所有的預(yù)取指令全部丟失。流水線越深,則意味著浪費和損失越嚴重;流水線越淺,則浪費和損失越少。
三、亂序執(zhí)行
超標(biāo)量CPU最典型特性就是亂序執(zhí)行,Out-of-Order Execution,簡稱OoOE或OOE。超標(biāo)量CPU會處理多種算法,因為不同指令的運算周期數(shù)不同,例如乘法運算所需的時間周期就比加法運算長很多,所以如果一個需要很長時間的指令后面緊跟著需要使用其結(jié)果的指令的話,就會浪費很多時間,所以打亂指令的執(zhí)行順序就很重要。
針對亂序執(zhí)行有不少的算法,但是歸根到底都是以保留棧為基礎(chǔ)進行設(shè)計的。保留棧的核心思想,是把譯碼完成后的指令根據(jù)各自的指令種類,將譯碼后的指令送往各自的保留棧中保存下來,如果該指令所有操作數(shù)都已準備齊全,則可以開始進行亂序發(fā)射。注意,執(zhí)行可以亂序,但提交不可以,否則就出現(xiàn)錯誤了。
在處理器中,先后執(zhí)行的指令之間經(jīng)常具有相關(guān)性(例如后一條指令用到前一條指令向寄存器寫入的結(jié)果),因此早期簡單的處理器使后續(xù)指令停頓,直到其所需的資源已經(jīng)由前序指令準備就緒。
Tomasulo算法則通過動態(tài)調(diào)度的方式,在不影響結(jié)果正確性的前提下,重新排列指令實際執(zhí)行的順序(亂序執(zhí)行),這種算法是1966年Robert Tomasulo 發(fā)明,沿用至今。該算法使用了寄存器重命名機制。
指令之間具有數(shù)據(jù)相關(guān)性(例如后條指令的源寄存器恰好是前條指令要寫入的目標(biāo)寄存器),進行動態(tài)調(diào)度時必須避免三類冒險(即流水線停頓):寫后讀(Read-after-Write, RAW)、寫后寫(Write-after-Write, WAW)、讀后寫(Write-after-Read, WAR)。
第一種冒險也被稱為真數(shù)據(jù)相關(guān)(True Data Dependence),而后兩種冒險稱為偽相關(guān),偽相關(guān)并不一定導(dǎo)致流水線停頓,它們可以由寄存器重命名來予以解決,亂序執(zhí)行的關(guān)鍵就是識別偽相關(guān)。 寄存器重命名有兩種,一種就是將一體系架構(gòu)寄存器ARF(Architected Register File)動態(tài)映射到一個專門的物理寄存器堆PRF(Physical Register File)上,消除WAW和WAR冒險。
由一個Mapping Table負責(zé)管理和查詢,這個重命名的過程對用戶透明,用戶只能看到ARF。讀出指令和動作后先把這些都保存在保留站(Reservation Stations)中,然后CDB(公共數(shù)據(jù)總線)上進行廣播,看這個計算結(jié)果有沒有作為是其他指令源操作數(shù)的。如果保留站發(fā)現(xiàn)有,就會更新保留站源操作數(shù)的值。這樣就代替了流水線寄存器。這種辦法有可能出現(xiàn)不按順序提交,因此無法實用。八十年代后對算法做了改進,增加了ROB(Reorder Buffer),在Tomasulo算法把指令分為Issue,Execute, 和Write Result三步的基礎(chǔ)上,增加一步,稱為Commit(交付,后提交)。
Commit的功能是指令將其結(jié)果交付給(寫入)目的寄存器或存儲單元。必須增加一硬件緩沖存儲器(buffer),供Write Result這一步存放已獲得的結(jié)果,并可以提供給其它指令應(yīng)用這些結(jié)果。當(dāng)指令進入Commit這一步時,將結(jié)果從buffer中拷貝到目的寄存器或存儲單元。這一硬件緩沖存儲器稱為重排序緩沖(ROB,Reorder Buffer)。ROB保證指令順序發(fā)射(issue/dispatch),亂序執(zhí)行(execute),順序提交(commit)。通過Reorder buffer(ROB),可以實現(xiàn)Precise Exception和HW Speculation,同時由于ROB保證指令順序提交,順便也消除了WAR和WAW 冒險。Precise Exception(精確異常) 的意思是當(dāng)指令出現(xiàn)異常時(除0、page fault等),前面的指令已經(jīng)完成,后面的指令不能對寄存器、內(nèi)存等進行修改,即與順序執(zhí)行的效果一樣。
亂序執(zhí)行微架構(gòu)
圖片來源:互聯(lián)網(wǎng)
ROB示意圖,F(xiàn)P指浮點運算。 ROB(重排序緩存)的出現(xiàn)還有一個好處是可以預(yù)測式執(zhí)行,處理器可以根據(jù)預(yù)測執(zhí)行指令,因為執(zhí)行完的指令并未提交,而是放在ROB內(nèi),即使出現(xiàn)預(yù)測錯誤,執(zhí)行了不該執(zhí)行的指令,但這個指令不會提交,會被直接丟棄。
亂序執(zhí)行,順序提交
圖片來源:互聯(lián)網(wǎng)
這當(dāng)中也有微小區(qū)別,一種稱之為顯式重命名,顯式重命名方案中ROB不記錄指令的結(jié)果,即將提交的數(shù)據(jù)和處于推測狀態(tài)的數(shù)據(jù)都保存在物理寄存器中,因此物理寄存器數(shù)目要高于邏輯寄存器數(shù)目。采用隱式重命名方案時,ROB (Recorder Buffer) 保存正在執(zhí)行、尚未提交的指令的結(jié)果;ARF (ISA Register File) 保存已經(jīng)提交的指令中即將寫入寄存器中的值。隱式重命名方案中ARF只保存已經(jīng)提交的指令的值,處于“推測”狀態(tài)的指令的值由ROB保存,因此需要的物理寄存器數(shù)量與邏輯寄存器數(shù)量相同。隱式重命名方案還需要建立一個映射表,記錄操作數(shù)在ROB中的位置。
相比于顯式重命名,隱式重命名需要的物理寄存器數(shù)目更少,也就意味著成本低,但每個操作數(shù)在其生命周期中需要保存在ROB和ARF兩個位置,讀取數(shù)據(jù)的復(fù)雜度較高、功耗更高。 在英特爾架構(gòu)中,亂序執(zhí)行做得異常復(fù)雜,引入了Allocator定位器,Allocator管理著RAT (RegisterAlias Table,寄存器別名表)、ROB (Re-Order Buffer,重排序緩沖區(qū))和 RRF (RetirementRegister File,退回寄存器文件)。
RAT將重命名的、虛擬的寄存器(稱為Architectural Register 或 Logical Register)指向ROB或者RRF。RAT是一式兩份,每個線程獨立,每個RAT包含了128個重命名寄存器。RAT指向在ROB里面的最近的執(zhí)行寄存器狀態(tài),或者指向RRF保存的最終的提交狀態(tài)。
ROB將亂序執(zhí)行完畢的指令們按照程序編程的原始順序重新排序的一個隊列,以保證所有的指令都能夠邏輯上實現(xiàn)正確的因果關(guān)系。打亂了次序的指令們(分支預(yù)測、硬件預(yù)取)依次插入這個隊列,當(dāng)一條指令通過RAT發(fā)往下一個階段確實執(zhí)行的時候,這條指令(包括寄存器狀態(tài)在內(nèi))將被加入ROB隊列的一端,執(zhí)行完畢的指令(包括寄存器狀態(tài))將從ROB隊列的另一端移除(期間這些指令的數(shù)據(jù)可以被一些中間計算結(jié)果刷新),因為調(diào)度器是In-Order順序的,這個隊列(ROB)也就是順序的。從ROB中移出一條指令就意味著指令執(zhí)行完畢了,這個階段叫做Retire回退,相應(yīng)地ROB往往也叫做Retirement Unit (回退單元,實際也就是提交單元),并將其劃為流水線的最后一部分。
在一些超標(biāo)量設(shè)計中,Retire階段會將ROB的數(shù)據(jù)寫入L1D緩存(這是將MOB集成到ROB的情況),而在另一些設(shè)計里,寫入L1D緩存由另外的隊列完成。例如,2008年英特爾推出的Nehalem, 這個操作就由MOB (Memory Order Buffer,內(nèi)存重排序緩沖區(qū))來完成。
英特爾Nehalem微架構(gòu)前端
圖片來源:互聯(lián)網(wǎng)
Nehalem的128條目的ROB擔(dān)當(dāng)中間計算結(jié)果的緩沖區(qū),它保存著猜測執(zhí)行的指令及其數(shù)據(jù),猜測執(zhí)行允許預(yù)先執(zhí)行方向未定的分支指令。在大部分情況下,猜測執(zhí)行工作良好——分支猜對了,因此其在ROB里產(chǎn)生的結(jié)果被標(biāo)識為已結(jié)束,可以立即被后繼指令使用而不需進行L1Data Cache 的Load 操作(這也是ROB的另一個重要用處,典型的x86應(yīng)用中Load操作是如此頻繁,達到了幾乎占1/3的地步,因此ROB可以避免大量的Cache Load操作,作用巨大)。在剩下的不幸情況下,分支未能按照如期的情況進行,這時猜測的分支指令段將被清除,相應(yīng)指令們的流水線階段清空,對應(yīng)的寄存器狀態(tài)也就全都無效了,這種無效的寄存器狀態(tài)不會也不能出現(xiàn)在RRF里面。
分支預(yù)測
流水線架構(gòu)把指令的執(zhí)行分為了多個階段,每個單元只負責(zé)完成指令執(zhí)行過程中的一個階段,而中間結(jié)果由專門的流水線寄存器暫存。這樣理論上,一條指令的執(zhí)行假設(shè)被分為5個階段,那么當(dāng)5個單元同時運行一段時間后,理論上相同時間可以同時執(zhí)行5條指令,當(dāng)然這只是最簡單的情況,實際的情況要復(fù)雜得多。
對于條件跳轉(zhuǎn)指令(匯編層面是JMP等,代碼層面例如if),需要等當(dāng)前的指令執(zhí)行完才知道結(jié)果是true還是false,也就是說,要等待若干個時鐘周期后,CPU才可以確定下一條要執(zhí)行的指令究竟是哪個分支的。
現(xiàn)代CPU都是超過十級的深流水線,這個等待時間太長了,難道這段時間就只能干等著嗎?當(dāng)然不是,這里CPU就會采取「分支預(yù)測」的方式,預(yù)測下一條要執(zhí)行的分支指令,并預(yù)先執(zhí)行,如果if執(zhí)行完后發(fā)現(xiàn)和預(yù)測的分支一致,那就中大獎了,整個執(zhí)行階段一點都沒有暫停。但如果悲劇地預(yù)測錯誤,那么這時候必須從取址開始,重新執(zhí)行另一個分支的指令。
分支預(yù)測有兩種,一種是BHT(分支歷史表),一種是BTB(分支目標(biāo)緩存)。BHT已經(jīng)很少人用了,BHT記錄分支指令最近一次或幾次的執(zhí)行情況(成功或不成功),并據(jù)此進行預(yù)測;BHT下依然要計算分支目標(biāo),所以判定分支是否成功所需的時間應(yīng)該大于確定分支目標(biāo)地址所需的時間,這時BHT方法才有用。BHT記錄的是跳轉(zhuǎn)信息,簡單點的,可以用1bit位記錄,例如1表示跳轉(zhuǎn),0表示不跳轉(zhuǎn),而這個表格的索引是指令PC值;考慮在32位系統(tǒng)中,如果要記錄完整32位的branch history,則需要4Gbit的存儲器,這超出了系統(tǒng)提供的硬件支持能力;所以一般就用指令的后12位作為BHT表格的索引,這樣用4Kbit的一個表格,就可以記錄branch history了。
BTB將分支成功的分支指令的地址和它的分支目標(biāo)地址都放到一個緩沖區(qū)中保存起來,緩沖區(qū)以分支指令的地址作為標(biāo)識。這個緩沖區(qū)就是分支目標(biāo)緩沖器。BTB用于記錄一條分支指令的跳轉(zhuǎn)地址,由于這兒存儲的是指令地址,例如32位地址,因此,這個表格就不能做到存儲BHT那樣多的內(nèi)容了,如果也支持4K條指令,則需要128Kbit的存儲空間,這幾乎可以趕上一個L1Cache的容量了,所以BTB一般很小,就32項或者64項。
為了盡量提高分支預(yù)測準確度,BTB又被細分為NANO、MICRO和主緩存。NANO和MICRO都很小,只有16或32,服務(wù)器級的有96項。主緩存最高有8K的。數(shù)據(jù)庫以及ERP等應(yīng)用,跳轉(zhuǎn)分支將會跨過很大的區(qū)域并具有很多的分支。