精简版 · 小杯2026-08 冻结,已停止更新(发布前修订了 4 处已知错误)。后续勘误与新增内容只在正式版。看正式版(中杯)→
Skip to content

Cache 写策略

考情分析

写策略在选择题和综合题里都会出现:选择题考"写直达/写回"的区别和脏位的含义;综合题算"Cache 总容量"时,几乎总会带上"是否有脏位"这个小坑。后者才是真正的分水岭。

大纲定位

考纲第三章(六)「高速缓冲存储器(Cache)」第 4 条:Cache 写策略

要求到什么程度:写直达/写回 × 写分配/非写分配四种组合下,某次写操作各动了哪几级存储;脏位何时存在、如何计入容量;能统计一段访问序列引发的主存访问次数。

写策略要解决的核心问题: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,多次写合并成一次写回

四种组合都是合法配置,只是上面两种更常见。所以:

编者注(卷面):题目只给了写命中策略(比如只说"采用写直达")而没说写不命中怎么办时,按写分配处理——这是通行约定,真题解析也是这么补的。别因为"写直达+写分配不是经典搭配"就认为题目出错或拒绝作答。

四种情形各动了哪几级存储

做题时真正要用的是这张表——某一次写操作到底改了什么:

情形Cache 是否变脏位是否写主存是否调块是否触发脏行写回
写直达 + 写命中无脏位
写直达 + 写不命中(非写分配)不变无脏位
写回 + 写命中置 1
写回 + 写不命中(写分配)置 1若被替换行脏则

两条速记:写直达一定写主存、永远没有脏位写回只动 Cache 并置脏位,主存写推迟到该行被替换时

写策略与 MESI 一致性的关系(拓展)

在多核场景下,写回策略必须配合总线侦听协议(如 MESI 协议)维持一致性:

  • M(Modified,脏):本核独占且与主存不同步
  • E(Exclusive,独占):本核独占但与主存同步
  • S(Shared):多核共享、与主存同步
  • I(Invalid):无效

408 大纲不展开 MESI,了解"写回策略在多核下需要硬件一致性协议支持"即可,单核题不涉及。

交互可视化

加载可视化中...

例题

例 1:标记阵列位数与 Cache 总容量

Cache 数据容量 4 KB,4 路组相联,块大小 64 B,主存 32 位地址,写回策略,采用 LRU 替换。

  • 总行数 = 4 KB / 64 B = 64 行;组数 = 64 / 4 = 16 组
  • offset = log₂ 64 = 6 位
  • index = log₂ 16 = 4 位
  • tag = 32 − 6 − 4 = 22 位

每行的非数据位(标记阵列部分):

1有效位+22tag+1脏位+2LRU:log24=26 标记阵列总位数=64×26=1664 

Cache 总容量(含数据区):

每行位数=26+64×8 512 =538 总容量=64×538=34432 =4304 B4.2 KB

编者注(易错):两个量别混。「标记阵列位数」不含数据区(1664 位),「Cache 总容量」含(34432 位),差了 20 倍。题目问哪个就答哪个——真题两种问法都出现过。

两处容易漏的位

  1. 替换算法位k 路组相联用计数器法实现 LRU,每行需 log2k 位。4 路是 2 位、8 路是 3 位——不是"8 路就 8 位"。题目没提替换算法就不加。
  2. 脏位:只有写回才有。改为写直达时每行非数据位 = 1 + 22 + 2 = 25 位,标记阵列 = 64 × 25 = 1600 位(每行少 1 个脏位,共少 64 位)。

例 2:访问主存次数估算

某程序共 100 次访存,其中写操作占 30%(写 30 次、读 70 次),总命中率 90%,且未命中在读写间按比例分布(读未命中 7 次、写未命中 3 次)。把"调入/写回 1 块"和"写 1 个字"都按 1 次主存访问粗略计数,对比两种搭配的主存访问次数:

  • 写直达 + 非写分配
    • 每次写都要写主存(命中与否都写,写未命中直接写主存、不调块)→ 30 次
    • 读未命中调块 → 7 次;读命中、写命中均不再额外访主存
    • 总 = 30 + 7 = 37 次
  • 写回 + 写分配(假设被替换的行 50% 是脏的):
    • 读、写未命中都要调块 → 10 次
    • 替换出的脏行写回 → 10 × 50% = 5 次(这里隐含假设 Cache 已处于稳态、每次调块都要替换掉一个有效行;冷 Cache 下前若干次调块填的是空行,不产生写回)
    • 读命中、写命中都只动 Cache,不访主存 → 0 次
    • 总 = 10 + 5 = 15 次

写回策略主存访问次数显著减少(37 → 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 仅需了解)

真题练习