多處理器排程 (Multi-Processor Scheduling)
本系列文章內容參考自經典教材 Operating System Concepts, 10th Edition (Silberschatz, Galvin, Gagne)。本文對應章節:Section 5.5 Multi-Processor Scheduling。
為什麼多處理器排程是獨立的議題?
前面各節討論的排程演算法,都建立在一個前提上:系統中只有一個 CPU 核心,所有 Thread 競爭這唯一的資源。然而當系統中出現多個可用的 CPU 時,情況發生了根本性的變化:多條 Thread 可以真正地同時(in parallel)執行,排程器必須回答一個新問題:哪條 Thread 要被排程到哪個 CPU 上?
這帶出一系列在單核心環境下根本不存在的問題:多個 CPU 如何共享 Ready Queue?CPU 之間的工作量要如何保持均衡?搬移 Thread 到另一個 CPU 會產生什麼代價?在多核心硬體上,「CPU」這個概念本身又該如何定義?本節逐一回答這些問題。
「多處理器(multiprocessor)」的定義在現代已大幅擴展,不再僅指多顆實體 CPU 晶片,而是涵蓋以下四類架構:
- Multicore CPUs(多核心 CPU):單一晶片上整合多個計算核心
- Multithreaded cores(多執行緒核心):每個核心能同時承載多條硬體執行緒
- NUMA systems:多個處理器節點,各自擁有本地記憶體,透過互連匯流排共享位址空間
- Heterogeneous multiprocessing(異質多處理):系統中同時存在效能不同、能耗不同的核心
本節前三個例子聚焦在處理器功能相同(homogeneous)的系統;最後一個例子則探討處理器能力不對稱的情況。
5.5.1 多處理器排程的方法 (Approaches to Multiple-Processor Scheduling)
非對稱多處理 (Asymmetric Multiprocessing)
一種直觀的做法是讓系統中只有一個 CPU(稱為 master server)負責所有排程決策、I/O 處理與系統活動,其餘 CPU 只執行使用者程式碼。這種架構稱為非對稱多處理(Asymmetric Multiprocessing)。
它的優點是設計簡單:因為只有 master server 存取系統內部資料結構,不需要擔心多個 CPU 同時讀寫同一份排程資料的競爭問題。缺點則是這個 master server 容易成為系統瓶頸,當排程需求增加時,整體系統效能受限於這一顆 CPU 的處理能力。
對稱多處理 (Symmetric Multiprocessing, SMP)
現代系統普遍採用的標準方案是對稱多處理(SMP,Symmetric Multiprocessing):每個 CPU 都有自己的排程器,能自行決定下一步執行哪條 Thread。Windows、Linux、macOS、Android、iOS 都支援 SMP。
在 SMP 架構下,Ready Queue 的組織方式有兩種策略:
- 共用 Ready Queue(Common Ready Queue):所有 Thread 放進同一個全域佇列,任何 CPU 的排程器都可以從中取用。
- 各核心私有的 Run Queue(Per-core Run Queues):每 個 CPU 維護自己的執行緒佇列,排程器只從本機佇列取用。
下圖對比兩種策略的組織方式:
- 圖左 (a) 的共用 Ready Queue 讓所有 Thread 集中在一個佇列,所有核心都從同一個地方取用執行緒。
- 圖右 (b) 的 Per-core Run Queue 讓每個核心擁有各自的私有佇列,彼此獨立運作,不需競爭同一資源。
兩種策略的核心差異在於競爭代價:共用 Ready Queue 必須用鎖(locking)保護以避免 Race Condition(競態條件),當多個 CPU 同時搶著從佇列取 Thread 時,鎖的爭用會成為嚴重的效能瓶頸。Per-core Run Queue 完全避免了這個問題,每個核心在自己的佇列上獨立排程,也因此是現代 SMP 系統最常採用的方案。此外,Per-core Run Queue 對快取記憶體的使用也更有效率(原因詳見 5.5.4 節的 Processor Affinity 討論)。
Per-core Run Queue 的問題在於不同核心之間的工作量可能不均衡,解決方案是負載平衡演算法(Load Balancing),詳見 5.5.3 節。
5.5.2 多核心處理器 (Multicore Processors)
傳統的 SMP 系統是透過增加獨立的實體 CPU 晶片來支援多處理;現代電腦則走向另一條路:將多個計算核心整合在同一顆晶片上,形成多核心處理器(Multicore Processor)。每個核心維護各自的架構狀態(instruction pointer、register set 等),在 OS 眼中看起來就是一個獨立的邏輯 CPU。
多核心處理器比多晶片方案速度更快、耗電更低,是今日主流的設計選擇。然而,多核心的出現也帶來了新的排程 挑戰。
記憶體停頓 (Memory Stall)
多核心處理器在排程上帶來的第一個問題,源自一個硬體現實:處理器的速度遠遠超過記憶體。當核心需要存取記憶體時,必須等待資料就緒,這段等待時間稱為記憶體停頓(Memory Stall)。記憶體停頓的發生有兩個原因:主記憶體存取本身的延遲,以及快取失效(Cache Miss)(要存取的資料不在快取中,必須從主記憶體重新載入)。
下圖展示記憶體停頓在時間軸上的實際樣貌:
圖中藍色的 C(compute cycle) 代表核心正在做計算,紅色的 M(memory stall cycle) 代表核心在等待記憶體回應。在現代處理器上,M 的比例可以高達 50%,也就是說核心有將近一半的時間處於空等的停頓狀態,大量計算能力被白白浪費。
多執行緒核心與晶片多執行緒技術
為了解決記憶體停頓造成的核心閒置問題,硬體設計師提出了一個直觀的解法:既然核心在等記憶體時無事可做,不如讓它切換到另一條執行緒繼續工作。這就是多執行緒核心(Multithreaded Processing Core) 的設計思路:每個核心配備兩條(或更多)硬體執行緒(Hardware Thread),當其中一條在等記憶體時,核心立即切換到另一條繼續執行。
下圖展示雙硬體執行緒核心的運作方式:
圖中 thread₀ 與 thread₁ 的 C/M 模式剛好交錯:當 thread₁ 在計算(C)時,thread₀ 正在等待記憶體(M);反過來也是一樣。核心在兩條 thread 之間來回切換,讓自己幾乎不需要空等,達到更高的利用率。
這項技術正式的名稱是晶片多執行緒(CMT,Chip Multithreading),在 Intel 處理器上稱為 Hyper-Threading(正式名稱為同步多執行緒,SMT,Simultaneous Multithreading)。下圖呈現一顆具備 4 個核心、每核心 2 條硬體執行緒的處理器,以及 OS 所見到的邏輯 CPU 數量:
圖上方是實體處理器內部的結構:4 個核心(core 0–3),每個核心包含 2 條硬體執行緒。圖下方是 OS 所看到的世界:因為每條硬體執行緒都維護各自的架構狀態,OS 將其視為獨立的邏輯 CPU,所以這顆處理器在 OS 眼中就是 8 個邏輯 CPU(CPU 0–7)。Intel Core i7 就是採用這種 2 硬體執行緒/核心的設計;Oracle SPARC M7 則可支援到 8 硬體執行緒/核心,搭配 8 個核心,提供 OS 64 個邏輯 CPU。
粗粒度與細粒度多執行緒 (Coarse-Grained vs. Fine-Grained Multithreading)
硬體執行緒切換的方式有兩種,差異在於切換的時機與代價:
粗粒度多執行緒(Coarse-Grained Multithreading) 的做法是:當前執行的硬體執行緒遭遇長延遲事件(如記憶體停頓)時,才切換到另一條硬體執行緒。問題在於,切換前必須先清空(flush)目前的指令管線(Instruction Pipeline),再讓新執行緒開始填充管線,切換代價相當高。這就像每次換手都要把整條流水線全部清空重來。
細粒度多執行緒(Fine-Grained Multithreading),又稱交錯多執行緒(Interleaved Multithreading),在每個指令週期邊界就可能切換執行緒,粒度極細。其代價低廉的關鍵在於:硬體設計上內建了切換邏輯(Thread-Switching Logic)