【梳理】計算機組成與設計 第4章 處理器 第2節 流水線 流水線衝突(內附文檔高清截圖)

配套教材:
Computer Organization and Design: The Hardware / Software Interface (5th Edition)
建議先修課程:數字邏輯電路、C / C++、彙編語言。
這是專業必修課《計算機組成原理》的複習指引。建議將本複習指導與博客中的《簡明操作系統原理》配合複習。
在本文的最後附有複習指導的高清截圖。需要掌握的概念在文檔截圖中以藍色標識,並用可讀性更好的字體顯示 Linux 命令和代碼。代碼部分語法高亮。
計算機組成原理不是語言課,本複習指導對用到的編程語言的語法的講解也不會很細緻。如果不知道代碼中的一些關鍵字、指令或函數的具體用法,你應該自行查找相關資料。


第二節 流水線 流水線衝突

11、我們回憶一下執行一條指令的大致步驟:
(1)從內存中取指令。
(2)對指令解碼(decode),並讀寄存器(MIPS指令集允許解碼和讀寫指令中指定的寄存器同時進行)。
(3)開始執行,計算需要訪問的地址。
(4)從內存中訪問操作數。
(5)把結果寫入寄存器。
構建一個5級(stage)流水線(pipeline,也稱管線)的CPU,每一級負責上述所說的一步。由於每一級用到的單元是獨立的,因此我們可以這樣執行:
·解碼並讀寄存器時,可以同時取下一條指令。
·一條指令開始執行後,下一條指令可以開始解碼。
·訪問內存時,下一條指令可以開始執行。
·結果寫入寄存器時,可以開始根據下一條指令訪問內存。
也就是說,一條指令執行到上述5步中非第一步時,下一條指令都開始執行上一步:

(這裏每一步的執行時間只是舉例;並且,對讀寫寄存器的指令,假設寫在讀之前,而且各佔一半週期)
由於CPU執行指令的速度是非常快的,也就是說即使是很短的時間,CPU都已經執行了很多指令。所以近似來說,流水線化後的一個CPU與流水線化之前的這個CPU的性能倍數之比就是流水線級數,也就是說一個核心有幾級流水線,可以近似把這一個核心看成幾個核心(就是上面的對比圖中流水線化的指令全部靠在最左邊,而不是每個空開一級流水線的位置,執行的指令數越多,這種估計與實際越接近)。當然,流水線結構是有額外開銷的,所以實際上的性能提升並沒有這麼多。
每一級流水線的時鐘週期是一樣的,而且這個時鐘週期必須不短於耗時最長的那一步的執行週期(如果週期再調短的話,就會出現這樣的情況:某級流水線對應的步驟雖然執行完畢,但是下一級流水線需要用到的單元還在被佔用,所以新的指令無法進入下一級流水線,造成部分單元閒置,所以不如直接把每一步的週期都設成一樣的)。
有的指令在整條流水線期間,在某些stage上可能什麼都不做。但即便如此,指令還是要通過這些stage的。

12、MIPS的指令集都是定長的(32-bit),所以流水線化比較容易:取指令可以在第一級流水線完成。但是x86指令短則1字節,長則十幾字節,流水線化就比較麻煩了。較新的x86 CPU中,指令會被拆分成一系列微操作(micro-op),來降低流水線化的難度。
MIPS的指令格式不多,而且在這些格式中,源寄存器在指令中的位置都是一樣的,所以在第二級流水線中可以同時讀寄存器堆、確定下一條指令的格式。如果指令格式比較複雜的話,這一級流水線就要拆分成更多級了。
在MIPS中,只允許L / S指令的操作數來自內存。所以第3級流水線可以同時執行並計算內存地址,然後在第4級流水線進行內存訪問。x86允許其中一個操作數爲內存,這兩級流水線在x86中要拆成3級(地址、內存、執行)。
MIPS強制要求內存對齊,所以傳輸數據到內存時只需要一級流水線就能完成了。

