影響併發效能的因素 (Factors Affecting Performance)

Overview Table

因素 問題 對策
處理器數量 執行緒多於硬體支援 → 超額申請 (oversubscription) std::thread::hardware_concurrencyasync(、執行緒池
資料爭用 多處理器搶同一資料 → 快取乒乓 (cache ping-pong) 減少多執行緒對同一記憶體位置的競爭
mutex 爭用 鎖資料在核心間搬移、OS 層序列化 降低鎖粒度與存取頻率;注意讀寫鎖也會改寫 mutex
偽共享 (false sharing) 不同執行緒的資料同住一條 cache line 填充間隔 ≥ hardware_destructive_interference_size
資料鬆散 資料散落多條 cache line → 快取缺失 讓同一執行緒的資料緊湊(constructive size 內)
任務切換 可跑執行緒過多 → 切換開銷 + 快取失效 限制工作執行緒數量

處理器數量與超額申請 (oversubscription)

能真正併發的執行緒數由硬體決定(16 個硬體執行緒的機器,無論是 1×16 核、4×雙核…都一樣只能同時跑 16 條)。可跑的執行緒(未阻塞)多於硬體支援數時,處理器把時間浪費在執行緒切換上,稱為超額申請 (oversubscription)

hardware_concurrency() 的盲點

不會考慮其他應用程式已占用的執行緒;若程式多處各自依它開執行緒、或系統同時跑多個 CPU 密集程式,仍會超額申請。它也可能回傳 0(資訊不可得),使用時要有預設值。

資料爭用與快取乒乓 (cache ping-pong)

多執行緒唯讀同一資料沒問題(各自快取一份);一旦有執行緒修改資料,變更就得傳播到其他核心的快取,非常耗時。

std::atomic<unsigned long> counter(0);
void processing_loop() {
  while (counter.fetch_add(1, std::memory_order_relaxed) < 100000000)
    do_something();
}
 CPU A 快取 ──(修改 counter,取得所有權)──▶ CPU B 快取失效
 CPU B 快取 ◀──(修改 counter,取得所有權)── CPU A 快取失效
        └────── cache line 來回「乒乓」,兩邊輪流停等 ──────┘

mutex 爭用

迴圈中鎖 mutex 與上面的原子操作情況類似:mutex 本身的資料也要在核心間搬移;搬移時間直接成為下一個執行緒等鎖的時間。

偽共享 (false sharing) 與 cache line

處理器快取以 cache line(通常 32 或 64 位元組)為單位搬移,不是單一位址。多個小資料項(如相鄰的 int)會落在同一條 cache line:

C++17 工具(標頭 <new>)

  • std::hardware_destructive_interference_size:可能發生偽共享的最大連續位元組數——不同執行緒的資料間隔 ≥ 此值即可避免偽共享。
  • std::hardware_constructive_interference_size:保證落在同一條 cache line 的最大連續位元組數(需對齊)——單執行緒熱資料塞進此大小內可提升快取命中。

資料緊湊度 (data proximity)

偽共享是「不同執行緒的資料太近」;反過來,單執行緒的資料太散也傷效能:

超額申請與頻繁任務切換

執行緒多於處理器本身很正常(執行緒常在等 I/O、mutex、條件變數,等待中的執行緒不占 CPU)。真正的問題是準備就緒的執行緒遠多於處理器:


Exam/Test Patterns

情境/關鍵字 答案
「就緒執行緒 > 硬體執行緒數,效能反而下降」 超額申請:切換開銷;用 hardware_concurrency() 限量或改用 std::async()/執行緒池
「多執行緒反覆改同一個 atomic 計數器很慢」 快取乒乓:cache line 所有權在處理器間搬移;減少對同一記憶體位置的寫入競爭
「兩執行緒改的是不同變數還是很慢」 偽共享:變數同住一條 cache line;以 hardware_destructive_interference_size 填充隔開
「讀寫鎖沒有帶來預期效能」 讀者也要修改 mutex 內部狀態 → mutex 所在 line 乒乓
「單執行緒資料散落各處、cache miss 高」 資料緊湊度:熱資料塞進 hardware_constructive_interference_size
「destructive vs constructive interference size」 destructive = 隔開避免偽共享的最小間距;constructive = 靠攏提升命中的同 line 上限
「mutex 爭用與 atomic 爭用差在哪」 mutex 由 OS 排程序列化(執行緒讓出 CPU);atomic 讓處理器原地等快取轉移