Appearance
Cache 写策略
考情分析
写策略是 Cache 章节的高频子考点:选择题考"写直达/写回"区别和脏位含义,大题里"Cache 总容量计算"几乎必带"是否有脏位"的小坑。近 5 年(2022–2026)多次以判断题、计算题形式直接考查。
写策略要解决的核心问题:CPU 写 Cache 时,主存怎么办?由此分裂出两个独立的策略维度——
- 写命中(Cache 中有副本):要不要立刻同步主存?
- 写不命中(Cache 中没有副本):要不要把这块先调进 Cache 再写?
两个维度可以任意组合,但实际系统里只有两种主流搭配。
一致性问题的根源
Cache 是主存数据的副本。读操作不会破坏一致性——读 Cache 等于读主存。写操作必须决定:写入的新数据如何让主存"看见"?如果处理不当,主存的旧值会被其他设备(DMA、其他核心)误读。
写策略本质上是在性能(少访问主存)和一致性(与主存同步)之间做选择。
写命中策略
写直达(Write Through)
规则:每次写 Cache,同时把这次写也发往主存。
CPU 写 → Cache 更新 → 主存同步更新- 优点:Cache 与主存始终一致,外部设备(DMA、多核)读主存永远是最新值;硬件实现简单,不需要脏位
- 缺点:每次写都要主存带宽,性能瓶颈明显(主存速度比 Cache 慢 1–2 个数量级)
- 缓解:配合写缓冲器(Write Buffer)——CPU 先把写请求放进 FIFO 缓冲器就走,缓冲器异步写主存,CPU 不必等待
写缓冲器满了之后 CPU 仍会被阻塞,所以它只能缓解突发写,不能消除写直达的根本带宽压力。
写回(Write Back)
规则:只写 Cache,不立即写主存。等这一行被替换时,如果它"脏"了,再写回主存。
CPU 写 → Cache 更新 → 该行 Dirty=1
↓(行被替换时)
如果 Dirty=1 → 整行写回主存- 优点:减少主存访问,性能高;多次写同一行只产生 1 次主存写
- 缺点:Cache 与主存可能不一致;需要脏位标记;多核/DMA 场景需额外一致性协议
- 关键硬件:每一行多 1 位脏位(Dirty Bit / Modified Bit),标识"该行被改过、与主存不同步"
编者注(计算题陷阱):大题计算 Cache 总容量时,写回策略每行多 1 位脏位、写直达策略没有脏位。常考"Cache 标记阵列共多少位",漏算脏位是高频失分点。
写命中策略对比
| 写直达 | 写回 | |
|---|---|---|
| 主存同步时机 | 每次写都同步 | 替换时才同步 |
| Cache/主存一致性 | 始终一致 | 可能不一致 |
| 脏位 | 不需要 | 需要(每行 1 位) |
| 主存写带宽 | 高(每次写都用) | 低(只在替换+脏时用) |
| 多核/DMA 友好度 | 高(外设读主存直接拿到新值) | 低(需一致性协议或显式 flush) |
| 实现复杂度 | 简单 | 复杂(脏位+替换写回逻辑) |
| 适用场景 | 写少、对一致性敏感 | 写多、追求性能 |
写不命中策略
写分配(Write Allocate)
规则:写不命中时,先把主存对应块调入 Cache,再在 Cache 中执行写。
预期未来这块会被反复访问,先调入 Cache 摊薄主存访问成本。
非写分配(No Write Allocate)
规则:写不命中时,直接写主存,不调入 Cache。
预期这次写是"一次性"的,调入 Cache 反而浪费 Cache 行。
经典搭配
写命中和写不命中策略原则上可以任意组合,但实际系统里只有两种稳定搭配:
| 写命中 | 写不命中 | 思路 |
|---|---|---|
| 写直达 | 非写分配 | 反正每次都要写主存,调入 Cache 也没节省什么;不浪费 Cache 容量 |
| 写回 | 写分配 | 既然要靠"批量写回"赚性能,就应该让数据先进 Cache,多次写合并成一次写回 |
考试出现的"奇葩搭配"(写直达 + 写分配 / 写回 + 非写分配)几乎都是干扰项,选默认搭配基本不出错。
写策略与 MESI 一致性的关系(拓展)
在多核场景下,写回策略必须配合总线侦听协议(如 MESI 协议)维持一致性:
- M(Modified,脏):本核独占且与主存不同步
- E(Exclusive,独占):本核独占但与主存同步
- S(Shared):多核共享、与主存同步
- I(Invalid):无效
408 大纲不展开 MESI,了解"写回策略在多核下需要硬件一致性协议支持"即可,单核题不涉及。
例题
例 1:Cache 总容量含脏位
Cache 4 KB,4 路组相联,块大小 64 B,主存 32 位地址,写回策略。计算 Cache 标记阵列(含有效位+tag+脏位)总位数。
解:
- 总行数 = 4 KB / 64 B = 64 行;组数 = 64 / 4 = 16 组
- offset = log₂ 64 = 6 位
- index = log₂ 16 = 4 位
- tag = 32 − 6 − 4 = 22 位
- 每行控制位 = 1(有效位)+ 22(tag)+ 1(脏位) = 24 位
- 总控制位 = 64 × 24 = 1536 位
变体:若改为写直达策略,每行控制位 = 1 + 22 = 23 位,总控制位 = 64 × 23 = 1472 位。两者相差 64 位(每行少 1 个脏位)。
例 2:访问主存次数估算
某程序对 Cache 命中率 90%,写操作占总访存的 30%,块大小 16 字节,写策略不同时主存访问次数对比(假设 100 次访存):
- 写直达 + 非写分配:每次写都访主存 → 30 次写访主存;读 10 次未命中,每次调 1 块 → 10 次读访主存。总 = 30 + 10 = 40 次
- 写回 + 写分配(假设替换时 50% 行是脏的):未命中 10 次(写不命中调入 + 替换可能写回),脏行写回约 5 次,未命中调入 10 次。总 ≈ 15 次
写回策略主存访问次数显著减少,但代价是脏位管理和一致性维护。
易混淆知识点
1. "写命中"和"写不命中"两个策略维度独立吗?
是的。写直达/写回 解决 命中时如何同步,写分配/非写分配 解决 不命中时是否调入。理论上 2×2 = 4 种组合,但实际只有 2 种主流搭配(写直达+非写分配 / 写回+写分配)。
2. 脏位是 Cache 的,还是主存的?
脏位是 Cache 行的属性,每一行 1 位。主存没有脏位概念——主存就是"被同步"的那一端。
3. 写缓冲器(Write Buffer)和写回缓冲器(Write Back Buffer)一样吗?
不一样。
- 写缓冲器:配合写直达策略,缓存"待写入主存"的请求,让 CPU 不必同步等待
- 写回缓冲器:配合写回策略,临时存放"被替换出去的脏行",等待写入主存
二者都是为了让"写主存"异步化,但服务的策略不同。
4. 写回策略 Cache 与主存一定不一致吗?
不一定。如果某行从未被写过,它与主存就是一致的(Dirty=0)。"可能不一致"是说存在不一致的可能性,不是任何时刻都不一致。
5. 直接映射 Cache 用写回策略,行被替换时一定要写主存吗?
不一定。只有 Dirty=1 才写。Dirty=0 的行可以直接覆盖,因为主存里的副本仍然有效。这也是脏位存在的意义——避免无意义的写回。
考点清单
- 写直达:每次写 Cache 同时写主存,始终一致,硬件简单,无脏位,主存带宽消耗大
- 写回:只写 Cache,替换时按脏位决定是否写主存,性能高,需脏位(每行 1 位)
- 写分配:写不命中时先调入 Cache 再写;非写分配:直接写主存不调入
- 经典搭配:写直达 + 非写分配 / 写回 + 写分配
- Cache 总容量计算时,写回策略每行多 1 位脏位(高频陷阱)
- 写缓冲器(Write Buffer)配写直达,写回缓冲器(Write Back Buffer)配写回,二者不同
- 多核场景下写回策略需要 MESI 等一致性协议(408 仅需了解)