13、假如一個指令在執行的時候需要先等待流水線上前一個指令先執行完畢,那麼這兩個指令相互之間具有依賴關係。這可能導致流水線衝突(pipeline hazard,也稱流水線冒險,流水線風險)的現象發生。流水線衝突分爲3種:
結構衝突(structural hazard):流水線上的一個指令需要使用已經被另一個指令佔據的資源(部件數量不夠,沒法滿足指令的重疊執行)。
數據衝突(data hazard):當指令在流水線中重疊執行時,因需要用到前面指令的執行結果而發生的衝突。數據衝突又可細分爲:
·指令層的數據衝突:指令需要的數據還沒有計算出來。
·傳輸層的數據衝突:指令需要的寄存器內容還沒有被存入寄存器。
控制衝突(control hazard,也稱分支衝突(branch hazard)):下一條指令流水時,CPU正在執行分支指令,不能在後來的指令的流水開始階段就判斷出先前的指令的分支結果。
這些衝突導致後來的指令必須等候,稱爲流水線停頓(pipeline stall)。這會在流水線上導致空缺,流水線就不能充分利用,處理速度便開始下降。因此要儘量避免這樣的衝突。

14、MIPS屬於典型的RISC 5-stage流水線,這樣的流水線在純整數計算時一般沒有結構性衝突,所以設計師們比較容易避免結構衝突。多週期單元容易引起結構性衝突,比如在乘法器、觸發器、浮點運算單元、寄存器的回寫部件上面易出現結構性衝突。
結構性衝突的解決辦法主要有:
·拖延新的指令,先待舊指令執行完畢,再執行新指令。
·增加硬件單元。例如,如果兩條指令同時需要存儲器的某部件,我們可以通過增加另一個該部件的方式避免衝突。
解決數據衝突的辦法主要有:
·轉發(forwarding),也稱旁路(bypassing),即上一條指令的計算結果一旦產生,就立刻將其傳給下一條指令,而不要等待上一條指令執行完畢。
如下圖,當先被取到的add指令在第3級流水線計算出結果以後,就直接將結果傳給下一條sub指令,而不等待add指令執行完畢。

(IF = instruction fetch, ID = instruction decode, EX = execute, MEM = memory access, WB = write back (to registers))
轉發結果之前,有時候需要額外添加流水線停頓,也稱氣泡(bubble)。氣泡是通過插入nop(no operation performed)指令實現的。指令譯碼時如果控制器發現可能存在冒險,就插入nop指令。這樣在有風險的指令進入流水線時,上一條指令已經在流水線中經過了充分多的週期,從而化解了冒險。如果插入的nop數量等於流水線級數,那麼CPU就排空了整個流水線。流水線冒泡可以消除任何一種流水線冒險。但插入過多的氣泡會嚴重浪費流水線機制帶來的性能提升。

在lw指令以後發生的這種數據衝突是比較特殊的一種,稱爲load-use數據衝突。
·預測:先假設一個值,如果假設錯誤,再進行改正。
解決控制衝突的主要辦法有:
·等待。在分支指令後插入流水線冒泡,直到分支指令的流水執行完畢。
·分支預測(branch prediction)。猜測分支可能的結果,然後嘗試選擇被判定爲可能性最大的結果執行。如果預測正確,性能就得到提升;如果誤預測(misprediction),則要清空這條提前執行的指令的流水線,重新按照實際的分支結果執行。
·延時分支。MIPS架構包含1條指令的分支延遲間隙,亦稱分支延時槽(branch delay slot)。分支指令之後,無論分支的判斷結果如何,延遲間隙中的指令總是先被執行。這條指令的優先執行與分支無關,不會對分支產生影響。至於將哪條指令放到延遲間隙裏,則由MIPS彙編器決定。
分支延遲間隙常見於DSP體系結構與老式的RISC體系結構。MIPS、PA-RISC、ETRAX CRIS、SuperH、SPARC等RISC體系結構在分支後有一個延遲間隙。PowerPC、ARM、DEC Alpha沒有采用分支延遲間隙。有的處理器(SHARC DSP與MIPS-X)包含2個分支延遲間隙。
除了分支延遲間隙以外,還有過存儲延遲間隙,即在存儲內存的指令之後跟一個延遲間隙,現在已經基本不用了。

