在指令管線化的過程中，主要有兩種技術可以用來處理指令之間的依賴問題，避免出現冒險（hazard）現象：

1. **轉發（Forwarding）**：
   轉發又稱為資料轉送（data forwarding）或資料重定向。當指令需要的數據尚未寫回暫存器，但已經在處理器的其他階段內可用時，可以直接將該數據從後面的管線階段轉發到前面的管線階段。這樣可以避免因為等待數據寫回而產生的延遲。轉發技術允許已經在管線中執行的指令輸出立即被後續指令接受，而不需要等待完整的寫回操作。

2. **延遲插入（Stalling）**：
   當無法使用轉發技術來解決依賴問題時，管線控制邏輯會選擇插入延遲（stall），也就是為了處理依賴問題而暫時停止一些指令的進一步執行。這樣可以確保在依賴數據可用之前，不會執行依賴於該數據的指令。雖然延遲會導致管線中的"氣泡"，降低效能，但在某些情況下仍然是必要的。

此外，還有其他技術如**分支預測（Branch Prediction）**來減少因程序控制流（例如條件語句）引起的冒險，但主要的依賴問題通常透過轉發和延遲插入來解決。這些技術共同作用，以最小化因數據依賴而導致的性能損失，實現更有效率的指令執行。
在指令管線化的過程中，如何處理指令之間的依賴問題，避免出現冒險現象？
**指令管線化**（英語：Instruction pipeline）是為了讓計算機和其它數位電子裝置能夠加速指令的通過速度（單位時間內被執行的指令數量）而設計的技術。

管線在處理器的內部被組織成層級，各個層級的管線能半獨立地單獨運作。每一個層級都被管理並且鏈結到一條「鏈」，因而每個層級的輸出被送到其它層級直至任務完成。 處理器的這種組織方式能使總體的處理時間顯著縮短。

未管線化的架構產生的效率低，因為有些 CPU 的模組在其他模組執行時是閒置的。管線化雖並不會完全消除 CPU 的閒置時間，但是能夠讓這些模組並行運作而大幅提升程式執行的效率。差不多有些類似流水線工廠的概念。

但並不是所有的指令都是獨立的。在一條簡單的管線中，完成一個指令可能需要 5 層。如右圖所示，要在最佳效能下運算，當第一個指令被執行時，這個管線需要運行隨後 4 條獨立的指令。可是，如果隨後 4 條指令依賴於第一條指令的輸出，管線控制邏輯器，就必須插入延遲時脈周期到管線內，直到依賴被滿足。而轉發技術能顯著減少延時。憑藉多個層，雖然管線化在理論上能提高效能，優勝於無管線的核心（假設時脈也因應層的數量按比例增加），但事實上，許多指令碼設計中並不會考慮到理想的執行。

## 簡介

管線化是假設程式執行時有一連串的指令要被執行（垂直座標 i 是指令集，水平座標表時間 t）。絕大多數當代的 CPU 都是利用時脈驅動。

而 CPU 是由內部的邏輯閘與正反器組成。當受到時脈觸發時，正反器得到新的數值，並且邏輯閘需要一段時間來解析出新的數值，而當受到下一個時脈觸發時正反器又得到新的數值，以此類推。而藉由邏輯閘分散成很多小區塊，再讓正反器鏈結這些小區塊組，使邏輯閘輸出正確數值的時間延遲得以減少，這樣一來就可以減少指令執行所需要的周期。

舉例來說，典型的 RISC 管線被分解成五個階段，每個階段之間使用正反器鏈結。

1. 讀取指令
2. 指令解碼與讀取暫存器
3. 執行
4. 記憶體存取
5. 寫回暫存器

## 優缺點

並非在所有情況下管線技術都起作用。可能有一些缺點。如果一條指令管線能夠在每一個時脈週期接納一條新的指令，被稱為**完整管線化**（fully pipelined）。因管線中的指令需要延遲處理而要等待數個時脈週期，被稱為非完整管線化。

當一名程式員（或者組合者 / 編譯者）編寫組合代碼（或者彙編碼）時，他們會假定每個指令是循序執行的。而這個假設會使管線化無效。當此現象發生後程式會表現的不正常，而此現象就是**危害**。不過目前有提供幾種技術來解決這些危害像是轉發與延遲等。

