Skip to content

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 仅需了解)

真题练习