计算机的三级缓存
计算机的三级缓存
CPU的处理速度远大于内存的吞吐速度,三级缓存是一种比内存读写速度更快的存储结构,用于缓解这种矛盾。
1 三级缓存结构
图 单核CPU下的三级缓存结构
三级缓存是集成在CPU内的缓存。L1最靠近CPU核心,L2其次。访问速度:L1 > L2 > L3。容量大小: L1 < L2 < L3。CPU首先在L1中查找目标,没找到则从L2查找,其次再从L3查找。
L1 通常为32-128kb,分为指令缓存L1i和数据缓存L1d。指令缓存:当前CPU核心正在执行的指令片段(如循环、条件片段机器码)。数据缓存:存储CPU核心正在计算的变量、临时结果。(如循环计数器、浮点运算中间值)时间局部性:频繁执行的指令会被保留在L1i中,预取连续地址的指令到L1i。空间局部性:连续内存地址的数据(如数组元素),会被预加载到L1d中。
L2 通常为256kb-2M。存储次高频数据。当L1缓存容量不足时,最近使用的数据会下沉到L2。
L3 通常为8-128M。缓存大型数据结构(如数据库查询结果)。
1.1 多核CPU下的三级缓存
图 多核CPU下的三级缓存
多核情况下,L3为所有该CPU所有核心共用,这样有利于减少多核竞争内存带宽,提升多任务性能。存储内容有全局变量、共享库代码等。
1.2 缓存行
缓存行(Cache Line)是CPU三级缓存管理数据的最小单位,也是内存与缓存之间数据传输的基本粒度。通常为64字节。
Tag标识内存地址 状态位MESI协议的状态 数据库64字节内容
表 缓存行的数据结构
即使程序仅需要1字节,也会加载其所在的64字节区域。
示例:若程序读取int变量(4字节),地址为0x1004,则CPU会加载包含0x1000-0x103F的整个缓存行。
2 缓存一致性问题
多核CPU或多CPU系统中,每个CPU内核都有自己的高速缓存,它们之间是互不可见的,但是它们都共享同一内存,这可能导致以下问题:
- 数据不一致:导致当一个核心对内存共享数据修改时,其他核心无法看到这个修改。
- 写入顺序混乱:多个核心对同一地址的写入顺序不确定。
2.1 总线(Bus)监听协议
基于总线的缓存一致性协议通过硬件机制解决多核缓存的数据一致性问题。
实现机制:
- 共享总线,所有核心通过总线连接,监听总线上其他核心的内存操作。
- 缓存行状态机,每个缓存行(通常64字节)维护状态。
- 事务广播,核心的缓存操作(读、写)会触发总线事务,其他核心监听并响应。
2.1.1 MESI协议
MESI是总线监听协议,定义了4种缓存行状态。
Modified 缓存行已被修改,仅当前核心有最新数据。触发:核心写入私有缓存,且未同步到其他核心或内存
Exclusive 缓存行未被修改,且仅当前核心持有该数据。触发:核心首次读取数据,且其他缓存无副本。
Shared 缓存行未被修改,且可能被多个核心共享。触发:多个核心从内存中读取同一地址数据。
Invalid 缓存行无效(数据过期或未加载)。其他核心修改了该地址的数据,或本地缓存被替换。
表 MESI协议的四种状态
2.1.2 总线监听协议的硬件实现
事务类型 触发条件 目的
Read 核心读取未缓存的地址或缓存行状态非独占。 获取数据的共享副本,标记为Shared或Exclusive
Read-for-Ownership(RFO) 核心需要修改数据(写操作) 获取独占权(Exclusive或Modified),并无效化其他核心的缓存行。
Invalidate 核心需要独占数据(如RFO事务) 广播通知其他指定缓存行标记为Invalid
Write-Back 核心需要替换Modified状态的缓存行。 将脏数据写回内存或共享缓存(L3),释放行空间。
Flush 显式刷新缓存指令 强制将缓存行写回内存并置为Invalid。
表 总线的事务类型
3 优化命中率
命中是指cpu核心能从L1(或L2、L3)缓存中直接获取数据,而不需要从内存中加载。
提升命中率的方法有:
- 将循环体控制在极小的代码范围内(避免大小超出L1)。
- 频繁访问的变量尽量紧凑排列(如结构体字段对齐),避免分散在内存中。
- 对于大型数据分块处理,确保每块数据能放入L2或L3。
- 减少不同核心对同一缓存行的竞争。
- 将多核共享的只读数据标记为常量,让其可能长期缓存在L3中。
3.1 字段对齐
字段对齐优化要求变量的地址必须为其字节大小的整数倍。
例如,读取一个8字节的double类型变量,如果其地址为0x1004(数据范围0x1004-0x100B),则需要分两次读取:1)读取0x1000-0x1007的数据;2)读取0x1008-0x100F;3)拼接有效部分。
3.1.1 对齐规则
1基本数据类型对齐,对齐到其字节大小。
char -> 对齐到1的倍数。
int -> 对齐到4的倍数。
2 结构体对齐,对齐到其最大成员类型的对齐值,且大小为对齐值的整数倍。
(成员变量优化排序顺序从小到大,如 double > int > short > char)
例如,结构体中有int、double、char类似变量各一个。
假如排序顺序为 int、double、short。按照规则,地址需要对齐到最大成员(double)的整数倍。int与double相邻,还差4个字节,那么编译器会在它们中间插入填充字段来填充这4个字节,最后,类的填充如下:
图 int、double、short排列下结构体的填充
结构体大小=4+4+8+2+6=24。排列优化如下:double、int、short
图 double、int、short排列下结构体的填充
结构体大小=8+4+2+2=16