CPU 主頻 (GHz) 與內存延遲 (ns) 差距巨大。以 i5-2400S (2.5GHz) 為例:
Cache 利用局部性原理 (時間/空間) 縮小速度鴻溝。
地址劃分三段:Tag | Set Index | Block Offset
31 s+b-1 s+b-1 b-1 b-1 0 +----------------------+------------+-----------+ | Tag (t) | Set Index (s) | Offset (b) | +----------------------+------------+-----------+
B = 2^b (典型 64B)S = 2^s| 類型 | 關聯度 E | 組數 S | 特點 |
|---|---|---|---|
| 直接映射 | 1 | C/B | 簡單、衝突多、抖動嚴重 |
| 組相聯 | 1 < E < C/B | C/(B*E) | 折衷、現代 CPU 主流 (4-16 路) |
| 全相聯 | C/B | 1 | 無衝突、硬件複雜昂貴、罕用 |
總容量 C = S × E × B
| 級別 | 容量 | 關聯度 | 行大小 | 延遲 | 共享範圍 |
|---|---|---|---|---|---|
| L1 I-Cache | 32KB | 8-way | 64B | 4 週期 | 核心私有 |
| L1 D-Cache | 32KB | 8-way | 64B | 4 週期 | 核心私有 |
| L2 | 256KB | 8-way | 64B | 12 週期 | 核心私有 |
| L3 | 6MB | 12/16-way | 64B | 40 週期 | 所有核心共享 |
兩線程分別寫入不同變量,但兩變量落在同一 Cache 行 (64B)。導致 Cache 一致性協議 (MESI) 在核心間頻繁傳輸該行,性能暴跌。
// 線程 0 頻繁寫入
struct { long a; } data0;
// 線程 1 頻繁寫入
struct { long b; } data1;
// 若 data0 與 data1 相鄰分配,極大概率共享一行
struct { long a; char pad[64 - sizeof(long)]; } 強制佔滿一行alignas(64):alignas(64) long counter;
// 差:跳躍訪問
for (int i = 0; i < N; i += 16) sum += a[i];
// 好:順序掃描
for (int i = 0; i < N; i++) sum += a[i];
// 結構體:熱點字段放前面,同一行
struct Particle { float x,y,z; float vx,vy,vz; int life; }; // 48B < 64B
// 分塊矩陣乘法:子塊適應 L1/L2
for (int ii = 0; ii < N; ii += BLOCK)
for (int jj = 0; jj < N; jj += BLOCK)
for (int kk = 0; kk < N; kk += BLOCK)
for (int i = ii; i < ii+BLOCK; i++)
for (int j = jj; j < jj+BLOCK; j++)
for (int k = kk; k < kk+BLOCK; k++)
C[i][j] += A[i][k] * B[k][j];
// 差:步長 4096 (2^12) 直接映射同一組
for (i) a[i * 4096] = 0;
// 好:填充改變步長
#define STRIDE 4097
for (i) a[i * STRIDE] = 0;
__builtin_prefetch(&a[i+16], 0, 1); // GCC/Clang 內建,提前載入
perf stat -e cache-references,cache-misses ./prog:硬件計數器統計valgrind --tool=cachegrind:模擬 Cache 行為likwid-perfctr:詳細核心級事件理解 Cache 多級結構、映射機制、一致性協議、Miss 類型,是高性能編程的基石。核心原則:
Cache 不可見,但決定程序上限。善用 perf、cachegrind 量化分析,針對性優化。