15、學習了流水線後,我們可以把前面學習的數據通路按流水線的每一級重新繪製(藍色爲傳輸數據的線路):

當未發生流水線衝突時,由於每一級流水線用到的單元在接下來都會被其它指令使用,所以有必要用寄存器單獨爲每條指令保存該指令在流水線上的後續步驟需要用到的內容。例如取指令以後要將取到的內容存入寄存器,這樣取指令這一步需要用到的相關單元就可以放心地提供給下一條指令使用。
下圖中的藍色部分是兩級流水線之間的流水線寄存器(級間寄存器)。最後一步回寫是不需要流水線寄存器的,數據直接寫入目標寄存器中。程序計數器(PC)可以視作流水線寄存器。不過當異常發生時,PC的值必須被保存,以便異常處理完畢後返回到該處繼續執行;而流水線寄存器中的值可以直接丟棄。流水線寄存器的容量必須足夠保存所有在後續可能需要的信息。
流水線寄存器的寬度是有要求的。例如在MIPS架構中,IF / ID兩級之間的寄存器必須是64-bit寬,因爲需要保存取到的32-bit指令和32-bit的新PC值。

如果要繪製多條指令在流水線上的執行情況,可以畫成這樣(展示的細節更少):

16、現在我們來進一步完善之前的圖。把數據通路按每一級流水線重繪後,我們把控制信號補上去:

控制信號會被送入相應的流水線寄存器:

將這兩個圖合併:

17、以下是添加轉發單元的流水線化數據通路的示意圖:

可以看出,轉發器針對兩種情況:一種是接下來的指令要用到先來的指令的ALU的計算結果,一種是需要用到先前的指令從內存中取得的數據。探測到後來的指令對前面的指令的結果具有依賴性後,轉發單元會輸出控制信號控制ALU前的MUX,將轉發的結果搶先送入ALU,以便流水線能被充分利用,不至於總是需要等待依賴的結果。兩個操作數的是否轉發都可以獨立控制。
不過,對於有的指令,不能轉發其計算結果給後續指令,例如目標寄存器爲零寄存器$0的時候。
下圖給出了一種需要用到轉發來解決數據衝突以便滿載流水線的情況,注意sub和add指令之間是沒有數據衝突的:

添加了轉發單元的數據通路的示意圖如下(主要突出轉發機制,一些細節沒有畫出):

事實上,在ALU與第二操作數的MUX之間還有一個MUX,用於選擇將帶符號立即數還是將第二操作數傳入ALU:

18、在編程中,常常需要進行內存內複製,也就是把內存中的一塊數據複製到內存中的另一塊。這對應MIPS彙編中在lw指令之後立馬跟一條sw指令。所以我們可以添加額外的轉發單元來加快這個過程。這裏只畫出草圖:

19、如果在讀取一個值到寄存器後,下一條指令立刻就要用到這個值,那麼通過轉發是不可能解決這個數據衝突的:

由圖可知,在第4個週期末尾才能讀到內存中的數據,我們不能將它直接轉發到下一條指令的第3級流水線之前,也就是第4個週期開頭。解決辦法是通過沖突檢測單元在指令譯碼期間檢測衝突,如果LOAD指令的下一條指令需要立刻用到讀取的值,那麼就在這兩條指令之間插入1個週期的流水線氣泡進行停頓。構成氣泡的nop指令的全部控制位都是0(具體來說,主要是寫入寄存器和寫入內存的信號一定要爲無效,其餘控制信號是否有效都沒關係,即可以當作任意項),並且這意味着沒有任何寄存器或內存寫入,也就是說程序計數器不會增加。另外,IF / ID流水線寄存器的值也被阻止改變。在下一個週期,新指令仍然從同一個內存地址讀取,並且指令解碼階段用到的寄存器的值依然從未修改的IF / ID流水線寄存器中讀出。
如圖,and指令在第2和第3週期分別被取指、解碼,但執行延遲至第5週期。

下圖是添加衝突探測單元后的數據通路,符號擴展和分支邏輯沒有畫出來:

由圖,衝突探測單元控制PC的和IF / ID流水線寄存器的寫入,也控制一個MUX選擇將原指令的控制信號保留還是全部輸出0(即改成nop指令)。

20、前面我們說過,用分支預測可以避免控制衝突。這裏我們舉一些具體的例子。
假設無論分支條件是否成立,都一律按不成立的情形處理,即直接順序執行。如果分支有50%的條件成立,那麼我們有50%的概率不必等待分支指令跑完整條流水線。如果程序中的分支語句佔比較多,這種辦法對性能的提升還是不小的。
如果分支預測錯誤,則已經進入流水線的那些指令要全部丟棄。在典型的5級流水線MIPS處理器中,具體方法是:將控制信號全部置零,像處理數據衝突時插入氣泡一樣;不過之後我們不能直接不管取指令(IF)、譯碼(ID)和執行(EX)這三級的內容,而是必須將其修改,使得與指令匹配。這一步也稱爲恢復(流水線級間的)寄存器。對於load-use停頓,只需要在譯碼階段把控制信號全部置零,然後讓剩下的內容自然通過流水線。

21、有時候,分支誤預測懲罰(branch misprediction penalty)還是代價太大了,而且處理分支的時間還可以縮短。MIPS支持快的單週期分支,這種支持使得分支誤預測懲罰較小。大量事實證明,處理許多分支實際上很簡單,比如只用比較符號,或者兩數是否相等。實現這種判斷不需要完整的ALU,只需要規模更小的邏輯電路。當分支的判定條件比較複雜的時候,再通過規模完整的ALU判定。
爲了使得處理分支的速度加快,兩個動作被移到更前的階段:計算分支地址和條件判定。取指令後,我們已經可以利用PC的值和IF / ID流水線寄存器的值來計算分支地址了,也就是說這一步從執行(EX)階段移到了譯碼階段。其實這一步無論執行上面指令都會執行,不過只有在需要的時候纔會取用結果。
對分支指令beq,我們通過對相應的位先按位異或後按位或來比較。把分支判定的步驟移到更前的階段,意味着需要額外的轉發和衝突探測硬件,因爲必須保證仍在流水線上的依賴結果的分支還能夠正確執行。
例如:對beq指令,我們需要將判定結果在譯碼階段搞出來,有兩個難點:
(1)在譯碼階段必須解碼指令,決定要不要旁通到比較相等的單元並完成比較,以便需要跳轉時重設PC的值。轉發是由ALU轉發邏輯負責的,但比較相等的單元需要新的轉發單元。被旁通的源操作數可以來自EX / MEM的鎖存器(latch)和MEM / WB的鎖存器。
(鎖存器和觸發器的區別是:觸發器一般爲邊沿觸發,鎖存器則是電平觸發,對電平敏感,易使信號產生毛刺,但面積比觸發器要小很多)
(2)可能發生數據衝突並需要流水線停頓。例如:一個分支前的ALU指令產生了用於分支比較的的一個操作數,就需要一週期的停頓,因爲執行階段完畢後才能給出這個需要的數。如果LOAD後面立刻有一條條件分支指令,而且需要用到讀取的數據,則需要兩個週期的停頓,因爲在內存訪問階段才能給出讀到的數據,但譯碼階段比內存訪問階段早兩個週期。
但即便有這些困難,如此縮短分支的執行時間依然可以提升性能,因爲誤預測懲罰減小了:只需要清除掉分支指令的下一條指令。由於該指令剛剛取得,僅經過了第一個流水線階段,因此浪費的週期只有1個。
添加了在分支誤預測後刷新流水線的信號的數據通路如下圖,只需要清空第一、第二階段之間的流水線寄存器即可:

22、雖然編譯器通常依賴硬件來解決流水線衝突並保證程序的正確執行,但編譯器作者應當理解平臺的流水線機制,以便發揮最佳性能,否則會出現意料之外的氣泡拖慢代碼的運行速率。

