現代處理器運作 (Modern Processor Operation)

Overview Table

主題 核心概念 關鍵詞
兩大效能下界 (p.554) 程式效能受 latency boundthroughput bound 限制 latency bound / throughput bound
整體運作 (5.7.1, p.554) Superscalar + out-of-order:ICU 產生 micro-operations,EU 平行執行 ICU / EU / superscalar
分支處理 (p.555) Branch prediction + speculative execution;猜錯則 flush 重來 misprediction penalty
指令解碼 (p.556) 一條 x86 指令拆成多個 primitive operations (micro-ops) load / op / store 分離
Retirement (p.557) Retirement unit 以 FIFO queue 確保 sequential semantics;retire 時才更新暫存器 retire / flush
Register renaming (p.558) 以 tag (r, t)(v, t) 轉發結果,免經 register file,支撐 speculative 執行 data forwarding
功能單元效能 (5.7.2, p.559) 每種運算以 latency / issue time / capacity 三參數刻畫 fully pipelined / divider 不 pipeline
最大吞吐量 (p.560) throughput = C/I ops/cycle;load unit 也構成 throughput bound 2 load units → 0.50
抽象模型 (5.7.3, p.561) Data-flow graphcritical path → CPE 下界 loop register / critical path
combine4 分析 (p.564–565) acc 更新鏈長 Ln → CPE = L(latency bound);整數加法 1.27 例外 其他資源限制

5.7 為什麼要理解微架構 (p.553–554)

前面各節的最佳化(消除函式呼叫、記憶體參照等 optimization blockers)不依賴目標機器特性;要再往前推,必須利用處理器的微架構 (microarchitecture)——處理器執行指令的底層系統設計。

本節所有 CPE 實測值基於 Intel Core i7 Haswell 參考機;數字因機器而異,但運作原理與最佳化原則通用於各種機器 (p.553)。

5.7.1 整體運作 (Overall Operation, p.554–558)

現代處理器是 superscalar(每個 clock cycle 可執行多個運算)且 out of order(執行順序不必對應機器碼順序)。整體設計分兩大部分:instruction control unit (ICU) 負責從記憶體讀指令並產生 primitive operations;execution unit (EU) 負責執行這些運算。相較第 4 章的 in-order pipeline(04-Processor-Architecture/05-Pipelined-PIPE-Implementation-and-Hazards),out-of-order 硬體複雜得多,但能達到更高 ILP。

        Instruction Control Unit (ICU)
  +--------------------------------------------+
  | Retirement     Fetch    --Address-->       |
  |   unit        control            Instruction
  | [Register    Instruction <--Instructions- cache
  |   file]        decode                      |
  +------------------|-------------------------+
      ^  Register    | Operations      ^
      |  updates     v                 | Prediction OK?
  +--------------------------------------------+
  | Branch  Arith  Arith   Load     Store      |  Functional
  |   |      ops    ops     |         |        |  units
  |   +--Operation results--+-Addr    +-Addr   |
  |      (forwarding)       |  ^Data  |  Data  |
  |                         v  |      v        |
  |                       [ Data cache ]       |
  +--------------------------------------------+
              Execution Unit (EU)

Fetch 與分支預測 (p.554–555)

書中 "branch" 專指條件跳躍指令;procedure return 與 indirect jump 等可轉往多個目的地的指令,對處理器帶來類似挑戰 (p.554 註 1)。

指令解碼為 micro-operations (p.555–556)

Decode 邏輯把實際指令轉成一組 primitive operations(micro-operations),每個只做一件簡單事(加兩數、讀記憶體、寫記憶體)。拆解目的:讓專用硬體單元分工,平行執行多條指令的不同部分。解碼細節各機器不同且高度保密,但不需知道底層細節也能做最佳化

指令 解碼結果
addq %rax,%rdx(純暫存器) 1 個 operation
addq %rax,8(%rdx)(含記憶體) 3 個 operations:load(讀記憶體)、add(相加)、store(寫回),記憶體參照與算術分離

功能單元 (Functional Units, p.556–557)

EU 每個 cycle 可接收多個 operations,分派 (dispatch) 給特化的功能單元:

Intel Core i7 Haswell 參考機有 8 個功能單元 (0–7)(p.557 部分能力):

單元 能力
0 整數算術、FP 乘法、整數與 FP 除法、branch
1 整數算術、FP 加法、整數乘法、FP 乘法
2, 3 Load、位址計算
4 Store(資料)
5 整數算術
6 整數算術、branch
7 Store 位址計算

Retirement unit 與 register renaming (p.557–558)

Retirement unit(位於 ICU,管轄 register file:整數、浮點、SSE/AVX 暫存器)追蹤處理進度,確保符合機器碼的循序語意:

為加速結果在指令間傳遞,執行單元之間直接交換 "operation results"(第 4 章 data forwarding 的進化版,參見 04-Processor-Architecture/04-Pipelining-Principles),最常見機制為 register renaming:

decode:  某指令將更新暫存器 r → 產生唯一 tag t,
         renaming table 加入項目 (r, t)
decode:  後續指令以 r 為運算元 → 送往 EU 的 operation 以 t 為來源
execute: 第一個 operation 完成 → 產生結果 (v, t) 廣播
         所有等待 t 的 operations 直接以 v 為來源值(forwarding)
歷史 (Aside, p.558):out-of-order 處理最早見於 CDC 6600 (1964,10 個獨立功能單元,10 MHz);IBM 360/91 (1966) 僅用於浮點指令;之後約 25 年被視為異國技術,IBM RS/6000 (1990) 重新引入,PowerPC 601 (1993) 成為首顆採用 OoO 的單晶片微處理器;Intel 自 PentiumPro (1995) 引入,微架構與本節參考機類似。

5.7.2 功能單元效能 (Functional Unit Performance, p.559–560)

每種運算以三個參數刻畫(Figure 5.12,Haswell 參考機,對其他處理器亦具代表性):

運算 Int latency Int issue Int capacity FP latency FP issue FP capacity
加法 1 1 4 3 1 1
乘法 3 1 1 5 1 2
除法 3–30 3–30 1 3–15 3–15 1

由此得出 combining 函式 CPE 的兩個基本下界(p.560):

Bound int + int * FP + FP *
Latency bound 1.00 3.00 3.00 5.00
Throughput bound 0.50 1.00 1.00 0.50
Throughput bound 的例外細節:整數加法 capacity 4 理論上可達 0.25,但只有 2 個 load units,每 cycle 最多讀 2 個資料值 → 需逐元素讀記憶體的 combining 運算,throughput bound 為 0.50。整數乘法僅 1 個單元、issue 1 → 無法超過每 cycle 1 個乘法。

5.7.3 處理器運作的抽象模型 (p.561–565)

分析工具:data-flow graph(資料流圖)——非正式的圖形表示法,顯示 operations 之間的資料相依如何限制執行順序;相依鏈形成 critical path(關鍵路徑),給出執行一組指令所需 clock cycles 的下界

combine4 的 CPE 實測(p.561)幾乎全部貼合 latency bound:

Function int + int * FP + FP *
combine4(accumulate in temporary) 1.27 3.01 3.01 5.01
Latency bound 1.00 3.00 3.00 5.00
Throughput bound 0.50 1.00 1.00 0.50

這不是巧合:計算 n 個元素的積/和需約

TLn+K

其中 L 為 combining 運算的 latency,K 為呼叫函式與迴圈起訖的 overhead,故 CPE = latency bound L

從機器碼到 data-flow graph (p.561–562)

combine4 內迴圈(data_t = double、OP = *;%rdx = data+i、%rax = data+length、%xmm0 = acc):

.L25:                             # loop:
  vmulsd (%rdx), %xmm0, %xmm0    #   acc *= data[i]
  addq   $8, %rdx                 #   data+i 前進
  cmpq   %rax, %rdx               #   與 data+length 比較
  jne    .L25                     #   若 != 繼續迴圈

4 條指令被 decoder 動態展開為 5 個 operations:vmulsd 拆成 load(讀來源運算元)+ mul;兩者出自同一條指令,其間的中間值不對應任何暫存器cmp 更新 condition codes,由 jne 測試。

迴圈中被存取的暫存器分四類(p.562–563):

類別 定義 combine4 中的例子
Read-only 只當來源值(資料或算位址),迴圈內不被修改 %rax
Write-only 只作為 data-movement 的目的地 (此迴圈無)
Local 迴圈內更新且使用,但無跨迭代相依 condition codes(cmp 寫、jne 讀,同一迭代內完結)
Loop 既是來源又是目的地,某迭代產生的值被另一迭代使用 %rdx (data+i)、%xmm0 (acc)

Loop registers 之間的運算鏈決定了限制效能的資料相依。 精煉圖形時(Figure 5.14):cmpjne 不在 loop registers 的相依鏈上——假設 ICU 預測 branch taken(繼續迴圈),它們的作用只是測試條件、若 not taken 通知 ICU,且假設此檢查夠快不會拖慢處理器 → 塗白剔除,只留 loop registers,得到單次迭代的抽象模板:

   %xmm0            %rdx
     |             /  |
     |   data[i]->load |
     |        |       add
    mul <-----+        |
     |                 |
   %xmm0            %rdx
  (每迭代兩條跨迭代相依:acc 走 mul 鏈,data+i 走 add 鏈)

