4. Cache 写入策略:写直达与写回的权衡

好,咱们今天聊一个绕不开的话题——Cache的写入策略。说白了,就是当CPU要写数据时,Cache该怎么处理这个写操作。是同步写回内存,还是先攒着再说?

这两种策略,就是写直达(Write-Through)和写回(Write-Back)。我做了这么多年芯片设计,可以负责任地告诉你:没有绝对的好坏,只有合不合适的场景。

4.1 写直达(Write-Through)

写直达的策略很简单:每次写Cache,同时也写下一级存储。CPU说“我要写数据”,Cache就一边更新自己,一边把数据往下传。

核心特征:写操作必须穿透Cache,直达内存。

我记得刚入行时,带我的老工程师说:“写直达就像你记笔记,每写一个字,都要大声念出来让全班都听到。” 虽然有点夸张,但道理没错。

4.1.1 写直达的优缺点

维度 说明
一致性维护 天然简单。内存永远是最新数据,其他Cache snoop时直接看内存就行
实现复杂度 低。不需要dirty bit,不需要写回逻辑
写带宽 高。每次写操作都要占用内存带宽
写延迟 高。CPU要等写内存完成(除非有write buffer)
功耗 高。每次写都访问内存

你想想看,如果一个程序频繁写同一个变量,写直达每次都要去内存。这就像你每次改一个字,都要把整本书重新印刷一遍——太浪费了。

4.1.2 写直达的典型应用

我在项目中遇到过用写直达的场景,主要是两种情况:

  • I/O空间:外设寄存器必须实时更新,不能缓存
  • 简单嵌入式系统:Cache很小,一致性要求高,实现越简单越好

小技巧:写直达通常配合write buffer使用。CPU把数据扔进buffer就返回,由buffer负责慢慢写内存。这样能有效降低写延迟。

4.2 写回(Write-Back)

写回就聪明多了。CPU写Cache时,只更新Cache,不立即写内存。等Cache行被替换时,才把修改过的数据写回内存。

说白了,就是“先记账,后结账”。

4.2.1 写回的核心机制

写回需要引入一个关键标志——dirty bit(脏位)。Cache行被修改后,dirty bit置1。替换时,只有dirty bit为1的行才需要写回内存。

// 写回策略的替换流程(伪代码)
if (cache_line.dirty) {
    write_back_to_memory(cache_line);
}
load_from_memory(new_data);
cache_line.data = new_data;
cache_line.dirty = 0;

嗯,这里要注意:如果Cache行是干净的(dirty=0),直接覆盖就行,不用写内存。这就是写回省带宽的关键。

4.2.2 写回的优缺点

维度 说明
写带宽 低。多次写同一个Cache行,只产生一次内存访问
写延迟 低。CPU只写Cache,不用等内存
一致性维护 复杂。内存数据可能过时,需要snoop协议介入
实现复杂度 高。需要dirty bit、写回控制器、替换策略配合
功耗 低。减少内存访问次数

避坑指南:我曾经在一个多核项目中,因为写回策略的dirty bit处理不当,导致两个核同时修改同一Cache行,数据丢失。后来加了MESI协议才解决。写回策略一定要配合缓存一致性协议使用,否则后果很严重。

4.3 写直达 vs 写回:核心权衡

这两种策略的取舍,我总结成一张图,方便你理解:

写直达 vs 写回:核心权衡 CPU 写直达:同时写Cache和内存 内存始终最新 写回:只写Cache,标记dirty 内存可能过时 写直达适用场景 • Cache很小(≤16KB) • 单核或简单多核 • I/O空间、外设寄存器 • 一致性要求极高 • 实现周期短,验证成本低 写回适用场景 • Cache较大(≥32KB) • 多核系统(配合MESI等协议) • 高性能计算、服务器 • 写密集型应用 • 功耗敏感场景

4.4 实际项目中的选择策略

我个人习惯这样选:

  1. 先看Cache大小:小于16KB,写直达更简单;大于32KB,写回更高效
  2. 再看核数:单核用写直达省事;多核必须写回+一致性协议
  3. 最后看应用:I/O密集用写直达;计算密集用写回

关键指标对比:

  • 写直达:写延迟≈Cache命中时间 + 内存写时间
  • 写回:写延迟≈Cache命中时间(dirty时替换才加写回时间)
  • 写回节省的内存带宽:通常30%~70%,取决于写局部性

4.5 混合策略:写缓冲与写合并

实际芯片设计中,很少只用纯写直达或纯写回。我见过的大多数高性能处理器,都用了写缓冲(Write Buffer)和写合并(Write Merging)。

写缓冲就是给写直达加个FIFO,CPU写操作先入队,后台慢慢写内存。写合并更聪明——如果多次写同一个Cache行,在缓冲里就合并成一次写操作。

// 写合并示例:连续写同一个地址
// 没有写合并:4次内存写
store 0x1000, A  → 写内存
store 0x1000, B  → 写内存
store 0x1000, C  → 写内存
store 0x1000, D  → 写内存

// 有写合并:1次内存写
store 0x1000, A  → 入缓冲
store 0x1000, B  → 合并为B
store 0x1000, C  → 合并为C
store 0x1000, D  → 合并为D → 写内存

实战经验:我在设计某款AI芯片时,L1 Cache用写直达+写缓冲,L2 Cache用写回。这样既保证了L1的简单性,又利用了L2的带宽优势。效果不错,写带宽降低了约40%。

4.6 本章小结

写直达和写回,没有银弹。写直达简单但费带宽,写回高效但复杂。选择时,要综合考虑Cache大小、核数、应用场景和功耗预算。

我个人建议:新手先从写直达入手,理解清楚后再过渡到写回。写回涉及的一致性协议、dirty bit管理、替换策略配合,都是需要实战积累的。我曾经在写回策略上踩过坑,但正是这些坑,让我对缓存一致性有了更深的理解。