23、其實剛纔說的遇到分支指令後一律按條件不成立並順序執行處理只是一種很簡單的分支預測方式。現代處理器中,多用動態分支預測(dynamic branch prediction)。動態分支預測通常需要編譯器配合。在一些CPU中,流水線很深(級數很多),而且一週期可以執行多條指令(超標量(superscalar)或超長指令字(very long instruction word,VLIW)),因此分支誤預測懲罰也隨之升高(一旦預測錯誤,就要清空更多的流水線階段乃至多條流水線)。面向對象的編程語言會產生許多難以預測的間接分支;一些略有不同的流水線停頓會導致分支延遲更高,這些都對分支預測提出了新的要求。
分支預測緩衝(branch prediction buffer),或者說分支歷史表(branch history table),保存有地址的低若干位和分支預測的歷史結果。如果預測錯誤,相應的結果會被刪除,存入正確的結果。分支預測的結果是根據緩衝區中的歷史記錄得到的。分支預測緩衝在取指令階段訪問。
簡單的1-bit預測策略在一些情況下可能會至少錯2次。例如:
設一段循環中,一個條件語句總是每滿足條件9次後有1次不滿足。由於每次預測錯誤以後,預測歷史都會被修改,於是在每輪循環的最後一次都會預測錯誤,因爲先前的預測結果記錄顯示分支條件要滿足;下一輪循環的第一次分支預測也會出錯,因爲上一輪循環中的最後一次結果是分支條件不滿足。
2-bit策略則在連續預測錯誤2次以後才修改相應的歷史記錄。容易看出,在上面的例子中,換用2-bit策略能夠將預測錯誤的概率從1 / 5降低到1 / 10。2-bit策略確保偶然的情形不對預測作出重大影響,在分支條件滿足或不滿足的概率很高的場合很有用。
分支目標緩衝(branch target buffer,BTB)是一種消除1個週期的誤預測懲罰的方式。BTB是定長的哈希表(hash table),將PC的值映射到目標地址,一般至少能存儲數千項。這張表記錄了最近執行的一些分支指令的目標地址,並且根據PC值進行查找。如果正在執行的指令是最近執行過的分支指令,那麼在BTB中會有相應的記錄,稱爲BTB命中(BTB hit)。此時不用再等待判定該指令確爲分支指令,也不用重新計算該指令的分支目標地址。於是,可以直接根據BTB中的記錄進行分支預測。如果BTB中未找到相應的項,則什麼都不做,一切照常進行。
關聯預測器(correlated predictor)用於處理分支之間存在關聯的情形。例如:

這些分支的結果存在關聯,所以可以根據前面執行過的分支來進行分支預測。
競賽預測器(tournament predictor)使用多種預測器,並追蹤哪種預測器對哪一條分支的預測效果最好。一種典型的競賽預測器爲每一條分支使用兩種分支預測器:一種基於分支本身的相關信息,通過PC的值來訪問這些信息;一種是關聯預測器,通過基於最後執行的一定數量的分支的全局歷史來進行預測。

24、MIPS和ARM指令集中,都有自己的減少分支數量的方式。例如MIPS有專門的movn和movz指令,分別在指定的寄存器的值爲非零和零時才執行移動操作,否則不進行任何操作。ARMv7的絕大多數指令都有專門的區域控制執行的條件,只有這些條件滿足時,該指令才執行。使用這些滿足一定條件才執行的指令,就不用通過專門的分支指令來實現相同的功能了。

25、流水線越長,意味着同一條指令能被拆成更多的步驟,於是每個步驟的時間理論上更短,因此CPU的頻率也越高。但是流水線的長度是有限制的:一條指令不能細分成無限個步驟,而且長的流水線在預測失敗時懲罰也會變高(需要清除更多的指令、恢復更多的級間寄存器,注意被清除的指令是徒增功耗),旁通數據解決流水線衝突也比較難做。當流水線的級數過長時,流水線寄存器的速度反而成了瓶頸,這時候再提升流水線級數,帶來的性能提升就沒有多少了。
越長的流水線意味着越複雜的內部結構,生產的良品率越發難以保證,越多的功耗將會被浪費在信號傳遞上,發熱得不到控制的結果不言而喻。
Load-use型冒險也會在長流水線上有更長的停頓。
在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述
在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章