管線化(pipelining)
想像一間自助洗衣店有三台機器:洗衣機、烘乾機,還有一張摺衣桌。你有四籃要洗的衣服。笨方法是:先洗第一籃、烘第一籃、摺第一籃,全部做完才開始洗第二籃。每籃要三個步驟,四籃就要花十二步的時間,而且大部分時間三台機器中有兩台閒著。聰明方法是裝配線:第一籃在烘的時候,就開始洗第二籃;第一籃在摺、第二籃在烘的時候,就開始洗第三籃。現在三台機器同時運轉。這就是管線化(pipelining)。
處理器用同樣的方式把指令管線化。執行一條指令要好幾個步驟:從記憶體提取它、解碼它要做什麼、做算術、存取記憶體、把答案寫回去。非管線化的 CPU 會把一條指令完全做完才開始下一條。管線化的 CPU 則把工作切成數個階段並讓它們重疊:當一條指令在執行階段時,下一條正在被解碼,再下一條正在被提取。有 k 個階段,就有最多 k 條指令同時在線上飛行,各自處於不同階段。時脈每個階段跳動一次,每跳一次每條指令往前一個階段,並從末端彈出一條完成的指令。
管線化是計算機結構中第一個偉大的效能構想,而且幾乎不花成本:你重複使用同一套硬體,只是在各階段之間加上暫存器來保存每條指令的狀態。但它有個初學者必須牢記的著名陷阱:管線化提升的是吞吐量(每秒完成多少條指令),卻不會降低任何單一指令的延遲。一籃衣服還是要洗加烘加摺那麼久;你並沒有讓任何一條指令變快,你只是讓機器不再閒置。這個領域接下來談的,就是會破壞順暢重疊的危障,以及讓生產線繼續前進的技巧。
五條指令、五階段管線。第 1 週期:I1 提取。第 2 週期:I1 解碼、I2 提取。第 3 週期:I1 執行、I2 解碼、I3 提取。到第 5 週期管線填滿:I1 寫回、I2 記憶體、I3 執行、I4 解碼、I5 提取——五條指令同時在線上。從第 5 週期起,每一個週期都完成一條指令。
管線一旦填滿,理想吞吐量就是每週期一條指令,即使每條指令從頭到尾仍然花了五個週期。
最常見的誤解:管線化不會讓任何一條個別指令變快。它靠重疊指令來提升吞吐量;單一指令的延遲不變(甚至因為多加的管線暫存器而略增)。