計算機組成與設計 - 流水線冒險

冒險

流水線技術之所以能提高性能 究其本質是利用了時間上的並行性,那它讓原本應該先後執行的指令在時間上一定程度的並行起來,然而這也會帶來一些衝突和矛盾,進而可能引發錯誤。

冒險(Hazard):在流水線中我們希望當前每個時鐘週期都有一條指令進入流水線可以執行。但在某些情況下,下一條指令無法按照預期開始執行,這種情況就被稱爲冒險。

冒險分爲三種:

**結構冒險:**如果一條指令需要的硬件部件還在爲之前的指令工作,而無法爲這條指令提供服務,那就導致了結構冒險。(這裏結構是指硬件當中的某個部件)
數據冒險:如果一條指令需要某數據而該數據正在被之前的指令操作,那這條指令就無法執行,就導致了數據冒險
**控制冒險:**如果現在要執行哪條指令,是由之前指令的運行結果決定,而現在那條之前指令的結果還沒產生,就導致了控制冒險。

結構冒險

示例一:如果指令和數據放在同一個存儲器中,則不能同時讀存儲器
在這裏插入圖片描述
***解決方案一:我們有一個方便又簡便的方法,即流水線停頓(stall),產生空泡(bubble)。
在這裏插入圖片描述雖然流水線停頓能用來解決各種冒險,但它的效率低下,應儘量避免。
解決方案二:在存儲器中設置單獨的指令高速緩存和數據高速緩存。(要強調的在計算機中主存儲器也就是內存是統一存放指令和數據的,這也是馮諾依曼結構的要求,只是在CPU當中 的一級高速緩存會採用指令和數據分別存放的方式)
在這裏插入圖片描述

示例二:如果讀寄存器和寫寄存器同時發生,如何處理?
在這裏插入圖片描述
解決方案:前半個週期寫,後半個週期讀,並且設置獨立的讀寫端口

相對來說寄存器堆的讀寫速度比較快, 我們假設讀或者寫寄存器的延遲爲100ps,而其他部件比如說ALU的延遲就就比較大,視爲200ps, 那麼我們就可以在前半個時鐘週期用於完成寄存器堆的寫,後半個時鐘週期用來完成讀操作,並且在寄存器堆上設置獨立的讀寫口。這樣就可以在一個時鐘週期內同時完成了讀和寫的操作。

要設計一個新的處理器,結構冒險仍然是我們優先要考慮並解決的問題。但結構冒險在設計處理器時就考慮並解決好了,我們在使用時就不必考慮。

#數據冒險

示例一:一條指令需要使用之前指令的結果,但是結果還沒有寫回。
在這裏插入圖片描述
軟件解決方案:插入nop指令
在這裏插入圖片描述
但這種方法有個很大的問題,首先,插入nop指令的個數與流水線的結構相關,例如在5級流水線上正確運行的程序,在8級流水線上就不能正確運行。其次,我們希望對軟件屏蔽硬件儘可能多的細節。

那麼既然兩條nop指令就能解決的問題,我們可以嘗試在硬件上完成相同的工作。

解決方案一:流水線停頓,增加氣泡
在這裏插入圖片描述
解決方案二:數據前遞(Forwarding)

t0在EX階段就被計算出,所以可將它送到下一條指令ALU的輸入,而不需要添加氣泡。
在這裏插入圖片描述
在電路上的實現如下:在過600ps後,t0的值會被保存到EX/MEM這個流水線寄存器中,與此同時,加法指令正在執行,它需要將t0的值傳到ALU的輸入,顯然它直接從t0寄存器讀的值不是最新的,最新的在訪存階段的連線上,我們從硬件連線上把這個信號引回來,作爲ALU的輸入端。是否使用前遞的信號,我們需要根據是否出現數據冒險,來控制一個二路選擇器。
當然加法指令,也有可能是第二個源操作數使用t0的值,所以前遞信號也要連接到第二個輸入端,同樣這裏也要添加二路選擇器。
在這裏插入圖片描述

