指令级并行--计算机体系结构
參考書:《計算機體系結構量化研究方法》 作者:John L. Hennessy
一、基本概念
幾乎所有處理器都使用流水線來重疊指令的執行過程,以提高性能。由于指令可以并行執行,所以指令之間可能實現的這種重疊稱為指令級并行(ILP)。
ILP大體有兩種不同開發方法
- 依靠硬件來幫助動態發現和開發并行
- 依靠軟件技術在編譯時靜態發現并行
基本塊:一段順序執行代碼,除入口外沒有其他轉入分支,除出口外沒有其他轉出分支
對于一段指令可能相互依賴,所以在基本塊中可以開發的重疊數量可能要少于基本塊的平均大小,為了提高性能,我們必須跨越多個基本塊開發ILP。
提高ILP 的最簡單、最常見的方法就是在循環的各次迭代之間開發并行。這種并行經常被稱為循環級并行。
在開發指令級并行時,只要存在相關就必須保持程序順序執行,共有三種不同類型的相關:
- 數據相關(也稱真數據相關)
- 名稱相關
- 控制相關
在介紹相關之前,先介紹幾種冒險??紤]兩條指令i和j,其中i指令根據程序順序排在j的前面??赡艹霈F的冒險有:
? RAW冒險(寫后讀):j試圖在i寫入一個源位置之前讀取它,所以j會錯誤的獲得舊值
? WAW冒險(寫后寫):j試圖在i寫入一個操作數之前寫該操作數。
? WAR冒險(讀后寫):j嘗試在i讀取一個目標位置之前寫入該位置
1.1 數據相關
如果以下任一條件成立,則說j數據相關與指令i
- 指令i生成的結果可能會被指令j用到
- 指令j數據像關于指令k,指令k數據相關于指令i
第二個條件是說,如果兩條指令之間存在第一類型的相關鏈,那么兩條指令也是相關的??梢杂幸韵聝煞N不同的方法來克服相關性
- 保護相關性但避免冒險,比如代碼的靜態調度
- 通過轉換代碼來消除相關性,如寄存器重命名等
1.2 名稱相關
當兩條指令使用相同的寄存器或存儲器位置(或名稱),但與改名稱相關的指令之間并沒有數據流動時,就會發生名稱相關。共有如下兩種類型的名稱相關。
- 當指令j對指令i讀取的寄存器或存儲器位置執行寫操作時,就會在指令i和指令j之間發生反相關。為了確保指令i能夠讀取到正確的值,必須保持原來的順序。即剛才介紹的讀后寫(WAR)冒險
- 當指令i和指令j對同一個寄存器或存儲器位置執行寫操作時,發生輸出相關。即介紹的寫后寫(WAW)問題
解決方案:
改變這些指令中使用的名稱,使這些指令不再沖突,那名稱相關中涉及的指令就可以同時執行,或者重新排序。對于寄存器來說這一步驟更容易實現,這種操作成為寄存器重命名,可由編譯器靜態完成,也可由硬件動態完成。
1.3 控制相關
控制相關決定了指令i相對于分支指令的執行順序。最簡單的例子就是if語句。一般來說:控制相關會施加下述約束:
- 如果一條指令于一個分支控制相關,那就不能把這條指令移到這個分支之前,使它的執行不再受控于這個分支
- 如果一條指令于一個分支沒有控制相關,那就不能把這個指令移到分支之后,使其受控于這個分支
這些前提都是針對于分支來說的,知道這條指令是分支之后,就不能隨便進行代碼的動態調度,即必須保持嚴格的程序順序。但是,在不影響程序順序正確性的情況下,我們希望可以執行一些此時還不應當執行的指令。從而會違反控制相關。因此,控制相關不是必須要保持的特性。但其必須滿足異常行為和數據流。
異常行為是指,改變當前執行順序的操作。如,當執行某條指令,產生中斷,處理完中斷服務程序時,必須能夠還原到此指令的狀態。意思是,不能允許此條指令之后的程序先行提交,不能允許此條指令之前的指令晚于此條指令提交。
數據流是指數據值在生成結果和使用結果之前的實際流動。換種說法,保證數據流是指要避免WAW,RAW,WAR冒險。
由于存在著多種相關,所以必須保持程序順序。但在并行執行程序的過程中,僅在程序順序影響程序輸出時才保持程序順序。檢測和避免冒險可以確保不會打亂必要的程序順序。
二、ILP的基本編譯器技術
本節研究一些簡單的編譯器技術,可以用來提高處理器開發ILP的能力。這些技術對于使用靜態發射或靜態調度的處理器非常重要。
2.1 基本流水線調度
為了使流水線保持滿載,必須找出可以在流水線中重疊的不相關指令序列,充分開發指令并行。為了避免流水線停頓,必須將相關指令與源指令的執行隔開一定的時間周期,這一間隔等于源指令的流水線延遲。編譯器執行這種調度的能力即依賴于程序中可用ILP的數目,也依賴于流水線中功能單元的延遲。
舉一個循環的例子
for(i=999; i>=0; i=i-1)
x[i] = x[i] + s;
把這段代碼轉換為MIPS匯編語言。如下所示:
在進行任何調度的情況下,循環的執行過程如下,共花費9個時鐘周期:
LOOP: L.D F0,0(R1) ;F0=數組元素停頓ADD.D F4,F0,F2 ;加上F2中的標量停頓停頓S.D F4,0(R1) ;存儲結果DADDUI R1,R1,#-8 ;指向下一位停頓BNE R1,R2,LOOP ;R1不等于R2時跳轉可以看出,1次循環共花費了9個時鐘周期。但是我們可以循環這個調度,使其只有兩次停頓如下:
LOOP: L.D F0,0(R1) ;F0=數組元素DADDUI R1,R1,#-8 ;指向下一位ADD.D F4,F0,F2 ;加上F2中的標量停頓停頓S.D F4,0(R1) ;存儲結果BNE R1,R2,LOOP ;R1不等于R2時跳轉在調度之后,循環執行所花費的時間縮短至7個周期。但是實際運算僅占用7個時鐘周期中的3個(載入,求和及存儲)。其余四個時鐘周期包括循環開銷(DADDUI和BNE)和兩次停頓。為了消除4個時鐘周期,需要使循環體中的運算指令數多余開銷指令數。要提高運算指令相對于分支和開銷指令的數目,一種簡單的方案就是循環展開。即將循環體復制多次,調整循環的終止代碼。
如將上述指令進行循環展開:
我們將循環展開了四次減少了三次DADDUI以及BNE。這個循環將運行27個時鐘周期??梢钥吹?#xff0c;通過調循環展開可以顯著提高其性能。但相應開銷為
- 增加了代碼規模
- 增多了寄存器消耗
對以上代碼進行調度,執行情況如下:
LOOP: L.D F0,0(R1) L.D F6,0(R1) L.D F10,0(R1)L.D F14,0(R1) ADD.D F4,F0,F2 ADD.D F8,F6,F2 ADD.D F12,F10,F2 ADD.D F16,F14,F2 S.D F4,0(R1) S.D F8,-8(R1) DADDUI R1,R1,#-32 ;避免BNE的RAW冒險S.D F12,-16(R1)S.D F16,-16(R1)BNE R1,R2,LOOP ;R1不等于R2時跳轉展開后的循環的執行時間縮減到14個時鐘周期。比簡單的循環展開的性能提高了近一倍。
循環展開與調度均屬于靜態調度。此類技術的關鍵在于判斷合適能夠改變指令順序以及如何改變。為了獲得最終展開后的代碼,必須進行如下決策和轉換:
? 確認循環迭代不相關,判定循環展開是有用的
? 使用不同寄存器,以避免不同運算對寄存器導致的約束
? 去除多余的測試和分支指令
? 觀察不同迭代中的載入和存儲指令互不相關
? 對代碼進行調度,保留必要相關
要進行所有的這些變換,最關鍵的要求就是要理解指令之間的相關依賴關系。但是會有三種不同的效果限制循環展開:
? 每次展開操作分攤的開銷數目降低
? 代碼規模限制
? 編譯器限制,如寄存器緊缺等
三、分支預測
由于需要通過分支冒險和停頓來實施控制相關,所以分支會傷害流水線性能。循環展開是降低分支冒險的一種方法(因為去除了大多數分支跳轉指令)。我們還可以通過預測分支的行為方式來降低分支的性能損失。綜上,降低分支冒險的兩種方案:
- 循環展開
- 分支預測
如圖所示,一種二級自適應預測器可以記住過去n次執行該指令時的分支情況的歷史,可能的2n種歷史模式的每一種都有1個專用的飽和計數器,用來表示如果剛剛過去的n次執行歷史是此種情況,那么根據這個飽和計數器應該預測為跳轉還是不跳轉。
例如,n = 2。這意味著過去的2次分支情況被保存在一個2位的移位寄存器中。因此可能有4種不同的分支歷史情況:00, 01, 10, 11。其中0表示未發生跳轉,1表示發生了分支跳轉?,F在,設計一個模式歷史表(pattern history table),有4個條目,對應于2n = 4種可能的分支歷史情況。4種歷史情況的每一種都在模式歷史表對應于一個2位飽和計數器。分支歷史寄存器用于選擇哪個飽和計數器供現在使用。如果分支歷史寄存器是00,那么選擇第一個飽和計數器;如果分支歷史寄存器是11,那么選擇第4個飽和計數器。
假定,例如條件跳轉每隔2次執行就發生一次,即分支情況的歷史序列是001001001…。在這種情況下,00對應的飽和計數器將是狀態“強選擇”(strongly taken),表明在兩個0之后必然是出現一個1。01對應的飽和計數器將是狀態“強不選擇”(strongly not taken),表示在01之后必然是出現一個0。這也同樣適用于10狀態。而11狀態從未使用,因為不可能出現連續兩個1。
2級自適應預測器的一般規則是n位分支歷史寄存器,可以預測在所有n周期以內出現的所有的重復序列的模式。
(n,m)預測器指從n個分支中的2n行為中進行預測,每個預測其都是單個分支的m位預測器。
局部分支預測:
局部分支預測對于每個條件跳轉指令都有專用的分支歷史情況緩沖區;模式歷史表可以是專用的,也可以是所有條件分支共享。
全局分支預測
全局分支預測并不為每條條件跳轉指令保持專有的歷史記錄。相反,他保持一份所有條件跳轉指令共享的歷史記錄。優點是能識別出不同跳轉指令之間的相關性。缺點是歷史記錄被不相關的不同條件跳轉指令的執行情況稀釋了;甚至歷史記錄沒有一位是來自同一個分支指令,如果有太多的不同分支指令。這種方法必須保證歷史緩沖區足夠長才能發揮出性能。
競賽預測器
競賽預測器采用了多個預測器,通常是一個基于全局信息的預測器和一個基于局部信息的與預測其,用選擇器將他們結合起來。
四、動態調度與推測
簡單理解Tomasulo算法與重加載緩沖區
五、多發射和靜態調度
前幾節介紹了消除數據冒險和控制停頓地方法,使CPI(一條指令耗費的時鐘周期數)到達理想值1。為了提高性能,我們希望將CP降低至小于1。但是我們如果每一個時鐘周期只發射一條指令,那CPI的值是不可能降低到小于1的。
因此開發了多發射處理器,目標:允許一個時鐘周期中發射多條指令。多發射處理器主要有以下三類
- 靜態調度超標量處理器
- VLIW(超長指令字)處理器
- 動態調度超標量處理器
不同處理器采用靜態調度的方式循序執行指令,動態調度處理器采用動態調度亂序執行指令。
VLIW處理器每個時鐘周期發射固定數目的指令,這些指令可以設置為兩種指令之一:一種格式是長指令;另一種是固定長度的指令包,指令之間有一定的并行度。VLIW處理器由編譯器采用靜態調度。
靜態調度超標量處理器在每個時鐘周期內發射的指令數目是可變的,不是固定的。但它在概念上與VLIW更接近一些,因為這種方法都依靠編譯器為處理器調度代碼。盡管靜態調度超標量處理器的收益會隨著發射寬度的增長而逐漸減少,所以靜態調度超標量處理器最主要用于發射寬度較窄的情況,通常只有兩條指令。下圖總結了多發射的基本方法和他們的突出特征:
5.1 VLIW處理器
VLIW使用多個獨立功能單元。VLIW沒有嘗試向這些單元發射多條獨立指令,而是將多個操作包裝在一個非常長的指令中。VLIW的收益會隨著發射指令長度的增長而增長,所以VLIW主要用在寬發射處理器中。
我們考慮一個VLIW處理器,在上面運行5種運算的指令,這五種運算是:一個整數運算、兩個浮點運算和兩個存儲器引用。
為了使功能單元繁忙,代碼序列必須具有足夠的并行度,以填充可用操作插槽。這種并行是通過展開循環和調度單個更大型循環體中的代碼而展開的。舉個簡單的例子,現需要將循環x[i] = x[i] + s展開。VLIW執行情況如下:
執行情況如上所示,該循環被展開后形成了循環體的7個副本,消除了所有停頓,運行了9個時鐘周期,生成了7個結果。
但是VLIW模塊仍然有相當多的局限:
1) 靜態調度增大了代碼的大小
2) 只要指令未被填滿,那些沒有用到的功能單元都會在指令編碼時變為多余的位
3) 沒有冒險檢測硬件,所有的功能單元都必須保持同步
4) 二進制代碼的兼容性
5.2 以動態調度、多發射和推測來開發ILP
在動態調度處理器中,每個時鐘周期發射多條指令都非常復雜,原因很簡單,這些指令之間可能存在相關性。因此必須為這些并行指令更新控制表;否則,這些控制表中可能會出現錯誤,或者會丟失相關性。
在動態調度處理器中,已經采用兩種方法在每個時鐘周期內發射多條指令,這兩種方法都基于這樣一個事實:要在每個時鐘周期中發射多條指令,其關鍵在于保留站的分配和流水線控制表的更新。兩種方法如下:
- 在一個時鐘周期的一半時間運行這一步驟,從而可以在一個時鐘周期內運行兩條指令
- 構建必要的邏輯,一次處理兩條或者多條指令,包括指令之間可能存在的相關性。
第一種方法僅適應兩條指令發射,無法處理多條指令發射。這兩種方法每個時鐘周期都會發射新的指令,所以必須能夠分配保留站,并更新流水線表,使下一個時鐘周期進行的相關指令發射能夠利用更新后的信息。
下圖給出了一種情境下的發射邏輯
該圖并沒有顯示指令發射邏輯,這在稍后將會介紹到。
為了實現多發射的動態調度,必須做到:
? 為可能在下一個發射包中發射的每條指令指定保留站和重排序緩沖區。
? 分析發射包中指令之間的所有相關
? 如果包中的一條指令依賴于包中的先前指令,則使用指定的重排序緩沖區編號來更新相關指令的保留表
六、用于指令傳送和推測的高級技術
在高性能流水線中,特別是在多發射流水線中,僅僅很好的預測分支還不夠;實際上還得能夠提交高帶寬的指令流。多發射處理器需要每個時鐘周期提取的平均指令數至少等于平均吞吐量。當然,提取這些指令需要有足夠寬的路徑能夠連向指令緩存,但最重要的部分還是分支的處理。在本節中先討論兩種處理分支的辦法,然后討論現在處理器如何將指令預測和預取功能結合在一起。
6.1 分支目標緩沖區
分支預測有兩種情況:
- 檢測到分支,預測程序計數器PC
- 未檢測到分支,該干嘛干嘛
如果檢測到分支,需要預測下一個程序技術去PC應當是什么,如果預測正確,就可以將分支代價將為0。于是,就需要一個分支預測緩存,用以存放分支下一條指令的預測地址,這一個緩存被稱為分支目標緩沖區或分支目標緩存。如圖所示:
需要注意的是,我們只需要在分支目標緩沖區中存儲預測選中的分支,而不用存儲非分支的一般指令。因為未被選中的分支應當直接提取下一條指令。如果預測正確,就不存在分支延遲;否則,至少存在兩個時鐘周期的代價。下圖使分支目標緩沖區中處理指令時涉及的步驟。
分支目標的一種變體使存儲一個或多個目標指令,用于作為預測目標地址的補充或替代。這一變體有兩個好處。第一,它允許分支目標緩沖區訪問花費的時間長于連續兩次指令提取之間的時間,從而可能允許采用更大型的分支目標緩沖區。第二,通過緩存實際目標指令可以讓我們一種稱為分支折合的優化方法。其可以實現0時鐘周期的無條件分支,也有可能實現0時鐘周期的分支條件。
6.2集成指令提取單元
為了滿足多發射處理器的要求,進來的設計人員選擇實現了一個集成指令提取單元,作為獨立的自主單元,為流水線的其余部分提供指令。這是因為:由于多發射的復雜性,不能再將取指過程視為簡單的單一流水線。
最近的設計已經開始使用集成了多種功能單元的集成指令提取單元,包括以下功能:
1) 集成分支預測:分支預測器變為指令提起單元的組成部分,它持續預測分支
2) 指令預取:為了在每個時鐘周期內提交多條指令,指令提取單元可能需要提取指令。
3) 指令存儲器訪問與緩存:在每個時鐘周期提取多條指令時會遇到不同的復雜性,包括:提取多條指令可能需要訪問多個緩存行。
6.3 推測:實現問題與擴展
ROB(重排序緩沖區)的一種替代方法時明確使用更大的物理寄存器集,并與寄存器重命名方法結合在一起。在Tomasulo算法中,在執行的任意時刻,體系結構可見的寄存器都包含在寄存器集和保留站的某一個組合中。在添加了推測功能后,寄存器值還會臨時保存在TOB中。在任一情況下,如果處理器在一段時間內沒有發射新指令,所有現有指令都會 提交,寄存器值將出現在寄存器堆中,寄存器堆直接與在體系結構中可見的寄存器相對應。
擴展后的寄存器取代了ROB的發部分功能;只需要一個隊列來確保循序完成指令。在指令的發射期間,一種重命名過程會將體系結構寄存器的名稱映射到擴展寄存器堆中的物理寄存器編號,為目的地分配一個新的未使用寄存器。在提交指令時重命名表被永久更新。
與ROB相比,重命名方法的一個優點時簡化了指令的提交過程,它只需要兩個簡單操作:
- 記錄體系結構寄存器編號與物理寄存器編號之間的映射不再是推測結果
- 釋放所有用于保存體系結構寄存器舊值的物理寄存器
但是在釋放之前必須知道它不在與體系結構寄存器相對應,而且該物理寄存器的所有使用都已完成。
動態調度超標量的關鍵復雜性瓶頸仍然在于所發射的指令包中包含相關性的情景。在采用寄存器重命名發射指令時,所部署的策略可以類似于采用重排序緩沖區進行發射時的策略,如下
1) 發射邏輯預先為整個發射包保留足夠的物理寄存器
2) 發射邏輯判斷包中存在的相關性
3) 如果一條指令依賴于該發射包排列在前的某條指令,那么將使用在其中存放結果的預留物理寄存器來為發射指令更新信息
6.4 值推測
值推測嘗試預測一條指令可能生成的值。當然,值預測的成功率非常有限。
不過有種比較簡單的與值預測相關的較早思想已經得到了應用,那就是地址別名預測。地址別名預測用來預測兩個存儲指令或者一個載入指令與一個存儲指令是否引用同一個存儲器地址。如果這樣兩條指令沒有應用一個地址,那就可以放心地交換他們的順序。否則,就必須等待。
七、多線程
多線程是向硬件展示更多并行的主要技術。盡管使用ILP來提高性能有很大優勢:他對編程人員適度透明。但是在很多種情況下,ILP會受到很大的局限或者難以開發,主要的局限性有以下三點:
1) 訪問存儲器的WAW和WAR冒險:這一研究通過寄存器重命名消除了WAW和WAR冒險,但卻沒有消除存儲器使用中的冒險。
2) 不必要的相關:有無數個寄存器時,就可以消除真寄存器數據相關之外的所有數據相關。但是由于遞歸或代碼生成約定而造成的相關仍然會引入一些不必要的真數據相關
3) 克服數據流限制:如果值預測的精度很高,那就可能克服數據流限制。但目前為止,沒有一種現實預測方案來顯著提高ILP。顯然完美的數據預測可以得到高效的無限并行,因為每個指令的每個值都可能提前預測得出。
另外,當指令發射率處于合理范圍時,那些到達存儲器或片外緩存的缺失不太可能通過可用ILP隱藏。由于人們試圖通過更多的ILP來應對很長的存儲器停頓時,效果非常有限。
多線程技術支持多個線程以重疊的方式共享單個處理器的功能單元。與之相對的是,開發線程級并行TLP的更一般方法是使用多處理器,它同時平行運行多個獨立線程。但是多線程不會像多處理器那樣復制整個處理器。而是在一組線程之間共享處理器核心的大多數功能,僅復制私有狀態,比如寄存器和程序計數器。
要復制處理器核心中每個線程的狀態,就要為每個線程創建獨立的寄存器堆、獨立的PC和獨立的頁表。當然,硬件必須支持對不同線程進行較快速的修改;具體來說:線程切換的效率應當遠遠高于進程切換,后者通常需要上百個到數千個時鐘周期。實現多線程的硬件方法主要有三種。
1) 細粒度多線程:每個時鐘周期在多線程之間進行一次切換,多個線程的指令執行過程交織在一起。好處是它能隱藏因為長短停頓的所導致的吞吐量損失。主要不足是他會減緩個體線程的執行程度
2) 粗粒度多線程:其設計目的就是用來作為細粒度的替代選項。粗粒度多線程僅發生在成本較高的停頓時才切換線程。粗粒度多線程有個嚴重的不足:克服吞吐量損失的能力非常有限,特別是由于較短停頓導致的損失
3) 同時多線程(SMT):利用線程級并行來隱藏處理器中的長延遲事件,從而提高功能單元的利用率。SMT的關鍵在于認識到通過寄存器重命名和動態調度可以執行來自獨立線程的多個指令,而不用考慮這些指令之間的相關性;這些相關性留給動態調度功能來處理。
下面是這些方法應用在一個超標量處理器功能單元執行槽的表現情況:
小結:
總結
以上是生活随笔為你收集整理的指令级并行--计算机体系结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简单理解Tomasulo算法与重加载缓冲
- 下一篇: 寒冰剑是谁的武器