05.2 多核心與 GPU 平行架構#
學習目標#
讀完本節後,你應該能夠:
- 說明多核心(multicore)崛起的原因,特別是 Dennard 縮放終結與功耗牆的角色。
- 用 Amdahl 定律計算平行加速比,並理解強縮放與弱縮放的差別。
- 描述快取一致性(cache coherence)與 MESI 協定的基本原理。
- 解釋 GPU 的 SIMT 執行模型、執行緒層級,以及為何 GPU 適合資料平行工作負載。
- 比較 CPU 與 GPU 的設計哲學差異(延遲導向 vs. 吞吐量導向)。
5.2.1 為什麼轉向多核心?#
5.1 節的結尾指出 ILP 有極限,但真正的臨界點發生在 2000 年代中期。此前的三十年,晶片設計者享受著兩條同時有效的縮放定律:
- 摩爾定律:每 18~24 個月,單晶片電晶體數量約翻倍。
- Dennard 縮放定律(1974):電晶體尺寸縮小時,電壓與電流也同步縮小,使功率密度保持不變——晶片做小做密,還可以同時做快(提時脈)而不過熱。
2005 年前後,Dennard 縮放終結:漏電流無法再隨尺寸等比下降,電壓下降趨緩(停在約 1V 附近),但電晶體密度仍依摩爾定律翻倍——結果是功率密度急速上升。奔騰 4 曾把時脈推向 3.8GHz,功耗逼近 130W,再往上就是熱當機。單核「垂直」成長(提時脈、加深流水線、加寬亂序視窗)的每一條路都撞上功耗牆。
業界的回應在 2005 年同步發生:Intel 宣布放棄單核時脈路線、轉向多核心;AMD、Sun、IBM 皆然。把省下的電晶體預算從「把一個核心做快」改成「多放幾個簡單核心」——這就是多核心時代,而其驅動力是功耗,不是創意的枯竭。
5.2.2 Amdahl 定律:平行化的數學天花板#
把程式交給多個核心,能快多少?Gene Amdahl 在 1967 年給出了著名的悲觀答案。設程式中可平行化的比例為 \(p\) ,使用 \(n\) 個核心,則加速比為:
- 當 \(n \to \infty\) , \(S \to \dfrac{1}{1-p}\) :加速比受序列部分的倒數封頂。若 5% 的程式無法平行,無論用多少核心,加速比上限只有 20 倍。
- 這解釋了為什麼「核心數競賽」很快失去意義:128 核心對 95% 可平行的程式也只得到約 17 倍加速。
Gustafson 在 1988 年提出反駁:實務上資料量會隨核心數增加(弱縮放),序列部分占比隨問題規模縮小,因此有效加速可以線性成長。兩者並不矛盾:
- 強縮放(strong scaling):固定問題規模、增加核心 → 遵循 Amdahl。
- 弱縮放(weak scaling):問題規模隨核心數成長 → 更接近 Gustafson。
此外還有不可忽略的通訊與同步開銷:鎖(lock)、屏障(barrier)、快取一致性流量都會隨核心數增加,實務加速比往往低於理論值。
5.2.3 快取一致性:多核心的第一道難題#
每個核心有自己的 L1/L2 快取,同一筆資料可能存在多份副本。若核心 A 改了它快取裡的 x,核心 B 讀到的卻是舊值,程式語意就崩壞。快取一致性(cache coherence)協定保證所有核心對記憶體有一致的視圖。
主流做法是偵聽匯流排(snooping bus)上的無效化協定,經典者為 MESI:
| 狀態 | 意義 |
|---|---|
| M (Modified) | 此快取有唯一有效副本,且已被修改(與記憶體不一致) |
| E (Exclusive) | 此快取有唯一有效副本,且與記憶體一致 |
| S (Shared) | 多個快取都有相同的有效副本 |
| I (Invalid) | 該行無效 |
寫入時必須先取得 M 態(將其他副本無效化),讀取時可共享 S 態。核心數一多,偵聽成本劇增,大型系統改用目錄式(directory-based)協定。一致性也解釋了並行程式的經典陷阱:偽共享(false sharing)——兩個執行緒寫不同變數,但變數位於同一快取行,導致快取行在核心間來回彈跳,效能雪崩。
flowchart TD
subgraph 晶片
C0[核心0<br/>L1/L2] <--> L3[共享 L3]
C1[核心1<br/>L1/L2] <--> L3
C2[核心2<br/>L1/L2] <--> L3
C3[核心3<br/>L1/L2] <--> L3
L3 <--> MEM[記憶體控制器]
end5.2.4 GPU 的誕生:從圖形管線到通用運算#
多核心解決的是「幾十個執行緒」的平行;但圖形運算需要的是同一著色程式對數百萬個頂點與像素並行執行。這種資料平行(data parallelism)——同一程式、不同資料——催生了完全不同的處理器形態。
GPU 的設計哲學與 CPU 相反:
| 面向 | CPU | GPU |
|---|---|---|
| 目標 | 最小化單一執行緒延遲 | 最大化整體吞吐量 |
| 控制電路 | 龐大(亂序、推測、大分支預測器) | 極簡(多用 in-order + SIMT) |
| 快取 | 大快取隱藏延遲 | 小快取 + 海量執行緒切換隱藏延遲 |
| ALU 比例 | 少 | 密度極高(晶片面積 80% 給運算單元) |
關鍵洞察:與其用快取「避免」等待,不如準備幾萬個執行緒——某個執行緒等記憶體時,硬體立刻切到別的執行緒算,延遲被吞吐量掩蓋(latency hiding by oversubscription)。
SIMT(Single Instruction, Multiple Threads)是 GPU 的執行模型:例如 NVIDIA 以 warp(32 執行緒)為單位,同一 warp 的所有執行緒共用一個程式計數器、執行同一指令,只是各自處理不同資料。這實為 SIMD 與多執行緒的混合體。若 warp 內執行緒在分支上意見分歧,就會分支分歧(divergence)——兩條路徑各執行一遍,非活躍執行緒空轉,這是 GPU 程式設計的首要效能殺手。
2007 年 NVIDIA 推出 CUDA,讓開發者以類 C 語言直接撰寫 GPU 程式,把 GPU 從圖形專用機轉為通用平行運算平台。執行緒組織成階層:執行緒(thread)→ 區塊(block,共享一塊低延遲的 shared memory)→ 網格(grid)。記憶體也有對應階層:暫存器 → shared memory → global memory(含 L2)。
flowchart TD
G[Grid 網格] --> B1[Block 0] --> T[Threads...]
G --> B2[Block 1] --> T2[Threads...]
B1 -.共享 shared memory.- T5.2.5 異構運算:CPU + GPU 的分工#
GPU 不是取代 CPU,而是與之互補。典型工作負載被切成兩半:
- 控制密集、分支多、序列性強的部分 → CPU(亂序、大快取、低延遲)。
- 規則、大規模、資料平行的部分 → GPU(SIMT、高吞吐)。
這種異構運算(heterogeneous computing)需要解決資料搬移的問題:CPU 與 GPU 各有記憶體空間,PCIe 的複製成本高昂。現代方案包括統一記憶體(unified memory)、快取一致性互連(NVIDIA NVLink、CXL、AMD Infinity Fabric),讓 CPU/GPU/加速器共享同一實體記憶體的視圖。程式語言層面則有 OpenCL、SYCL、oneAPI 等試圖寫一次、到處平行。
至此,第 5 章的技術主線成形:Tomasulo→超純量→ILP 極限→Dennard 終結→多核心(執行緒級平行 TLP)→ SIMD/GPU(資料級平行 DLP)→異構運算。但電晶體再怎麼平行分配,功耗牆仍在——晶片上愈來愈多電路因供電與散熱預算而必須長期關閉,這個「黑暗矽」問題,把架構師推向下一個戰場:領域特定架構。
本章小結#
- 2005 年 Dennard 縮放終結造成功耗牆,單核垂直成長(時脈、ILP)觸頂,業界轉向多核心。
- Amdahl 定律 \(S=\frac{1}{(1-p)+\frac{p}{n}}\) 說明序列部分封頂加速比;Gustafson 的弱縮放觀點補充了實務面向。
- 快取一致性(MESI)是多核心正確性的基礎;偽共享是常見效能陷阱。
- GPU 以 SIMT + 海量執行緒實現資料平行,用吞吐量掩蓋延遲,與 CPU 的延遲導向哲學相反;CUDA(2007)開啟 GPGPU 世代。
- 異構運算依工作負載特性在 CPU 與 GPU 間分工,互連與統一記憶體是系統整合關鍵。
習題#
- 某程式 70% 可完美平行。用 4、16、64 個核心時,依 Amdahl 定律各得到多少加速比?序列比例上限何在?
- 兩個執行緒分別不斷更新
struct { int a; int b; }中的a與b(64 位元系統,快取行 64B)。解釋為何效能可能比單執行緒更差,以及兩種修正方法。 - 核心以 MESI 協定運作。核心 1 欲寫入一個自己快取中為 S 態的資料行,而核心 2 也持有該行的 S 態副本。寫出後續的狀態轉移序列。
- 解釋 GPU 為何「用執行緒切換而非大快取」來處理記憶體延遲,並說明這與工作負載的什麼特性配合。
- 一段影像濾波程式(每像素計算彼此獨立)在 CPU 上 10 秒完成,在 GPU 上 0.5 秒,但資料來回複製耗時 0.8 秒。討論在什麼條件下改用 GPU 才划算,並提出一種減少複製成本的系統層做法。