### 優點

1. 減少了處理器執行指令所需要的時脈週期，在通常情況下增加了指令的輸入頻率（issue-rate）。
2. 一些積體電路（combinational circuits），例如加法器（adders）或者乘法器（multipliers），通過添加更多的環路（circuitry）使其工作得更快。如果以管線化替代，能相對地減少環路。

### 缺點

1. 非管線化的處理器每次（at a time）只執行一個指令。防止分支延時（事實上，每個分支都會產生延時）和串行指令被並列執行產生的問題。設計比較簡單和較低生產成本。
2. 在執行相同的指令時，非管線化處理器的指令傳輸延遲時間（The instruction latency）比管線化處理器明顯較短。這是因為管線化的處理器必須在數據路徑（data path）中添加額外正反器（flip-flops）。
3. 非管線化處理器有固定指令位寬（a stable instruction bandwidth）。管線化處理器的效能更難以預測，並且不同的程式之間的變化（vary）可能更大。

## 範例

### 一般的管線

右圖是一般有 4 層管線的示意圖：

1. 讀取指令（Fetch）
2. 指令解碼（Decode）
3. 執行指令（Execute）
4. 寫回執行結果（Write-back）

上方的大灰色格是一連串未執行的指令；下方的大灰色格則是已執行完成的指令；中間的大白色格則是管線。

執行順序如以下列表所示：

#### 汽泡

指令執行中產生一個「打嗝」（hiccup），在管線中生成一個沒有實效的氣泡。

如右圖，在編號為 2 的時脈週期中，紫色指令的讀取被延遲，並且在編號為 3 的時脈週期中解碼層也產生了一個氣泡。所有在紫色指令之後的指令都被延遲執行，而在其之前已經執行了的指令則不受影響。

由於氣泡使指令執行延遲了一個時脈週期，完成全部 4 條指令的執行共需要 8 個時脈週期。

而氣泡處對指令的讀取、解碼、執行與寫回都沒有實質影響。這可以使用 nop 代碼來完成。

## 複雜化

很多處理器的管線深度到 5 層、7 層、10 層，甚至 31 層（像是 Intel Pentium 4 Prescott）。Xelerator X10q 甚至有多於 1000 層的管線深度 [1] （頁面存檔備份，存於網際網路檔案館）。

當程式出現分支將不利於過深管線，整條管線將會無效化。為了減輕此狀況，分支預測就變的重要。如果分支預測錯誤，也能夠藉由自行結束預測來避免加速惡化效率。在某些運用上，像是超級電腦運算，為了能夠將超長管線的運算優勢凸顯出來，會特地將程式寫的極少分支化來避免預測失敗，而且深度的管線化主要是為了能降低每個時脈執行的指令量而設計。當程式經常出現分支，把分支重新排序（像是將更為需要的指令提早放入管線中）而將明顯的降低損失的速度以避免將分支「沖垮」。像是 gcov 程式能夠使用一種覆蓋率檢查的技術檢查特定分支的執行頻率，但是這種檢查法經常是最佳化的最後手段。處理能力高的管線會因為很多分支的程式而降低效率，這是因為處理器不知道下一個要讀取的指令是甚麼，而需要等待完成分支指令而讓管線清空。處理完分支之後，下一個指令就要經過所有管線，直到整個指令集的結果出現，而處理器才會再繼續執行。而在極端的狀況下，管線化處理器的效能理論上可能會與未管線化處理器一致，甚至是每層管線都在待命狀態，而且指令經常在管線之中跑來跑去時的效能比較差一些。

由於指令管線化，處理器讀取機器碼時並不會立即執行。因為如此，在很接近的地方執行更新機器碼的動作就可能無法作用，因為這些機器碼已經進入預讀輸入隊列內。指令快取又會讓此現象更加惡化。不過這只會在能夠自我變更的程式出現此現象。

## 範例

### 範例一

一個典型的加法指令可能會寫成像 `ADD A, B, C`，而中央處理器（CPU）會將記憶體（Memory）內 A 位置與 B 位置的數值相加後放到 C 位置。在管線化處理器內，管線控制器會將這個指令分拆成一連串微指令：

