CPU
Cache
缓存行 (Cache Line)
缓存行是 CPU 缓存(L1、L2、L3)与主内存 (RAM) 之间数据交换的最小单位,通常大小为 64 字节。
利用空间局部性 (Spatial Locality)。当 CPU 请求一个字节的数据时,OS 知道程序很可能接下来会访问周围的字节。因此,CPU 会一口气把整个 64 字节的缓存行都从 RAM/L3 搬到 L1/L2 中。
缓存块的结构
缓存(Cache)本质上是一个高速的查找表。它被组织成一系列缓存块 (Cache Block)
| 组成部分 | 描述 |
|---|---|
| 数据区 (Data) | 存储实际的 64 字节 数据(即缓存行)。 |
| 有效位 (Valid Bit) | 类似于页表中的有效位,用于标记这个缓存块中存储的数据是否有效或已初始化。 |
| 标记 (Tag) | 存储一个地址片段,用于识别这个缓存块中的数据来自 RAM 中的哪个地址。 |
分组相联映射 (Set-Associative Mapping)
速度
在一颗 ~3 GHz 的现代 CPU 上,大概是这样:Gist+1
- CPU 一个时钟周期:≈ 0.3–0.4 ns
- L1 Cache 命中:≈ 0.5–1 ns(大约 3–4 个周期)
- L2 Cache 命中:≈ 3–5 ns(大约 10–15 个周期)
- L3 Cache 命中:≈ 10–20 ns(几十个周期)
- 主内存 DRAM:≈ 80–150 ns(两百多甚至几百个周期)
你可以先记一条粗略比例:
L1 : L2 : L3 : 内存 ≈ 1 : 10 : 30 : 100
MESI
针对同一条 cache line,每个核心自己的缓存里都有一个状态:
- M (Modified):我这份是最新的,只在我这里,并且比内存新(脏)。
- E (Exclusive):我这份是最新的,只在我这里,而且和内存一致(干净)。
- S (Shared):我这份是最新的,可能多个核都有,而且和内存一致(干净)。
- I (Invalid):我这里没有有效副本。
读(Read)
- 我若是 M/E/S:直接读,不发总线事务(命中)。
- 我若是 I:必须去拿数据(发 BusRd)。 拿回来后是 E 还是 S 取决于“别人有没有这行”。
写(Write)
- 我若是 M:直接写。
- 我若是 E:直接升级成 M(不需要通知别人,因为别人没有)。
- 我若是 S:必须让别人都失效(发 BusUpgr),然后 S→M。
- 我若是 I:必须“读到并拿到独占”(发 BusRdX 或 RFO),然后 I→M。
BusRd(读共享):我缺这行,想读。
BusRdX / RFO(读并独占):我缺这行,但我想写,需要独占。
BusUpgr(升级为独占):我已经有 S,只是要写,让别人失效即可(不用再搬数据)。