資料危障(data hazard)
想像一份食譜,第 3 步寫著「拌入你在第 1 步做好的醬汁」。若你在裝配線上作業,而第 3 步的廚師伸手要醬汁時第 1 步的醬汁還沒做好,第 3 步就卡住了。資料危障是處理器版的這件事:一條後續指令需要一個值,而較早、仍在飛行中的指令本該產生它卻還沒寫回。
具體來說,看 add x1, x2, x3 緊接著 sub x4, x1, x5。sub 需要 x1,但 x1 是 add 的結果。在五階段管線裡,add 在它的 EX 階段算出 x1,卻要到兩週期後的 WB 才把它寫進暫存器檔。同時 sub 在 add 的 WB 之前就到了自己的 ID 階段(在那裡讀暫存器)——所以天真的管線會讓 sub 讀到舊的、過時的 x1。這就是危障。這種相依的技術名稱叫讀後寫(read-after-write):sub 必須讀到 add 寫的值。若不介入,程式就會算出錯誤答案。
資料危障是最常見的危障,主力解法是前饋(也叫旁路或轉送):add 的結果在 EX 就誕生了,所以把它直接從 EX/MEM 管線暫存器繞回 sub 的 ALU 輸入,而不必讓 sub 等暫存器檔。前饋能完全不損失週期地化解大多數資料危障。它唯一無法徹底解的是載入-使用的情況——載入的值要到 MEM 之後才備妥,晚了整整一階——這迫使一次停頓。編譯器會幫忙把獨立的指令排進那個空檔。
add x1, x2, x3 接著 sub x4, x1, x5。add 在第 3 週期(它的 EX)算完 x1,卻在第 5 週期(WB)才寫回。sub 在第 4 週期(它的 ID)讀暫存器——早了一個週期。前饋把 x1 從 add 的 EX 輸出直接遞進 sub 的 EX 輸入,一個週期都不損失。
讀後寫資料危障:消費者在生產者寫回之前就讀。前饋提早把新值繞過去,藏起停頓。
並非每對鄰近指令都是危障——唯有它們真的共用一個被前者寫、被後者讀的暫存器才算。硬體靠比對生產者的目的暫存器與消費者的來源暫存器來偵測;不相符,就沒危障。