3. Cache 工作原理:从 Line 到 Way,再到读写与替换策略
大家好,我是你们的嵌入式系统讲师。今天我们来聊聊 Cache 的工作原理。说实话,Cache 这个概念,很多工程师做了三五年项目,可能还是一知半解。但你要做多核调试、搞缓存一致性,这块必须吃透。
我个人习惯把 Cache 比作「工程师的临时工作台」。你想想看,CPU 从主存拿数据,就像你去仓库找零件——来回跑一趟太慢了。Cache 就是放在你手边的工具盒,常用的东西随手就能拿到。
3.1 Cache 的基本概念:Line、Set、Way
先看三个最基础的概念。搞懂了它们,Cache 的骨架你就搭起来了。
| 术语 | 英文 | 一句话解释 |
|---|---|---|
| Line | Cache Line | Cache 的最小存储单元,通常 32 或 64 字节 |
| Set | Set | 一组 Cache Line 的集合,按地址索引分组 |
| Way | Way | 每个 Set 中包含的 Line 数量,决定相联度 |
Cache Line 是搬运数据的最小单位。CPU 从内存读一个字节,实际上会把整条 Line 都搬进 Cache。我在 STM32H7 上调试时遇到过一个问题:两个变量刚好落在同一条 Cache Line 里,一个被修改,另一个也跟着「遭殃」——这就是伪共享(False Sharing)的典型场景。
Set 和 Way 决定了 Cache 的组织方式。举个例子:一个 4-Way Set-Associative Cache,每个 Set 里有 4 个 Line 位置。地址映射时,先算 Set 索引,再在 4 个 Way 里找空闲或可替换的 Line。
核心公式:
Cache 总大小 = Set 数量 × Way 数量 × Line 大小
比如:256 Sets × 4 Ways × 64 Bytes = 64 KB
我画了一张图,帮你把这三个概念串起来:
3.2 Cache 的读写策略
读写策略决定了 Cache 和主存之间怎么「同步账本」。两种主流策略:Write-Through 和 Write-Back。我当年在项目里选错策略,导致系统性能直接腰斩——嗯,这里要注意。
3.2.1 Write-Through(写通)
写操作同时更新 Cache 和主存。优点是数据永远一致,缺点是每次写都要访问慢速的主存。
- 写命中: 同时写入 Cache Line 和主存
- 写未命中: 直接写入主存(有些实现会先加载到 Cache)
我曾经踩过的坑: 在 STM32H743 上用 Write-Through 策略做高速数据采集,结果发现写入带宽被主存延迟卡死了。后来换成 Write-Back,性能直接翻倍。
3.2.2 Write-Back(写回)
写操作只更新 Cache,标记该 Line 为「脏」(Dirty)。只有该 Line 被替换时,才写回主存。
- 写命中: 更新 Cache,标记 Dirty
- 写未命中: 先加载到 Cache,再更新,标记 Dirty
- 替换时: 检查 Dirty 位,脏则写回主存
| 对比项 | Write-Through | Write-Back |
|---|---|---|
| 写性能 | 慢(每次写主存) | 快(只写 Cache) |
| 数据一致性 | 天然一致 | 需要软件维护 |
| 总线流量 | 高 | 低 |
| 典型场景 | 共享数据、外设寄存器 | 大块数据、性能敏感 |
我的建议: STM32 的 MPU(内存保护单元)可以按区域配置 Cache 策略。对性能要求高的数据区用 Write-Back,对外设寄存器或共享内存用 Write-Through 或 Non-Cacheable。
3.3 Cache 的替换策略
当 Cache 满了,新数据要进来,就得把旧数据踢出去。怎么踢?这就是替换策略的事。
3.3.1 常见替换算法
- LRU(最近最少使用):替换最久没被访问的 Line。命中率最高,但硬件开销大。
- FIFO(先进先出):替换最早进入的 Line。实现简单,但可能踢掉热点数据。
- Random(随机):随机选一个 Line 替换。硬件最简单,性能不稳定。
- PLRU(伪 LRU):用二叉树近似 LRU,硬件复杂度适中。ARM Cortex-A 系列常用。
STM32 的 Cortex-M7 和 M33 内核,Cache 替换策略通常是硬件固定的——你没法改。但了解它,能帮你理解为什么某些访问模式会频繁 Miss。
避坑指南: 如果你在循环里反复访问超过 Cache 大小的数组,LRU 策略会导致「Cache Thrashing」——数据刚加载就被替换,性能还不如不用 Cache。我曾经在图像处理项目里遇到这个问题,后来通过分块处理(Tiling)解决了。
3.3.2 替换策略对比
| 策略 | 命中率 | 硬件开销 | 适用场景 |
|---|---|---|---|
| LRU | 高 | 大 | 通用高性能 |
| FIFO | 中 | 小 | 顺序访问 |
| Random | 中低 | 极小 | 低功耗、简单场景 |
| PLRU | 接近 LRU | 中 | ARM 多核处理器 |
最后说一句:Cache 不是万能的。你写代码时,数据布局、访问模式、对齐方式,都会影响 Cache 效率。多核环境下,Cache 一致性更是个大坑——我们后面几章会重点讲。