3. 缓存优化技术:缓存分区与路预测、缓存压缩、写缓冲与写合并、非阻塞缓存与预取
缓存,说白了就是芯片的「临时记忆」。但这份记忆的功耗,往往能占到整个芯片的 30%-50%。我见过太多团队,CPU 算力做得飞快,结果一测功耗,缓存漏电和动态切换占了半壁江山。嗯,今天我们就来聊聊怎么把这部分功耗降下来。
3.1 缓存分区与路预测
先问一个问题:为什么缓存要分区?
传统缓存是全相联或组相联的。每次访问,所有路(Way)都要同时比较 Tag。这就像你进图书馆,管理员把所有书架都翻一遍——功耗自然高。我个人习惯的做法是:把缓存分成多个独立的子区,每次只激活可能命中那个区。
3.1.1 缓存分区(Cache Partitioning)
缓存分区有两种主流方式:
- 静态分区:在编译或启动时固定分配。比如 L1 数据缓存分成 4 个 8KB 的 bank,每个核心或线程独占一个 bank。优点是实现简单,缺点是利用率不高。
- 动态分区:运行时根据访问模式调整。我记得在某个 AI 加速器项目中,我们用了基于「重用距离」的动态分区算法,把缓存命中率提升了 12%,同时动态功耗下降了 18%。
3.1.2 路预测(Way Prediction)
路预测是另一种思路。它不分区,而是预测「这次访问最可能命中哪一路」。预测对了,只激活那一路的 Tag 比较;预测错了,再全路查找。
我常用的路预测策略有三种:
| 策略 | 原理 | 功耗节省 | 命中率影响 |
|---|---|---|---|
| MRU(最近最多使用) | 预测命中最近使用的那一路 | 30%-40% | 几乎无影响 |
| PC-based 预测 | 根据程序计数器(PC)历史记录预测 | 40%-50% | 命中率下降 1%-3% |
| 混合预测 | MRU + PC 结合,带置信度计数器 | 45%-55% | 命中率下降 <1% |
3.2 缓存压缩技术
缓存压缩,说白了就是用更少的存储空间装更多的数据。你想想看,如果缓存能压缩到原来的一半大小,那漏电功耗直接砍半,动态功耗也因为访问次数减少而下降。
3.2.1 常见压缩算法
在缓存里做压缩,不能太复杂。解压延迟超过 2 个周期,基本就没意义了。我常用的有:
- BDPI(Base-Delta-Immediate):适合数值相近的数据,比如数组元素。压缩率 1.5x-2x,解压延迟 1 周期。
- FPC(Frequent Pattern Compression):识别常见数据模式(全 0、全 1、符号扩展等)。压缩率 1.3x-1.8x,解压延迟 1-2 周期。
- 字典压缩:维护一个小型字典,重复数据只存索引。适合有大量重复值的场景,比如稀疏矩阵。
3.2.2 压缩缓存架构
我参与过一个 RISC-V 处理器的 L2 缓存压缩项目,架构是这样的:
3.3 写缓冲与写合并
写操作是缓存功耗的大头。为什么?因为写操作通常需要先读(Read-Modify-Write),而且写回主存的功耗极高。写缓冲和写合并就是用来「攒一批再写」的技术。
3.3.1 写缓冲(Write Buffer)
写缓冲是一个 FIFO 队列,CPU 写缓存时,数据先进入写缓冲,然后异步写入缓存阵列。好处是:
- CPU 不用等写完成,减少流水线停顿
- 多个写操作可以合并,减少对缓存阵列的访问次数
我一般建议写缓冲深度为 4-8 项。太浅了合并效果差,太深了控制逻辑复杂且面积大。
3.3.2 写合并(Write Merging)
写合并是写缓冲的进阶版。当多个写操作命中同一个缓存行(Cache Line)时,把它们合并成一个写操作。举个例子:
// 原始写操作序列(假设缓存行大小为 64 字节)
写地址 0x1000,数据 0x12345678(4 字节)
写地址 0x1004,数据 0x9ABCDEF0(4 字节)
写地址 0x1008,数据 0x11223344(4 字节)
// 写合并后
写地址 0x1000,数据 0x112233449ABCDEF012345678(12 字节,合并成一次写)
3.4 非阻塞缓存与预取
最后聊聊非阻塞缓存和预取。这两个技术看似是性能优化,但用好了,功耗也能降不少。
3.4.1 非阻塞缓存(Non-blocking Cache)
传统缓存遇到 Miss 时,会阻塞 CPU 直到数据回来。非阻塞缓存允许在 Miss 处理期间继续处理后续的 Hit 请求。这减少了 CPU 的空转时间,间接降低了功耗。
非阻塞缓存的核心是 Miss Status Holding Register(MSHR)。每个 MSHR 记录一个未完成的 Miss 请求。我常用的配置是:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| MSHR 数量 | 4-8 | 太少无法隐藏延迟,太多增加功耗 |
| 每个 MSHR 的合并能力 | 2-4 次 | 多个 Miss 到同一缓存行可合并 |
| MSHR 查找方式 | 全相联 | 面积大但查找快,适合少量 MSHR |
3.4.2 预取(Prefetching)
预取是在 CPU 真正需要数据之前,提前把数据加载到缓存里。预取做得好,缓存 Miss 率下降,主存访问次数减少,功耗自然降低。
我常用的预取策略:
- 顺序预取:访问地址 A 后,预取 A+1、A+2... 适合数组遍历。预取深度一般 2-4 条缓存行。
- 步长预取:检测固定步长的访问模式(如 A, A+16, A+32...)。适合矩阵运算。
- 基于 PC 的预取:记录每个指令的历史访问模式,预测下一次访问地址。
3.5 本章小结
缓存优化不是单一技术能搞定的。我个人的经验是:先做分区或路预测,把动态功耗降下来;再考虑压缩,降低漏电功耗;写缓冲和写合并是「顺手」就能做的优化;非阻塞缓存和预取则需要在性能和功耗之间找平衡。你想想看,这些技术组合起来,缓存功耗降低 40%-60% 是完全可行的。