載入-使用危障(load-use hazard)
想像你叫一名跑者去遠處的書架拿一本書,而你接下來要做的第一件事就取決於那本書裡的內容。無論跑者多快,你都不可能在書送到之前讀它。載入-使用危障正是這個尷尬的情況:一條指令從記憶體載入一個值,而緊接的下一條指令想用它——但那個值就是來不及從記憶體回來。
它是前饋獨力無法徹底解的那一個資料危障,原因在時序。在五階段管線裡,一般的算術結果在 EX 結束時誕生,早得足以前饋給下一條指令的 EX。但載入的值要到 MEM 結束才存在——晚了一階。下一條指令在載入的值備妥之前就到了自己的 EX,所以連最快的前饋線此刻也沒東西可送。因此硬體必須讓使用的指令恰好停頓一個週期;在那單一氣泡之後,從 MEM/WB 暫存器來的前饋恰好準時遞交載入的值。
這就是為什麼載入-使用危障在實務上重要:載入極為常見,所以若什麼都不做,這一週期的懲罰會不斷糾纏程式。經典的對策是排程——編譯器重排程式碼,把一條獨立、不相關的指令塞進緊接載入之後的槽,讓那個本會是氣泡的週期改做有用的工作。某些較舊的設計甚至把它暴露為程式設計師必須填的「載入延遲槽」。載入-使用危障是教科書級的證明:前饋有極限,而聰明的指令排序仍能掙得真實的速度。
lw x1, 0(x2) 接著 add x3, x1, x4 迫使一次停頓,因為 x1 要到 MEM 之後才備妥。但把一條無害、獨立的指令重排進那個空隙——lw x1, 0(x2); or x6, x7, x8; add x3, x1, x4——載入延遲就被藏起來,不花成本。
載入-使用危障付出一次停頓,因為載入的值晚一階、來不及前饋;把一條獨立指令排進空隙就能藏起它。
前饋並未消除載入-使用停頓,只是把它縮成一個週期。唯有「立即」使用載入結果的指令才付代價;若載入與其使用之間哪怕只夾一條獨立指令,就完全沒有停頓。