缓存miss了咋整?传统做法是CPU干等着,直到数据从内存回来。无阻塞缓存(Non-blocking Cache)打破了这个规矩——即使前面有个miss正在处理,后面的请求该干嘛干嘛。这篇聊聊这个技术的原理、实现和效果。
1. 阻塞缓存的痛点
想象你在餐厅排队点餐:
- 前面的人点了个复杂的套餐(缓存miss,需要等很久)
- 你只想点杯咖啡(缓存hit,很快)
- 但阻塞式服务要求你必须等前面的人拿完餐,才能点你的咖啡
这显然不合理。CPU里的缓存也是这个道理。
1.1 实际性能损失
1994年,Farkas和Jouppi在DEC Western Research Lab做了个实验[1]。他们模拟了一个8KB单级缓存,miss penalty 14个周期(90年代初的典型配置)。
结果发现:SPECINT92基准测试中,处理器因为缓存miss停顿的时间占总执行时间的比例高达20-30%。这意味着每5条指令就有1条在等内存。
2. 无阻塞缓存的两级优化
无阻塞缓存不是单一技术,而是两个层次的优化:
2.1 Hit Under Miss(缺失时命中)
核心思想:前面有个load miss了,后面的load如果hit,照样可以服务。
效果:Farkas & Jouppi的测试显示[2],允许1个hit under miss时:
- SPECINT92:有效miss penalty降低20%
- SPECFP92:有效miss penalty降低30%
FP程序提升更明显,因为科学计算常有规律的内存访问模式,miss后紧接着的访问往往还在缓存里。
2.2 Miss Under Miss(缺失时缺失)
核心思想:允许同时发出多个内存请求,重叠它们的延迟。
这需要内存系统支持多未完成请求(Multiple Outstanding Requests)。现代DDR内存控制器可以管理几十个待处理的请求,通过bank-level parallelism提升带宽。
效果:Li等人在2011年更新了这项研究[3],基于Intel Core i7-like模型测试SPEC2006:
| 允许重叠的miss数 | SPECINT2006延迟降低 | SPECFP2006延迟降低 |
|---|---|---|
| 1个hit under miss | ~9% | ~12.5% |
| 2个miss under miss | ~9% | ~12.5% |
| 64个miss under miss | ~9% | ~12.5% |
有趣的是,现代处理器中,单纯增加miss under miss的数量收益有限。因为:
- L3缓存已经很大(8-32MB),L2 miss rate本身就很低
- 乱序执行处理器本身就能掩盖部分延迟
- 内存带宽成为瓶颈,同时发太多请求反而冲突
3. 硬件实现:MSHR
无阻塞缓存的核心部件是MSHR(Miss Status Handling Register,缺失状态处理寄存器)[4]。
3.1 MSHR是干嘛的
当缓存miss时,MSHR记录:
- 缺失的内存地址
- 请求类型(load/store)
- 目标寄存器(load的话)
- 等待该数据的后续指令列表
一个MSHR条目对应一个正在处理的miss。条目数决定了能同时处理的miss数量。
3.2 实际芯片的MSHR配置
| 处理器 | L1D MSHR | L2 MSHR | 备注 |
|---|---|---|---|
| DEC Alpha 21164[5] | 6 entries (MAF) | 2 entries (BAF) | 1995年经典设计 |
| Intel Core i7[3] | 10+ entries | 16+ entries | 支持复杂乱序执行 |
| NVIDIA GPU[6] | 16 entries | 128 entries | 高并行需求 |
| ARM Cortex-A77 | 有限支持 | 有限支持 | 移动端简化设计 |
Alpha 21164的MSHR叫MAF(Miss Address File),6个条目。这6个条目不仅记录miss,还合并对同一块缓存行的多次访问。比如:
// 假设addr1和addr2在同一缓存行(64字节对齐)
load r1, [addr1] // miss,分配MAF entry 0
load r2, [addr2] // 同一块,合并到entry 0,不新增请求
这样内存控制器只发一次请求,但两个load都能得到满足。
3.3 MSHR的硬件结构
简化版MSHR条目结构:
// MSHR Entry 简化结构
module mshr_entry (
input valid, // 该条目是否有效
input [31:0] miss_addr, // 缺失的物理地址
input [3:0] block_offset, // 块内偏移(用于merge检查)
input is_load, // 是load还是store
input [4:0] dest_reg, // load的目标寄存器
input [7:0] waiting_mask, // 等待该数据的指令位图
output filled // 数据是否已从内存返回
);
// 同一块检查逻辑
wire same_block = (addr[31:6] == miss_addr[31:6]);
wire can_merge = valid & same_block & ~filled;
endmodule
MSHR需要支持:
- 分配:新miss时找空闲条目
- 合并:检查新请求是否匹配已有条目
- 唤醒:数据返回时通知所有等待者
- 释放:数据填入缓存后回收条目
4. 经典案例:DEC Alpha 21164
DEC Alpha 21164是1995年发布的处理器,最早实现完整无阻塞缓存的商用芯片之一[5][7]。
4.1 缓存层次
| 层级 | 大小 | 相联度 | 延迟 | 特性 |
|---|---|---|---|---|
| L1I | 8KB | 直接映射 | 1 cycle | 指令缓存 |
| L1D | 8KB | 直接映射 | 2 cycles | 双端口,无阻塞 |
| L2 | 96KB | 3-way | 8 cycles | 片上,write-back |
| L3 | 可选 | 直接映射 | 12+ cycles | 片外 |
4.2 无阻塞机制
21164的L1D支持:
- 6-entry MAF:位于L1和L2之间,缓存L1 miss的地址
- 2-entry BAF:位于L2和外部内存之间,管理L2 miss
- Load合并:最多21个load可以合并到同一个MAF entry
- 双端口:每周期可以服务2个load hit,即使正在处理miss
关键设计:21164的L1D是write-through的,所有store直接下推到L2。这样L1只需要处理load的miss,简化了MSHR设计。
4.3 性能数据
21164在300MHz时达到:
- SPECint92:约345分
- SPECfp92:约505分
作为对比,同期Intel Pentium(66MHz)大约只有200分。无阻塞缓存是21164高性能的关键因素之一。
5. 现代处理器的演进
5.1 Intel Core系列
现代Intel Core处理器(i3/i5/i7/i9)继承了无阻塞缓存设计,但更加复杂:
- L1D:32KB,8-way,4-cycle延迟,支持hit under miss
- L2:256-512KB,支持miss under miss
- L3:8-32MB,共享,支持大量并发miss
Intel的MSHR设计不公开具体条目数,但从性能反推,L1D大约支持10-16个未完成请求,L2支持更多。
5.2 移动端 vs 桌面端差异
| 特性 | 桌面/服务器(Core i7) | 移动端(ARM A77) |
|---|---|---|
| L1D MSHR | 多条目(10+) | 有限或没有 |
| L2 MSHR | 支持 | 有限支持 |
| 设计重点 | 吞吐量最大化 | 功耗/面积优化 |
| 乱序执行 | 深度(192+ entries ROB) | 中等(128 entries) |
ARM Cortex-A77的L1D实际上是有阻塞的,或者说只支持非常有限的非阻塞能力。因为移动端:
- 功耗敏感,MSHR消耗静态功耗
- 内存带宽有限,同时发多个请求收益小
- 工作负载不同,移动应用缓存miss模式更简单
6. 什么时候无阻塞缓存没用
不是所有场景都能从无阻塞缓存受益。
6.1 顺序执行处理器
如果处理器是顺序执行的(如简单的嵌入式MCU),一条指令不完成,后面的指令不能发射。这时候无阻塞缓存帮不上忙,因为后面的load根本进不来。
6.2 内存带宽瓶颈
如果内存控制器已经饱和,同时发更多请求只会增加排队延迟,不会提升吞吐量。这时候需要更大缓存或更快内存,而不是更深的MSHR。
6.3 单线程顺序访问
// 指针追逐,每次访问依赖前一次结果
while (node) {
node = node->next; // 必须等这次load完成才能知道下一次地址
}
这种代码无法利用hit under miss,因为下一次访问地址依赖于当前load的结果。乱序执行也帮不上忙。
7. 总结
无阻塞缓存通过MSHR结构,让CPU在等待慢速内存时也能处理其他请求。关键要点:
| 技术 | 效果 | 硬件代价 |
|---|---|---|
| Hit Under Miss | 降低20-30%有效miss penalty | MSHR条目,合并逻辑 |
| Miss Under Miss | 重叠多个内存延迟 | 更多MSHR条目,内存控制器支持 |
设计权衡:
- 高性能处理器(服务器/桌面):深MSHR,支持大量并发miss
- 低功耗处理器(移动/嵌入式):简化或省略MSHR,节省面积功耗
历史意义:从1994年Farkas & Jouppi的理论研究,到1995年Alpha 21164的首次商用实现,再到现代Intel/AMD/ARM处理器的标配,无阻塞缓存已成为高性能CPU的基石技术。
参考
-
Farkas, K. I., & Jouppi, N. P. (1994). Complexity/performance tradeoffs with non-blocking loads. ACM SIGARCH Computer Architecture News, 22(2), 211-222. ↩
-
计算机体系结构:量化研究方法(第5版),第2章:内存层次设计优化。 ↩
-
Li, S., Chen, K., Brockman, J. B., & Jouppi, N. P. (2011). Performance impacts of non-blocking caches in out-of-order processors. HP Labs Technical Report HPL-2011-65. ↩ ↩
-
Kroft, D. (1981). Lockup-free instruction fetch/prefetch cache organization. ISCA 1981. ↩
-
DEC Alpha 21164 Microprocessor Hardware Reference Manual, EC-QAEQBTE, Digital Equipment Corporation, 1994. ↩ ↩
-
LATPC: Accelerating GPU Address Translation Using Locality-Aware TLB Prefetching and MSHR Compression. MICRO 2024. ↩
-
Edmondson, J. H., et al. (1995). Superscalar instruction execution in the 21164 Alpha microprocessor. IEEE Micro. ↩