# 思维导图
# Cache 的结构
# Cache 是小容量、高速缓冲存储器,由 SRAM 组成
# 一般将 Cache 和主存的存储空间都划分为若干大小相同的块并映射
把主存划分成大小相等的主存块 (Block)
Cache 中存放一个主存块的对应单位称为行 (line) 或槽 (Slot) 或项 (Entry) 或块 (Block),一段 Cache 行包括:
Valid 位,1 位
LRU 位
Dirty 位,1 位,采用 Write Back (写回、一次性写、回写) 时需要
Tag 位
Data 数据
主存块 (Block) 与 Cache 中的 行 / 槽 / 项 / 块 映射
# Cache 中存放最近访问的数据
# Cache 工作原理
# 程序运行时,CPU 使用的一部分数据 / 指令会预先成批拷贝到 Cache 中,Cache 的内容是主存储器中部分内容的映象 (副本)
# 当 CPU 需从主存读 (写) 数据或指令时,先查看 Cache
失效或缺失 (miss)
若被访问信息不在 cache 中,则直接从 Cache 中取,不用访问主存命中 (hit)
若被访问信息在 cache 中,则直接访问主存
# Cache 对程序员 (编译器) 是透明的
# 对操作系统程序员也是基本透明的
# 对一些内核高级程序员不是透明的
# 程序访问局部性
# 时间局部性 (Temporal Locality) :
刚被访问过的存储单元很可能不久又被访问
所以让最近被访问过的信息保留在靠近 CPU 的存储器中
# 空间局部性 (Spatial Locality) :
刚被访问过的存储单元的邻近单元很可能不久被访问,单个数据不考虑空间局部性
所以将刚被访问过的存储单元的邻近单元调到靠近 CPU 的存储器中
# 块大小的设置需利用空间局部性
- 块太小,无法利用空间局部性,发生缺失的概率增大
# Cache 在计算机存储层次结构中的位置
#
# Cache 位于 CPU 内,速度几乎与 CPU 一样快
# 块 (Block) 是一个定长块,是两层存储器之间存储信息的最小单元。Cache 是主存一部分的副本
# Cache 替换算法 / 淘汰策略问题
# 当一个新的主存块需要复制到 Cache 中时,如果 Cache 中的对应行已经全部被占满,如何选择被替换掉的 Cache 行?
直接映射(Direct Mapped)Cache
映射唯一,无条件用新信息替换老信息N 路组相联(N-way Set Associative)Cache
每个主存数据有 N 个 Cache 行可选择,需考虑替换哪一行全相联(Fully Associative)Cache
每个主存数据可存放到 Cache 任意行中,需考虑替换哪一行
# 先进先出(First In First Out,FIFO)
总是把最近最少用的那一块淘汰掉,利用时间局部性
加一位标记位标记当前哪一行是最先进入的
替换掉之后按顺序将下一行标记为最先进入的,最后一行被替换掉则标记第一行
# 最近最少用(Least Recently Used,LRU)
总是把最近最少用的那一块淘汰掉,利用时间局部性
具体实现:通过给每个 Cache 行设定一个计数器,根据计数值来记录这些主存块的使用情况。这个计数值称为 LRU 位。
组相联每组 n 行时,LRU 位长度 = log_2 (n)
全相联相当于组相联只有一组,LRU 位长度 = log_2 (Cache 的行数)
命中时,被访问行的 LRU 位置 0,其他行 LRU 位加 1,其余不变
未命中且该组未满时,新行 LRU 位置为 0,其余全加 1
未命中且该组已满时,LRU 位最大的那一行中的主存块被淘汰,新行 LRU 位置为 0,其余加 1
LRU 位的最大值 = n-1 (从 0 开始计数)
LRU 算法的命中率随组中行数的增大而提高
# 随机替换(Random)
随机地从候选的槽中选取一个淘汰,与使用情况无关
模拟试验表明,随机替换算法在性能上只稍逊于 LRU 算法,而且代价低!
# 最不经常使用 LFU
# 相关指标与术语
# 命中(Hit):要访问的信息在 Cache 中
Hit Rate (命中率 p): 在 Cache 中的概率
Hit Time (命中时间 Tc) :访问 Cache 所需时间,包括:判断时间 + Cache 访问
# 失效(Miss):要访问的信息不在 Cache 中
Miss Rate (缺失率 / 失效率) = 1 - (Hit Rate) = 1-p
Miss Penalty (失效损失 Tm):从主存将一块数据读取到 Cache 所需时间,包括访问主存块,向上逐层传输块直至将数据块放入发生缺失的那一层所需时间。
# 平均访问时间 = p × Tc + (1-p) × (Tm + Tc) = Tc + (1-p) × Tm
【失效损失 Tm】 不包括 【命中时间 Tc】!考点
# Cache 抖动
由于内存中不同主存块都映射到 Cache 同一行,某些块在主存和 Cache 之间频繁传送,产生了频繁的 Cache 替换,称之为 Cache 抖动
可能引起 Cache 抖动的 Cache 失效类型
容量失效
冲突失效
增加容量和相联性,有助于缓解这种现象
# 逻辑地址
CPU 给出的虚拟地址
# 物理地址 / 主存地址
在 Cache 和主存中查询所用的地址
Tag 位,与 Cache 中的 Tag 位比较,相等则命中
Index 位,组号位,只有组相联才有
Offset 位,在 Cache 行或主存块中的偏移量,即最低位块内地址
# 关联度
主存块映射到 Cache 时,可能存放的位置个数
关联度最低?直接映射(关联度为 1)
关联度居中?N - 路组相联映射(关联度为 N)
关联度最高?全相联映射(为 Cache 行数)
Cache 大小和块大小一定的情况下直观结论
提高关联度通常能够降低缺失率 (miss rate)
提高关联度通常会增加命中时间
# Cache 的失效率(Miss Rate)
# Miss Rate 与 Cache 大小、Block 大小、映射方式、Cache 级数等有关
Cache 大小:
Cache 越大,Miss Rate 越低
但成本越高!
Block 大小:
Block 越大,Miss Rate 越低,因为空间局部性充分发掘
Block 大小在 Cache 大小中所占的比例增加到一定程度时, Miss Rate 也会随之增加
Cache 不扩大时 Cache Block 的总数变少了,冲突变大
Cache 映射方式
Cache 容量小时, Cache 映射方式对 Miss Rate 有影响
Cache 容量大时,Cache 映射方式对 Miss Rate 影响不大
- 映射到同一组的概率降低
# Cache 失效类型
强制失效 (Compulsory misses)
首次访问某数据块时,必然引起的 Cache 失效
增加 Block 大小,有利于减少此类不命中
容量失效 (Capacity misses)
Cache 不能存放程序运行所需的所有块,替换后再次被使用所引起的 Cache 失效
增加 Cache 大小,有利于减少此类不命中
冲突失效 (Conflict misses)
映射到同一组的数据块个数超过组内可容纳的块时,竞争所引起的 Cache 失效
全相联没有此类失效,但价格贵且访问速度慢
# Cache 的一致性问题
# 导致 Cache 和主存数据不一致的情况
情况 1:当 Cache 中的内容进行更新时,而没有改变主存中的相应内容时,Cache 和主存之间产生了不一致 (inconsistent)
情况 2:当多个设备都允许访问主存时
- 例:I/O 设备可通过 DMA 方式直接读写内存时,如果 Cache 中的内容被修改,则 I/O 设备读出的对应主存单元的内容无效;若 I/O 设备修改了主存单元的内容,则对应 Cache 行中的内容无效。
情况 3:当多个 CPU 都有各自私有的 Cache 并且共享主存时
- 例:某个 CPU 修改了自身 Cache 中的内容,则对应的主存单元和其他 CPU 中对应的 Cache 行的内容都要变为无效。
# Cache 写机制
写命中 (Write Hit)
要写的单元已经在 Cache 中时Write Through (写直达、写通过、直写、写穿透)
当一个写操作产生时,无论写的是 Cache 还是主存,新值同时写到 Cache 和主存的块中问题:内存速度太慢,所以会导致 CPU “被拖累”,CPU 性能降低
一种改进措施:在 Cache 和主存之间使用写缓冲 (Write Buffer)
当一个数据等待被写入主存时,先将其存入写缓冲;
在把数据写入 Cache 和写缓冲后,处理器继续执行命令;
当写主存操作结束后,写缓冲里的数据释放
写穿透 / 直达 Cache 可用写分配或写不分配
Write Back (写回、一次性写、回写)
当一个写操作产生时,新值仅被写到 Cache 中,而不被写入主存特点
大大降低主存带宽需求
提高系统性能
控制可能很复杂
每个 Cache 行都设置一个修改位 (“dirty bit - 脏位”)
如果修改位为 “0”,则说明对应主存块没有被修改过
如果对应 Cache 行中的主存块被修改,就同时置修改位为 “1”
只有当修改位为 “1” 的块从 cache 中替换出去时,才把它写回主存
(写不命中时 Cache 里没有有效的对应块,即使写分配也不需要考虑 Cache 中的数据。但是写分配写入 Cache 新的行时,被替换掉的行不能忘了考虑写回)修改位为 “0” 的块从 cache 中替换出去时,不写回主存
写回 Cache 通常用写分配
写不命中 (Write Miss)
要写的单元不在 Cache 中时Allocate-on-miss (写分配)
更新主存块中相应单元,再将该主存块装入 Cache;
试图利用空间局部性,但增加了从主存读入一个块的开销
No-allocate-on-write (写不分配)
直接写主存,不把被写数据放入 Cache,同时将 Cache 中该行 Valid 位置 0;
减少了从主存读一个块的开销,但没有利用空间局部性
# Cache 和主存之间的映射
# 直接映射 / 模映射 (Direct Mapped)
原理:
- 把主存的每一块映射到一个固定的 Cache 行中。
Cache 总行数为 N,将存储空间总块数为 M 的主存分成 K = M/N 个组群,每个组群有 N 个块;
即每个主存地址对应于高速缓存中唯一的地址,也称模映射(本质是哈希)
- 把主存的每一块映射到一个固定的 Cache 行中。
映射关系为:Cache 行号 = 主存块号 mod Cache 行数
物理地址
Tag 位,标记位
长度 = log _2 (主存中 块 的 数量 / Cache 中 块 的 数量) =log_2 (K)Index 位,Cache 槽号
长度 = log _2 (Cache 中 块 的 数量) = Log_2 (N)Offset 位,块内地址
长度 = log _2 (块 的 大小) (按编址单位算)
特点
关联度 = 1
易实现 (求低地址就行了) 电路简单,命中时间短
淘汰 / 替换策略简单
不灵活,Cache 存储空间得不到充分利用,命中率低,抖动多
# 全相联映射(Fully Associative)
原理:
主存块可装到 Cache 任一行 / 槽中,称为全相联映射
Cache 有几个 slot 就最多需要查找多少次 slot,查找时需要一个 slot 一个 slot 比较 Tag 位查找(除非并行同时比较所有 Cache 行的 Tag,硬件实现更复杂)物理地址
Tag 位,标记位
长度 = log _2 (主存中 块 的 数量)Offset 位,块内地址
长度 = log _2 (块 的 大小) (按编址单位算)
特点:
关联度 = Cache 的行的数量
块冲突概率低:只要有空闲 Cache 块,都不会发生冲突
实现复杂 (比较逻辑的硬件代价大)、速度慢
# 组相联映射(N-way Set Associative)
n - 路组相联将总行数为 N 的 Cache 分成 S = N/n 组,每一组有 n 行;
将存储空间总块数为 M 的主存分成 K = M/S = M*n/N 个组群,每个组群有 S 个块;
把主存块映射到 Cache 固定组的任一行中。即:组间模映射、组内全映射物理地址
Tag 位,标记位
长度 = log _2 (主存中 块 的 数量 / 组的数量) = log _2 (K)组号位
长度 = log _2 (组 的 数量) = log _2 (S)Offset 位,块内地址
长度 = log _2 (块 的 大小) (按编址单位算)
特点
关联度 = n
结合直接映射和全相联映射的优点。当 Cache 的组数为 1 时,则为全相联映射;
每组两个行(2 路组相联)较常用。在较大容量的 L2 Cahce 和 L3 Cahce 中使用 4 路以上
当每组只有一行时,则为直接映射
当只有一组时,则为全相联映射