Cache
思维导图¶
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 行号 = 主存块号 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 路以上
-
当每组只有一行时,则为直接映射
-
当只有一组时,则为全相联映射