```
LOAD R1, A
LOAD R2, B 
ADD R3, R1, R2
STORE R3, C
LOAD next instruction
```

_R1_, _R2_ 和 _R3_ 是 CPU 內的暫存器（register 是 CPU 裡面能夠快速存取的暫存記憶體）。主記憶體儲器內標註為 A 位置和 B 位置之儲存單元中的數值被載入（或稱複製）到暫存器 R1 和 R2 中，然後送到加法器中相加，結果輸出到暫存器 R3 中，R3 中的數值再被儲存到主記憶體儲器內標註為 C 位置的儲存單元。

而且在非管線化的例子，開始驅動加法動作到完成的時間是不變的。

在這個範例中的管線分為 3 層：載入，執行，儲存。每一步被稱為管線層（或稱管線階段，pipeline stages）。

在非管線化處理器中，同一時間只允許一個層運作，所以必須等待指令執行完畢才能開始執行下一條指令。在管線化處理器中，所有的層能在同一時間處理不同的指令。當一條指令在執行層，另外一條指令在解碼層，第三條指令在讀取層。

管線沒有減少執行指令所花費的時間；它增加了在同一時間被處理的指令數量，並且減少了完成指令之間的延遲。隨著處理器中管線層的數量增加，能在同一時間被處理的指令數量也相應增加，也減少了指令等待處理所產生的延遲。現在生產的微處理器至少有 2 層管線。[來源請求]（Atmel AVR 與 PIC 微控制器都有 2 層管線）Intel Pentium 4 處理器有 20 層管線。

### 範例二

以下表格具體列出 3 層管線理論：

以組合語言表示將會被執行的指令列表：

```
	LOAD  A, #40      ;讀取40載入A內
	MOVE  B, A        ;將A內的数据移动到B內
	ADD   B, #20      ;將B內的數據與20相加
	STORE 0x300, B    ;將B內的數據儲存到地址為0x300的存儲器單元
```

代碼的執行循序如下：

從主記憶體儲器中讀取 LOAD 指令。

LOAD 指令被執行，同時從主記憶體儲器中讀取 MOVE 指令。

LOAD 指令在儲存層（Store stage），LOAD 指令的執行結果 #40（the number 40）將被儲存到暫存器 A。同時，MOVE 指令被執行。MOVE 指令必須等待 LOAD 指令執行完畢才能將暫存器 A 中的內容移動到暫存器 B 中。

STORE 指令被載入，同時 MOVE 指令執行完畢，並且 ADD 指令被執行。

注意！有時候，一個指令會依賴於其他指令的執行結構（例如以上的 MOVE 指令）。當一個指令因為操作數而需參照一個特定的位置，讀取（作為輸入）或者寫入（作為輸入），執行那些指令的循序不同於程式原本的執行循序能導致冒險（hazards）。現時有機種技術用於預防危害，或者繞過（working around）它們。

## 參見

* 等待狀態
* 傳統的 RISC 管線
* 管線
* 並行運算
* 延遲間隙

## 外部連結

* 來自 ArsTechnica 的管線化相關文章 （頁面存檔備份，存於網際網路檔案館）
## 數據型冒險

指令調度一般在某基礎塊（basic block）上執行。為了確保指令的運行順序在重組後，其運算結果依舊不變，編譯器的開發者們必須要認識到_數據依賴_這種概念。數據型冒險總共有三種，其性質恰恰與數據型冒險相符，這三種數據冒險分別是：

1. 寫入後讀取（RAW, _Read After Write_） - **I1** 的輸出值之後會被 **I2** 使用。
2. 讀取後寫入（WAR, _Write After Read_） - **I1** 的輸入位置之後會被 **I2** 使用並覆蓋。
3. 寫入後寫入（WAW, _Write After Write_） - **I1** 以及 **I2** 皆將結果寫入同一個位置上。

其中 **I1** 以及 **I2** 是兩個在不同時間點上執行的指令。

