Appearance
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 位
每行的非数据位(标记阵列部分):
Cache 总容量(含数据区):
编者注(易错):两个量别混。「标记阵列位数」不含数据区(1664 位),「Cache 总容量」含(34432 位),差了 20 倍。题目问哪个就答哪个——真题两种问法都出现过。
两处容易漏的位:
- 替换算法位:
路组相联用计数器法实现 LRU,每行需 位。4 路是 2 位、8 路是 3 位——不是"8 路就 8 位"。题目没提替换算法就不加。 - 脏位:只有写回才有。改为写直达时每行非数据位 = 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 仅需了解)