Cache 策略:Write-Through / Write-Back + Allocation(分配策略)
- 写策略(Write Policy) :写内存时,Cache 和主存怎么同步:
Write Through/Write Back - 分配策略(Allocation Policy) :Cache miss(读缺失 / 写缺失)的时候,要不要把数据从主存加载进 Cache:
Write Allocate/No-Write Allocate
写策略 = 写的时候怎么更新内存;
分配策略 = Miss 的时候要不要捞数据进 Cache
文章目录
- [Cache 策略:Write-Through / Write-Back + Allocation(分配策略)](#Cache 策略:Write-Through / Write-Back + Allocation(分配策略))
-
- [一、写策略(2 种)](#一、写策略(2 种))
-
- [1. Write Through(WT,直写)](#1. Write Through(WT,直写))
- [2. Write Back(WB,回写,ARM 默认常用策略)](#2. Write Back(WB,回写,ARM 默认常用策略))
- [二、分配策略(针对 Cache Miss,有读缺失和写缺失)](#二、分配策略(针对 Cache Miss,有读缺失和写缺失))
-
- [1. Write Allocate(WA,写分配)](#1. Write Allocate(WA,写分配))
- [2. Read Allocate(No-Write Allocate,NWA,非写分配)](#2. Read Allocate(No-Write Allocate,NWA,非写分配))
- 常用组合
- [三、关键对比(结合 DMA)](#三、关键对比(结合 DMA))
- 四、一些问题
-
- [Write Through:为什么每次写都走 DDR?数据不是本来最终就要落 DDR 吗?图像 buffer 不用 WT 难道不会不一致?](#Write Through:为什么每次写都走 DDR?数据不是本来最终就要落 DDR 吗?图像 buffer 不用 WT 难道不会不一致?)
-
- [① "最终都要落 DDR" 和 **"每次写都同步到 DDR" 是两回事**](#① “最终都要落 DDR” 和 “每次写都同步到 DDR” 是两回事)
- [② 图像 buffer 不用 WT,会不会数据不一致?](#② 图像 buffer 不用 WT,会不会数据不一致?)
- [问题 2:WB 模式下,每次调用 `dma_sync_for_cpu`,确实要消耗资源,为什么还要这么选?](#问题 2:WB 模式下,每次调用
dma_sync_for_cpu,确实要消耗资源,为什么还要这么选?) -
- [先明确 `dma_sync_single_for_cpu` 在 ARM WB 下做什么](#先明确
dma_sync_single_for_cpu在 ARM WB 下做什么) - [对比两种方案的开销(OV8858 1632×1224 RAW10)](#对比两种方案的开销(OV8858 1632×1224 RAW10))
- [什么时候 WB+sync 的开销会变成瓶颈?](#什么时候 WB+sync 的开销会变成瓶颈?)
- 补充容易混淆的坑
- [先明确 `dma_sync_single_for_cpu` 在 ARM WB 下做什么](#先明确
- 总结
一、写策略(2 种)
1. Write Through(WT,直写)
写 Cache 的同时,同步写回主内存
- 流程:CPU 写数据 → 写入 Cache 行 并且同时写入 DDR 主存
- Cache 行永远和 DDR 保持一致
- 脏位 (dirty bit):没有脏位,不需要标记脏
✅ 优点:
Cache 和 DDR 永远一致;一旦 DMA 访问这块内存,不需要刷 Cache,天然一致性好
❌ 缺点:
- 每次写都走 DDR,写带宽开销巨大,性能差(为什么呢,最后会讲)
- 适合少量写、对一致性要求极高的场景,图像帧 buffer 一般不用 WT
2. Write Back(WB,回写,ARM 默认常用策略)
写只写到 Cache,不立刻写 DDR;只有 Cache 行被淘汰 (evict) 时,才一次性刷回 DDR
- 流程:CPU 写 → 仅更新 Cache 行,DDR 暂时不变
- 引入 dirty bit(脏位):标记这行 Cache 数据和 DDR 不一致
- dirty=1:Cache 最新,DDR 旧
- dirty=0:Cache 和 DDR 一致
✅ 优点:
- 大量写操作命中 Cache 时,减少 DDR 访问,性能高(Linux 内核默认就是 WB)
❌ 缺点:
- Cache 和 DDR 数据不同步
Linux 普通内存(CMA 分配的可 Cache 内存)是 WriteBack。 Sensor 硬件 DMA 直接读 DDR,Cache 里如果还有脏数据,硬件读到旧数据,图像花屏。 所以必须:
dma_sync_for_device把脏 cache 刷到 DDR。 硬件 DMA 写完帧到 DDR 后,CPU 要读图像,需要dma_sync_for_cpu,invalidate cache,扔掉 CPU 旧缓存,从 DDR 拿新帧。
二、分配策略(针对 Cache Miss,有读缺失和写缺失)
Read Allocate(读分配):读 Cache miss 时,把对应内存行加载进 Cache 绝大多数 CPU 默认开启 Read Allocate,基本都打开,很少关。
下面两个是写缺失时的策略(写的时候,这个地址不在 Cache 里)
1. Write Allocate(WA,写分配)
写缺失:数据不在 Cache → 先把 DDR 的数据读到 Cache 行,然后再在 Cache 里做写修改
先 load,再 write。
- 搭配 WB 最常见。适合:连续多次重复写同一个区域(循环改写一块 buffer)
- 代价:写 miss 多了一次读 DDR 操作
2. Read Allocate(No-Write Allocate,NWA,非写分配)
写缺失:数据不在 Cache → 不加载到 Cache,直接写 DDR,Cache 完全不参与
不会把这行数据读到 Cache 里
- 适合:一次性大块写,写完不再复用这块内存(比如一次性写日志)
- ARM Linux 默认:
WriteBack + ReadAllocate + No-WriteAllocate
常用组合
- WriteBack + ReadAllocate + No-WriteAllocate(Linux 普通内存默认,CMA 内存就是这组)
- 读 miss:捞进 cache
- 写 hit:只写 cache,标记 dirty,不写 DDR
- 写 miss:直接写 DDR,不把行读到 cache
- WriteThrough + ReadAllocate 直写,写同时更新 DDR,无脏位。很少用于大 buffer。
补充:ARM 中
uncache内存:完全不走 Cache,所有读写直接访问 DDR,没有 WT/WB 概念 dma_alloc_coherent 拿到的就是 uncache 内存,不存在 cache 同步,代价是 CPU 读写很慢。
三、关键对比(结合 DMA)
| 属性 | WriteBack | WriteThrough | Uncache |
|---|---|---|---|
| 脏位 | ✅有 dirty bit | ❌无 | ❌无 |
| 写操作 | 先写 Cache,淘汰才写 DDR | 写 Cache 同时写 DDR | 直接访问 DDR |
| Cache-DDR 一致性 | 不一致,需要 dma_sync | 基本一致 | 天然一致 |
| CPU 读写性能 | 最高 | 较差 | 最差 |
| 适用场景 | CMA 流式 DMA,图像 buffer | 寄存器 / 少量共享内存 | 一致性 DMA (dma_alloc_coherent) |
四、一些问题
Write Through:为什么每次写都走 DDR?数据不是本来最终就要落 DDR 吗?图像 buffer 不用 WT 难道不会不一致?
① "最终都要落 DDR" 和 "每次写都同步到 DDR" 是两回事
先理解 WT 的硬件行为:
Write Through:CPU 写 Cache 行,同时并行发起写 DDR 请求。Cache 只是副本,每一次 store 指令,都要往 DDR 发写事务。
WriteBack:CPU 写只改 Cache,不立刻发起 DDR 写;只有 Cache 行被淘汰 evict,或者主动 clean,才一次性批量写到 DDR。
举个例子:CPU 在图像 buffer 里连续写 100 个字节:
- WT:100 次写操作 → 100 次 DDR 写事务。哪怕这 100 字节在同一个 Cache 行,每一次 CPU 写都触发 DDR 访问。
- WB :100 次写命中同一 Cache 行 → 只修改 Cache,0 次 DDR 访问 ;等到这一行要被挤出去,一次性1 次 burst 写把整行(64B)刷到 DDR。
最终数据确实都会落到 DDR,但是访问 DDR 的时机、次数、带宽开销完全不一样 。 WT:写操作即时同步 ;WB:写操作延迟批量同步。
② 图像 buffer 不用 WT,会不会数据不一致?
不是硬件自动不一致,是 "没有手动同步才会不一致"。
- WT:硬件保证写 Cache 同时写 DDR → Cache 和 DDR 永远一致。DMA 硬件读 DDR,直接拿到最新数据,不需要软件做 cache 同步。
- WB:硬件不会自动同步。CPU 修改数据留在 Cache,DDR 是旧值。此时 DMA 直接读 DDR,读到旧数据 → 花屏。 👉 但 WB 提供了软件主动同步接口(dma_sync_xxx) ,在合适的时间点一次性同步,而不是每次 CPU 写都同步。
核心权衡: WT:硬件每次写自动同步,带宽开销持续存在 WB:平时不用同步,只在 CPU 和硬件交接 buffer 的那一刻,一次性同步一次
图像场景:一帧 2.4MB,CPU 可能只会在帧处理完成后交接一次 buffer,只做 1 次同步; 如果用 WT,CPU 每一次写像素都访问 DDR,持续占用 DDR 带宽,30fps 大分辨率场景带宽压力巨大。 这就是图像 buffer 不用 WT 的根本原因。
问题 2:WB 模式下,每次调用 dma_sync_for_cpu,确实要消耗资源,为什么还要这么选?
✅ 一句话结论:开销确实存在,但是相比全程 WT 的持续带宽开销,这个一次性开销小得多,收益远大于代价。
先明确 dma_sync_single_for_cpu 在 ARM WB 下做什么
Sensor(DMA 硬件)写完一帧到 DDR 之后,CPU 要读这帧图像: 调用 dma_sync_for_cpu → invalidate 对应 buffer 对应的 Cache 行 含义:告诉 CPU,Cache 里这一块区域的副本作废;CPU 后续访问这块内存,必须从 DDR 重新读新数据。
⚠️ 注意:这个场景不需要 clean!因为是硬件写 DDR,CPU 之前只是读,没有写这块 buffer,Cache 行不会有 dirty 标记。所以只是 invalidate,开销会比 clean 小。
对比两种方案的开销(OV8858 1632×1224 RAW10)
- 方案 A:WT 内存 CPU 读 / 写像素时,每一次内存访问都要访问 DDR。 只要 CPU 在处理图像(裁剪、统计、isp 后处理),全程持续占用 DDR 带宽 。30fps 持续跑,带宽压力一直在。 带宽开销:持续生效,每一次像素读写都产生 DDR 流量。
- 方案 B:WB + dma_sync_for_cpu(流式 DMA,CMA 内存)
- Sensor 写帧阶段:CPU 不碰 buffer,没有任何 cache 开销
- 硬件写完一帧,只执行一次 invalidate,把这一帧对应的 cache 行作废
- 之后 CPU 处理这帧图像:全部走 Cache,读像素在 L1/L2 cache 命中,DDR 访问极少,处理速度快很多
- 处理完,交给下一次 DMA 前,按需做 clean 同步(如果 CPU 修改了 buffer)
👉 开销模型区别: WT:细粒度、持续的硬件开销 (每一次 CPU 写都走 DDR) WB+sync:粗粒度、一次性的软件触发开销(一帧只同步 1 次)
只有在 buffer 交接点才付出同步代价;而图像处理的主体阶段,CPU 享受 Cache 高速读写。
什么时候 WB+sync 的开销会变成瓶颈?
- 分辨率极大、fps 极高,每帧同步的 cache 操作耗时累积;
- buffer 非常零散,同步要遍历大量不连续 cache 行;
- CPU 核心负载已经拉满,cache 操作挤占 CPU 流水线。
在 OV8858 这种 1632x1224@30fps 场景:单帧 invalidate 开销很小,完全可接受。
补充容易混淆的坑
- dma_sync 不是每次都刷全部 2.4M 内核会记录 buffer 的有效范围,dma_sync 只会操作buffer 占用的 Cache 行,不是无脑遍历整个地址空间。
- 区分两种场景的同步操作(非常重要,摄像头必考)
- CPU → DMA 硬件(CPU 填 buffer,交给 sensor DMA 读取):
dma_sync_for_device→ clean,把 Cache 里脏数据刷回 DDR,保证硬件读到最新数据 - DMA 硬件 → CPU(sensor 写完帧,CPU 读图像):
dma_sync_for_cpu→ invalidate,丢弃 CPU 旧 Cache 副本,从 DDR 拿硬件新写的数据
只有 CPU 曾经写过这块 buffer,才会产生 dirty 行,才需要 clean;单纯硬件写完,CPU 只做读,只需要 invalidate。
总结
WT 是每次 CPU 写都同步 DDR,一致性由硬件保证,但每一次写都占用 DDR 带宽,持续消耗带宽,图像大数据场景性能差。 WB 不会每次写落 DDR,存在数据不一致风险,但我们只在 CPU 和 DMA 硬件交接帧 buffer 时,调用 dma_sync 做一次性 cache 操作。 虽然 dma_sync 有开销,但只是帧与帧之间少量一次性开销;图像处理阶段 CPU 可以利用 Cache 加速,整体带宽和性能远优于 WT。