對於這三種冒險，指令 **I1** 都必須執行於 **I2** 被執行前，否則其運算結果是不被定義的（具體結果視該機器的硬體實現而定）。對於冒險 1，這是因為 **I2** 依賴著 **I1** 的數據；對於冒險 2，則是因為 **I1** 依賴著即將被 **I2** 所覆蓋的數據；對於冒險 3，則是因為在 **I1** 和 **I2** 之間所運行的指令可能會使用到 **I1** 的輸出結果。為了確保這些數據依賴所需的運行順序得到保證，編譯器首先需要建立一幅依賴圖，即一幅頂點是指令，而邊是依賴性的有向圖。最終，這幅圖的任何一種拓撲排序都可以是有效的指令調度表。 **危障**（hazard）又稱作**冒險**或**險象**，是指在電腦 CPU 的微體系結構中，指令管線亂序執行中的一些問題可能會導致得到不正確的計算結果。有 3 類典型的危障：

* 資料危障
* 結構危障
* 控制危障（分支危障）

有幾種方法用於應對這些風險，如管線停頓（管線氣泡），暫存器直通等待。

## 背景

指令在一個管線處理器中的執行被分為若干階段，因此在一個時刻有多條指令處於管線的不同階段上被處理。如取運算元、執行等等。Intel 的 Pentium 就是處理器管線。有的處理器進一步是指令亂序執行，如 Intel 的 Pentium Pro 開創的 Intel P6 微體系結構。指令的亂序執行就會引入一些危險的情況。 **指令管線化**（英語：Instruction pipeline）是為了讓計算機和其它數位電子裝置能夠加速指令的通過速度（單位時間內被執行的指令數量）而設計的技術。

管線在處理器的內部被組織成層級，各個層級的管線能半獨立地單獨運作。每一個層級都被管理並且鏈結到一條「鏈」，因而每個層級的輸出被送到其它層級直至任務完成。 處理器的這種組織方式能使總體的處理時間顯著縮短。

未管線化的架構產生的效率低，因為有些 CPU 的模組在其他模組執行時是閒置的。管線化雖並不會完全消除 CPU 的閒置時間，但是能夠讓這些模組並行運作而大幅提升程式執行的效率。差不多有些類似流水線工廠的概念。

但並不是所有的指令都是獨立的。在一條簡單的管線中，完成一個指令可能需要 5 層。如右圖所示，要在最佳效能下運算，當第一個指令被執行時，這個管線需要運行隨後 4 條獨立的指令。可是，如果隨後 4 條指令依賴於第一條指令的輸出，管線控制邏輯器，就必須插入延遲時脈周期到管線內，直到依賴被滿足。而轉發技術能顯著減少延時。憑藉多個層，雖然管線化在理論上能提高效能，優勝於無管線的核心（假設時脈也因應層的數量按比例增加），但事實上，許多指令碼設計中並不會考慮到理想的執行。 **指令管線化**（英語：Instruction pipeline）是為了讓計算機和其它數位電子裝置能夠加速指令的通過速度（單位時間內被執行的指令數量）而設計的技術。

管線在處理器的內部被組織成層級，各個層級的管線能半獨立地單獨運作。每一個層級都被管理並且鏈結到一條「鏈」，因而每個層級的輸出被送到其它層級直至任務完成。 處理器的這種組織方式能使總體的處理時間顯著縮短。

未管線化的架構產生的效率低，因為有些 CPU 的模組在其他模組執行時是閒置的。管線化雖並不會完全消除 CPU 的閒置時間，但是能夠讓這些模組並行運作而大幅提升程式執行的效率。差不多有些類似流水線工廠的概念。

但並不是所有的指令都是獨立的。在一條簡單的管線中，完成一個指令可能需要 5 層。如右圖所示，要在最佳效能下運算，當第一個指令被執行時，這個管線需要運行隨後 4 條獨立的指令。可是，如果隨後 4 條指令依賴於第一條指令的輸出，管線控制邏輯器，就必須插入延遲時脈周期到管線內，直到依賴被滿足。而轉發技術能顯著減少延時。憑藉多個層，雖然管線化在理論上能提高效能，優勝於無管線的核心（假設時脈也因應層的數量按比例增加），但事實上，許多指令碼設計中並不會考慮到理想的執行。