這樣的方式就被成爲前遞。它還有個名稱叫作旁路。那從根本上來說,前遞和旁路指的都是這件事情。只不過是觀察和描述的角度不同而已。前遞是從指令執行順序的角度來描述的,而旁路則是從電路的結構角度來描述。 本來前一條指令應該將運行的結果寫入到寄存器堆,然後再交給後一條指令使用,而我們現在搭建來一條新堆通路,相當於繞過了寄存器堆,直接進行了數據堆傳遞,所以從硬件時限的角度來看,這是一個旁路。那這就是前遞和旁路的關係。

那我們進一步來看,其實不僅僅在這個點可以建立旁路,我們在下一個流水級也可以建立旁路。

示例二:從MEM/WB階段前遞到ALU的情況
在這裏插入圖片描述
所以,再添加一條旁路
在這裏插入圖片描述

示例三:訪存指令出現數據冒險
在這裏插入圖片描述

這個單純的前遞也無法解決(前遞的箭頭方向正向下或者右下)

解決方案:流水線停頓+數據前遞
在這裏插入圖片描述

控制冒險

示例:尚未確定是否發生分支,如何進行下一次取指
在這裏插入圖片描述

解決方案一:流水線停頓,添加氣泡
在這裏插入圖片描述

前面說過添加氣泡效率很低,並不是一種較好的方法。我們可以從一下兩方面考慮:

一、假設分支不發生

例如,假設經過beq指令分支不發生,最壞情況是其實分支總是發生,所以執行兩條錯誤的lw、sw指令,又執行兩條正確的指令,這樣導致50%的性能浪費。
在這裏插入圖片描述

這也是因爲轉移指令本身和流水線的模式是衝突的,因爲轉移指令會改變指令的流向, 而流水線則希望能夠依次地取回指令,將流水線填滿。那如果這種情況是非常罕見的,也許我們還可以容忍,但實際上轉移指令是非常常用的指令。

二、縮短分支延遲

轉移指令的分類:
直接轉移:j target和beq rs,rt,imm兩種
間接轉移:jr t0
無條件直接跳轉(j target)

這種情況跳轉是確定發生的,且跳轉地址在取指階段就能得到,所以流水線不停頓。

這條指令的編碼當中,帶有一個26位的立即數,這個數就是要轉移的目標地址的主體部分, 但是我們的目標地址應該是32位的,所以還差6位,在差的6位當中,低兩位我們用0補上,因爲目標地址肯定是四字節對齊的,地址的低兩位肯定是0,然後還缺4位,我們通過當前的PC寄存器計算而得。先將PC寄存器的內容加4,得到的這個32位數,取其高4位,和26位地址以及最低的兩位的0連接起來,構成了一個32位的數,這就是轉移的目標地址。這些工作和取指可在一個時鐘週期內完成。
在這裏插入圖片描述
無條件間接跳轉(jr rs)

在譯碼階段得出跳轉地址,流水線需停頓一個週期。
在這裏插入圖片描述

條件跳轉(beq rs,rt,imm16)

不做優化,需要根據EX階段的結果,判斷是否跳轉,需要等待2個週期。
在這裏插入圖片描述

而實際上,比較兩個數是否相等是十分簡單的,只需在譯碼階段對寄存器的兩個輸出進行比較,這樣流水線停頓週期縮減爲1個週期

三、 延遲轉移技術

就是調整指令的順序,將一定會執行的指令放在分支指令後面,這樣流水線不停頓。注意,不能改變這段代碼原來的意義。

例如:可以將xor指令放到beq後面,經過xor指令後,beq不用等待正好可以執行,但是不能將addi或subi放到beq後面,因爲beq指令需要這兩個。
在這裏插入圖片描述

參考資料:https://www.coursera.org/learn/jisuanji-zucheng/lecture/fWOzV/606-kong-zhi-mou-xian-de-chu-li

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