Critical path:模板複製 n 次 (Figure 5.15, p.564–565)

 Critical path(左鏈)        (右鏈)
 data[0] --> load
               |
              mul            add
               |              |
 data[1] --> load             |
               |              |
              mul            add
               |              |
              ...            ...
               |              |
 data[n-1]--> load            |
               |              |
              mul            add

 左鏈:n 個 mul(FP latency 5)→ 5n cycles ← critical path
 右鏈:n 個 add(int latency 1)→ n cycles(不構成限制)

其他效能因素 (p.565)

整數加法實測 CPE = 1.27 > 1.00(左右兩鏈皆預測 1.00):

Critical path 只給出 cycles 的下界 (lower bound),不是精確預測。其他因素亦可能限制效能:可用功能單元總數每步可在功能單元間傳遞的資料值數量。整數加法本身太快,其餘 operations(指標操作、load)供應資料的速度跟不上;要解釋 1.27 的確切成因,需要未公開的硬體設計細節。

小結:combine4 的 critical path 長 Ln(acc 的連續更新造成),CPE 至少為 L;latency bound 看似根本極限,但下一步(5.8 起)將重構運算以增強 ILP,使 throughput bound 成為唯一限制,把 CPE 壓到 1.00 以下或附近 → 05-Program-Optimization/03-Loop-Unrolling-and-Parallelism

多項式求值案例(Practice Problems 5.5 / 5.6 素材,p.566–567)

版本 運算次數(degree n) 跨迭代相依鏈 CPE
poly(直接法):result += a[i]*xpwr; xpwr = x*xpwr 2n 乘法 + n 加法 xpwr = x*xpwr 乘法鏈為 critical path(FP mul,latency 5 → 5n);result += 亦跨迭代但為較短 FP add 鏈(latency 3 → 3n);a[i]*xpwr 不跨迭代可平行 5.00
polyh(Horner 法):result = a[i] + x*result n 乘法 + n 加法 result 的更新需 mul (5) + add (3) 串聯 → 每迭代 8 cycles 8.00
poly: a0+a1x+a2x2++anxnHorner: a0+x(a1+x(a2++x(an1+xan)))

關鍵教訓:Horner 法運算次數較少(n vs 2n 個乘法)卻更慢——效能由 critical path 上的 latency 總和決定,而非運算總數;poly 較多的乘法與相依鏈平行執行,不佔 critical path。

Exam/Test Patterns

情境 / 關鍵字 答案
「latency bound 何時遇到?」 運算必須嚴格循序(資料相依)時;CPE 最小值 = 運算 latency L
「throughput bound 怎麼算?」 潛在吞吐量 C/I ops/cycle,其倒數為 CPE 下界;還要考慮 load units 的讀取限制
「int add 的 throughput bound 為何是 0.50 不是 0.25?」 4 個加法單元理論 0.25,但只有 2 個 load units,每 cycle 最多讀 2 個元素
addq %rax,8(%rdx) 解碼成幾個 operations?」 3 個:load、add、store(記憶體參照與算術分離);純暫存器指令則為 1 個
「branch 送入 EU 的目的?」 不是決定跳哪,而是驗證預測是否正確;錯則丟棄分支後結果、從正確目標重新 fetch
「misprediction 為何不破壞程式狀態?」 Speculative 結果不寫入 registers/memory;retirement unit 只 retire 分支確認正確的指令,錯誤路徑整批 flush
「register renaming 機制?」 decode 時發 tag,表存 (r,t);完成時廣播 (v,t);等待者直接取 v(forwarding),免經 register file;table 只含 pending write
「哪類暫存器決定效能瓶頸?」 Loop registers(跨迭代又讀又寫,如 acc、data+i);其間運算鏈形成 critical path;local registers(如 condition codes)不跨迭代
「combine4 FP * 為何 CPE = 5.00?」 acc 連續更新形成 n 個 mul 的 critical path,FP mul latency 5 → 5n cycles;TLn+K
「critical path 預測 1.00 但實測 1.27?」 Critical path 僅是下界;功能單元數量、單元間資料傳遞頻寬等亦限制效能
「運算較少的 Horner 法為何較慢 (8.00 vs 5.00)?」 相依鏈 latency:Horner 每迭代 mul+add 串聯 = 8;直接法跨迭代鏈只有單一 mul = 5,其餘運算平行執行
「divider 特性?」 不 pipelined(issue = latency)、時間依運算元值(int 3–30、FP 3–15)、與 FP sqrt 共用單元,昂貴
「fully pipelined 定義?」 issue time = 1 cycle,每 cycle 可開始一個新運算;需連續且邏輯獨立的運算才能發揮
「superscalar / out-of-order 定義?」 Superscalar:每 cycle 執行多個運算;out-of-order:執行順序不必符合機器碼順序