本文將討論處理器的一個重要的基礎知識:“流水線”。熟悉計算機體系結構的讀者一定知道,言及處理器微架構,幾乎必談其流水線。處理器的流水線結構是處理器微架構最基本的一個要素,猶如汽車底盤對于汽車一般具有基石性的作用,它承載并決定了處理器其他微架構的細節。本文將簡要介紹處理器的一些常見流水線結構,讓您真正讀懂處理器流水線。
1 從經典的五級流水線說起
流水線的概念來源于工業制造領域,以汽車裝配為例來解釋流水線的工作方式,假設裝配一輛汽車需要四個步驟:
第一步沖壓:制作車身外殼和底盤等部件。
第二步焊接:將沖壓成形后的各部件焊接成車身。
第三步涂裝:將車身等主要部件清洗、化學處理、打磨、噴漆和烘干。
第四步總裝:將各部件(包括發動機和向外采購的零部件)組裝成車。
汽車裝配則同時對應需要沖壓、焊接、涂裝和總裝四個工人。最簡單的方法是一輛汽車依次經過上述四個步驟裝配完成之后,下一輛汽車才開始進行裝配,最早期的工業制造就是采用的這種原始的方式,即同一時刻只有一輛汽車在裝配。不久之后人們發現,某個時段中一輛汽車在進行裝配時,其它三個工人都處于閑置狀態,顯然這是對資源的極大浪費,于是思考出能有效利用資源的新方法,即在第一輛汽車經過沖壓進入焊接工序的時候,立刻開始進行第二輛汽車的沖壓,而不是等到第一輛汽車經過全部四個工序后才開始,這樣在后續生產中就能夠保證四個工人一直處于運行狀態,不會造成人員的閑置。這樣的生產方式就好似流水川流不息,因此被稱為流水線。
計算機體系結構教材中被提及最多的經典MIPS五級流水線如圖1所示。在此流水線中一條指令的生命周期分為:
取指:
指令取指(Instruction Fetch)是指將指令從存儲器中讀取出來的過程。
譯碼:
指令譯碼(Instruction Decode)是指將存儲器中取出的指令進行翻譯的過程。經過譯碼之后得到指令需要的操作數寄存器索引,可以使用此索引從通用寄存器組(Register File,Regfile)中將操作數讀出。
執行:
指令譯碼之后所需要進行的計算類型都已得知,并且已經從通用寄存器組中讀取出了所需的操作數,那么接下來便進行指令執行(Instruction Execute)。指令執行是指對指令進行真正運算的過程。譬如,如果指令是一條加法運算指令,則對操作數進行加法操作;如果是減法運算指令,則進行減法操作。
在“執行”階段的最常見部件為算術邏輯部件運算器(Arithmetic Logical Unit,ALU),作為實施具體運算的硬件功能單元。
訪存:
存儲器訪問指令往往是指令集中最重要的指令類型之一,訪存(Memory Access)是指存儲器訪問指令將數據從存儲器中讀出,或者寫入存儲器的過程。
寫回:
寫回(Write-Back)是指將指令執行的結果寫回通用寄存器組的過程。如果是普通運算指令,該結果值來自于“執行”階段計算的結果;如果是存儲器讀指令,該結果來自于“訪存”階段從存儲器中讀取出來的數據。
在工業制造中采用流水線可以提高單位時間的生產量,同樣在處理器中采用流水線設計也有助于提高處理器的性能。以上述的五級流水線為例,由于前一條指令在完成了“取指”進入“譯碼”階段后,下一條指令馬上就可以進入“取指”階段,依次類推,如圖2所示,如果流水線沒有停頓,理論上可以取得每個時鐘周期都完成一條指令的性能。
圖1 MIPS五級流水線結構圖
圖2 MIPS五級流水線運行圖
2 可不可以不要流水線——流水線和狀態機的關系
言及處理器微架構,幾乎必談流水線。那么,我們能否挑戰一下權威提出一個有意思的問題:處理器難道就一定需要流水線嗎?可否不要流水線呢?
在回答這個問題之前,我們先探討下流水線的本質:
流水線并不限于處理器設計,在所有的ASIC電路實現中都廣泛采用流水線的思想。流水線本質上可以理解為是一種以面積換性能(Trade Area for Performance)、以空間換時間(Trade Space for Timing)的手段。
譬如,以5級流水線為例,其增加了5組寄存器,每一個流水線級數內部都有各自的組合邏輯數據通路,彼此之間沒有復用資源,因此,其面積開銷是比較大的,但是由于可以讓不同的流水線級數同時做不同的事情,而達到流水的效果,提高了性能,優化了時序,增加了吞吐率。
與流水線相對應的另外一種策略是狀態機,狀態機是流水線的“取反”,同樣在所有的ASIC電路實現中都廣泛采用。狀態機本質上可以理解為是一種以性能換面積(Trade Performance for Area)、以時間換空間(Trade Timing for Space)的手段。
“流水線”和“狀態機”的關系,還有一種說法稱之為“展開”和“折疊”的關系。本質上都是一種電路設計時,選擇側重時間(性能)還是空間(面積)的一種取舍。
通過上述分析,假設處理器不采用流水線,而是使用一個狀態機來完成,則需要多個時鐘周期才能完成一條指令的所有操作,每個時鐘周期完成狀態機的一個狀態(譬如分別為取指、譯碼、執行、訪存和寫回)。通過使用狀態機,可以省掉上述流水線中的寄存器開銷,還可以復用組合邏輯數據通路,因此面積開銷比較小,但是每條指令都需要5個周期才能完成,吞吐率和性能很差。
談及此處,就不得不提及8位單片機時代的傳奇老炮兒8051內核,早期原始的8051內核微架構就是采用了類似狀態機的實現方式而不是流水線。因此,回到最開始我們提出的問題,處理器可否不要流水線,答案是:當然可以,傳奇老炮兒8051內核就沒有流水線。
所以說從功能能上來講,處理器完全可以不使用流水線,而使用狀態機的方式來實現,只不過由于這種方式性能比較差,在現代處理器設計中比較罕見而已。
3 深處種菱淺種稻,不深不淺種荷花——流水線的深度
流水線的級數(又稱深度)多少最好呢?要回答這個問題,就需要了解流水線的深淺各自的優劣。此處有一個常見面試題,題目便是:處理器的流水線是否越深越好?在此我們給出答案:
早期的經典流水線是5級流水線,分別為取指、譯碼、執行、訪存和寫回。現代的處理器往往具有極深的流水線級數,譬如高達十幾級,或者二十幾級的深度。流水線就像一根黃瓜,切五刀下去得到的每一截長度和切二十到下去得到的每一截長度肯定是不一樣的。當流水線的級數越多,那么意味著流水線被切的很細,每一級流水線內容納的硬件邏輯便越少,熟悉數字同步電路設計的讀者應該比較熟悉,在兩級寄存器(每一級流水線由寄存器組成)之間的硬件邏輯越少,則意味能夠運行到更高的主頻。因此現代的處理器流水線極深主要是由于處理器追求高頻的指標所驅使,高端的ARM Cortex-A系列由于有十幾級的流水線,所以能夠運行到高達2GHz的主頻,而Intel的x86處理器甚至采用幾十級的流水線深度將主頻推到3-4GHz的高度。主頻越高也意味著流水線的吞吐率越高從而性能越高,這是流水線加深的正面意義。
由于每一級流水線都由寄存器組成,那么意味著更多的流水線級數要消耗更多的寄存器,也意味著更多的面積開銷。這是流水線加深的負面意義。
同時流水線越深,由于每一級流水線需要進行握手,流水線最后一級的反壓信號可能會一直串擾到最前一級造成嚴重的時序問題,需要使用一些比較高級的技巧來解決此類反壓時序問題。這是流水線加深的負面意義。
較深的處理器流水線還有一個問題,由于在流水線的取指令階段無法得知條件跳轉的結果是跳還是不跳,因此只能進行預測,而到了流水線的末端才能夠通過實際的運算得知該分支是真的該跳還是不該跳,如果發現真實的結果(譬如該跳)與之前預測的結果(譬如預測為不跳)不相符,則意味著預測失敗,需要將所有預取的錯誤指令流全部丟棄掉,而重新取正確的指令流,這個過程叫做流水線沖刷(Pipeline Flush),雖然可以使用分支預測器來保證前期的分支預測盡可能的準確,但是也無法做到萬無一失。那么,流水線的深度越深,則意味著已經預取了很多的錯誤指令流,需要將其全部拋棄然后重啟,不僅白白的浪費了功耗,還造成了性能的損失。流水線越深則意味著浪費和損失越嚴重,流水線越淺則浪費和損失越少。這是流水線加深的另一個主要的負面意義。
綜上,所謂深處種菱淺種稻,不深不淺種荷花,流水線的不同深度皆有其優缺點,需要根據不同的應用背景合理地進行選擇。
由于處理器流水線深淺的不同優劣,根據不同的應用場景,當今處理器的流水線深度在向著兩個不同的極端發展,一方面級數越來越深,另一方面又越來越淺,下面我們結合不同的商用處理器例子予以探討。
評論