存储器层次结构——高速缓存存储器

存储器层次结构——高速缓存存储器 文章目录高速缓存存储器层次结构通用的高速缓存存储器组织结构直接映射高速缓存组相联高速缓存排版较难处理请务必访问此处阅读高速缓存存储器层次结构高速缓存是介于寄存器和主存储器之间的存储器L1 高速缓存一级缓存的访问速度几乎和寄存器一样快L1 高速缓存和主存之间又插入了一个更大的 L2 高速缓存有些现代系统还包括一个更大的位于 L2 高速缓存和主存之间的 L3 高速缓存。:::color1高速缓存既保存数据也保存指令。$ i-cache $只保存指令的高速缓存。通常是只读的。$ d-cache $只保存程序数据的高速缓存。统一的高速缓存既保存数据也保存指令的高速缓存。:::在$ Intel Core i7 处理器的高速缓存层次结构中每个 C P U 芯片有 4 个核每个核有私有的 的高速缓存层次结构中每个 CPU 芯片有 4 个核每个核有私有的的高速缓存层次结构中每个CPU芯片有4个核每个核有私有的L1 i-cache、 、、L1 d-cache $和 L2 统一的高速缓存。所有的核共享片上 L3 统一的高速缓存。这个层次结构的一个有趣的特性是所有的 SRAM 高速缓存存储器都在 CPU 芯片上。通用的高速缓存存储器组织结构核心组织维度 (S, E, B)一个通用高速缓存的结构可以用一个元组来完全确定组数 ()整个高速缓存被划分为个互不相交的“组”Set从组0一直到组。组索引需要个比特来定位。行数 ()每一个组内包含个“行”Line。根据的取值不同高速缓存可以分为直接映射高速缓存、组相联高速缓存和全相联高速缓存只有一个组。块大小 (字节)每一行中包含一个高速缓存块Cache Block存储着真正从主存中复制过来的连续数据大小为字节。块偏移需要个比特来定位。高速缓存行的内部组成高速缓存中的每一行都由三个关键部分组成有效位Valid bit每行有个有效位。用来指示这一行是否包含有效的数据1表示有效0表示无效。标记位Tag bits每行有个标记位其中为主存地址的总位数。因为多个主存地址可能会映射到同一个缓存组标记位用来唯一确定当前缓存块中存储的到底是哪一个内存块。数据块Data Block即图中的。它是实际存放数据的地方共个字节。高速缓存总容量计算高速缓存的总大小Capacity通常指能够存储的纯数据容量不包括有效位和标记位等开销由公式决定字节即总容量 组数每组的行数每块的字节数。图中的硬件地址结构是 CPU 寻址高速缓存Cache的核心机制。一个系统拥有 $ m $ 位主存地址当 CPU 试图从主存中读取一个字时它不会直接把整个地址发送给主存而是将这 $ m $ 位地址划分成三个逻辑字段。地址结构的字段描述块偏移Block Offset位含义用来在选中的高速缓存块Cache Block中定位目标数据的具体字节位置。对应关系由于高速缓存中每个数据块包含字节因此需要地址的低位作为偏移量。例如若字节则需要位可表示偏移量 0, 1, 2, 3。组索引Set Index位含义用来决定该内存地址映射到高速缓存中的哪一个“组”Set。对应关系通用高速缓存被划分成个组。地址中间的这位相当于一个数组的下标告诉硬件应该去组 0 到组中的哪一组去查找数据。标记Tag位含义用来唯一标识映射到同一个高速缓存组的不同内存块。对应关系主存的大小远远大于高速缓存这意味着会有很多不同的主存块映射到同一个高速缓存组中。地址的高位其中会与高速缓存行中的“标记位”进行比对以确定当前缓存行里存的数据到底是不是 CPU 想要的那个内存块。地址结构与高速缓存结构之间的动态联系工作原理当 CPU 携带这个位地址去高速缓存中查找数据时两者的结构通过以下三个步骤产生联系实现“缓存寻址与命中判定”第一步组选择利用组索引位硬件抽取地址中间的位作为索引定位到高速缓存对应的组。结构联系地址的位直接对应高速缓存的个组。由于硬件需要并行访问组索引就像一个多路复用器的选择信号瞬间帮 CPU 在纵向上筛选出具体某一个“组”空间。第二步行匹配利用标记位与有效位进入选定的组后硬件检查该组内的所有个行。对于每一行硬件会同时做两件事检查该行的有效位Valid bit是否为 1。将地址中的位标记与该行硬件结构里自带的位标记字段进行相等性比较。结构联系如果组内某一行同时满足“有效位为1”且“标记完全匹配”这就是一次缓存命中Cache Hit。这说明主存中的那个数据块此时此刻正躺在这一个高速缓存行里。如果找不到满足条件的行就是缓存缺失Cache Miss。第三步字抽取利用块偏移位一旦确定缓存命中硬件就会来到该行对应的数据块Data Block中。结构联系数据块在结构上是一个从到字节的数组。地址底部的位块偏移正是这个数组的下标。硬件根据位的数值精确地从字节的数据块中抽取出 CPU 请求的那个字节或字Word并返回给 CPU。| ** 基本参数** | | | --- | --- | | ** 参数** | ** 描述** | | $ S2^s $ | 组数 | | $ E $ | 每个组的行数 | | $ B2^b $ | 块大小字节 | | $ m\log_2(M) $ | 主存物理地址位数 |衍生出来的量参数描述$ M2^m $内存地址的最大数量$ s\log_2(S) $组索引位数$ b\log_2(B) $块偏移位数$ tm-(sb) $标记位数$ CB \times E \times S $不包括像有效位和标记位这样开销的高速缓存大小字节直接映射高速缓存根据每个组的高速缓存行数 E高速缓存被分为不同的类。每个组只有 1 行E1的高速缓存称为直接映射高速缓存。直接映射高速缓存确定一个请求是否命中然后抽取出被请求的字的过程组选择核心操作CPU 从主存地址中抽取出中间的位组索引。硬件行为将这位组索引视为一个无符号数作为高速缓存组数组的下标直接定位到对应的组在多个组中纵向筛选出目标组。行匹配核心操作在选中的组中检查是否存在一行满足命中条件。判定条件必须同时满足以下两个条件该行的有效位Valid bit必须为 1。该行硬件结构中的标记位Tag必须与地址中的位标记完全相等。结果若同时满足则缓存命中Cache Hit进入字选择若都不满足则为缓存缺失Cache Miss。字选择核心操作在命中的那一行中定位并提取出 CPU 请求的具体字节或字。硬件行为将地址中低位的位块偏移视为字节数组的下标。由于高速缓存块是一个从到字节的数组硬件根据 $b$ 位的数值精准抽取出请求的起始字节并返回给 CPU。行替换核心操作当发生缓存缺失且需要将新数据读入该组时如果该组内已经没有空闲行有效位均为 1就必须腾出空间。策略机制硬件依据替换策略如LRU 最久未使用算法、LFU 最少使用算法在当前组内选择一个“牺牲行”将其驱逐。若该牺牲行是脏数据被修改过需写回主存随后将新读入的内存块写入该行并更新标记位和有效位。注对于直接映射高速缓存由于每组只有一行无需策略直接覆盖即可。冲突不命中核心操作这是一种特殊的缓存缺失现象又称碰撞缺失。发生原理当程序的访问模式导致多个不同的主存块被频繁映射到同一个高速缓存组时即便高速缓存的总空间还非常充裕这些块也会在这个特定的组里交替发生行替换互相将对方驱逐俗称“缓存抖动”抖动Cache Thrashing导致连续访问时持续不命中。:::color4 ⚠️** 为什么用中间的位来做索引**高速缓存之所以选择中间的位组索引 $ s $ 位而不是高位来做索引是为了最大化利用局部性原理减少冲突不命中。:::如果使用“高位”做索引:::danger映射规律高位相同的连续内存块会被映射到同一个缓存组中。致命缺陷程序在运行时通常具有空间局部性即倾向于顺序访问连续的内存块如数组、连续的代码指令。结果如果用高位索引当程序顺序读取0000,0001,0010,0011这四个相邻的块时它们全都会争夺同一个组组 00。这会导致该组发生严重的频繁替换缓存抖动而剩下的组 01、10、11 却完全闲置。:::使用“中间位”做索引:::color1映射规律中间位做索引时相邻的内存块会被均匀、交错地散列到不同的缓存组中如0000到组000001到组010010到组10…。巨大优势当程序具有空间局部性、顺序访问一整块连续内存时这些块会依次存入不同的缓存组。结果4个组都能被同时、均衡地利用起来连续的内存访问绝不会在同一个组内“打架”从而大幅降低了冲突不命中率。:::组相联高速缓存一个$ 1EC/B $的高速缓存通常称为 E 路组相联高速缓存。比如 2 路组相联高速缓存组相联高速缓存确定一个请求是否命中然后抽取出被请求的字的过程1. 组选择机制CPU 抽取主存地址中的位组索引。过程将这位无符号数作为组数组的下标定位到对应的一个高速缓存组。由于它是组相联的这一步在纵向上筛选出包含了个可选行的“目标组”。2. 行匹配和字选择行匹配并行检索过程硬件会同时并行地检查该选定组中的所有个行。命中判定对于任意一行必须同时满足“该行的有效位为 1”且“该行的标记位Tag与地址中的位标记完全一致”。如果找到这样的一行即为缓存命中Cache Hit。字选择数据抽取过程一旦行匹配成功硬件利用地址中的低位块偏移作为字节数组的下标从该行的数据块包含字节中精确抽取出 CPU 请求的字节或字并将其返回。注如果组内没有任何一行的标记能匹配成功或者匹配成功的行有效位为 0则为缓存缺失Cache Miss进入第三阶段。3. 行替换触发条件当发生缓存缺失需要从主存中将包含目标字的数据块加载到当前组时触发。替换逻辑有空闲行如果该组内存在有效位为 0 的空闲行则直接将新块写入该空闲行设置有效位为 1并填入相应的标记。无空闲行需要驱逐如果整个组的个行都满了有效位全为 1硬件必须根据既定的替换策略如LRU 最久未使用算法、LFU 最少使用算法挑选出一个“牺牲行”。写入与更新将牺牲行的数据腾空若该行为脏数据需先写回主存随后把从主存读入的新数据块写入该行并更新其标记位。#### 全相联高速缓存 全相联高速缓存是由一个包含所有高速缓存行的组即$ EC/B $组成的。组相联高速缓存确定一个请求是否命中然后抽取出被请求的字的过程1. 组选择机制由于整个高速缓存结构中只有一个组组 0因此主存地址中不需要专门的组索引位。过程CPU 根本不需要进行任何计算或检索硬件默认且必须总是直接选择组 0。2. 行匹配和字选择行匹配全并行检索过程因为所有的数据块都挤在同一个组里硬件必须同时并行地对组 0 内的所有高速缓存行进行检索。命中条件硬件会逐一比对。必须有一行同时满足有效位必须被设置即有效位 1。高速缓存行中某一行的标记位必须与地址中的位标记位相匹配。字选择数据抽取过程如果上述 1 和 2 的条件同时满足即为缓存命中Cache Hit。抽取硬件接着利用地址底部的位块偏移作为该行数据块的起始字节下标精准地抽取出 CPU 请求的字或字节。注如果遍历完组内所有的行都没有找到“有效”且“标记匹配”的行则判定为缓存缺失Cache Miss随后会触发第三阶段的行替换。#### 高速缓存参数的性能影响 | ** 特征维度** | ** 直接映射高速缓存(Direct-Mapped)** | ** 组相联高速缓存(Set-Associative)** | ** 全相联高速缓存(Fully Associative)** | | --- | --- | --- | --- | | ** 组数 (**$ S $** )** | $ S C / B 1 $ 有多个组 | $ S C / (E \times B) 1 $ 有多个组 | $ S 1 $ 只有一个组 | | ** 每组行数 (**$ E $** )** | $ E 1 $ 每组只有一行 | $ E 2, 4, 8, 16 \dots $ 每组多行 | $ E C / B $ 所有行都在一组内 | | ** 地址中的组索引位 (**$ s $** )** | 有 $ s \log_2(S) $ 位 | 有 $ s \log_2(S) $ 位 | ** 无** $ s 0 $ 位不需要组索引 | | ** 行匹配/检索方式** | 硬件只需根据组索引检查** 唯一的一行** | 硬件必须** 并行同时** 检索选中组内的 ** $E$**** 个行** | 硬件必须** 全并行同时**** 检索整个 Cache 的**** 所有行** | | ** 块替换策略** | 不需要新块直接覆盖旧块 | 需要如 LRU、LFU 等算法 | 需要如 LRU、LFU 等算法 | | ** 硬件成本与复杂度** | ** 极低** 索引定位快不需要复杂的并行比较器 | ** 中等** 需要在组内进行小规模的并行比较 | ** 极高** 需要大量庞大的并行相联比较器 | | ** 冲突不命中率** | ** 最高** 极易发生抖动 | ** 中等/低** 行数越多冲突越少 | ** 最低** 空间利用率最高基本消除了冲突不命中 | | ** 访问速度** | ** 最快** 命中判定结构最简单 | ** 中等** | ** 最慢** 全并行大规模检索延迟较高 | | ** 典型应用场景** | ** 大容量的低级缓存** 如 L3 Cache或对硬件成本/功耗极其敏感的嵌入式处理器中。 | ** 现代 CPU 的主流缓存** 如 L1、L2 Cache。在速度、成本和命中率之间取得了完美的平衡。 | ** 容量极小但要求绝对命中** 的致命核心组件如 ** TLB 快表** 、虚拟内存的页表缓存。 |:::danger一些衡量高速缓存性能的指标不命中率在一个程序执行或程序的一部分执行期间内存引用不命中的比率。$ 不命中数量/引用数量 $。命中率命中的内存引用比率。$ 1-不命中率 $命中时间从高速缓存传送一个字到 CPU 所需的时间包括组选择、行确认和字选择的时间。不命中处罚由于不命中所需要的额外的时间。:::有关写的问题当 CPU 执行写操作修改数据时如何保持高速缓存Cache与低一层存储如主存之间的数据一致性。这个问题需要分写命中和写不命中两种情况来讨论并结合产业界的最优解进行总结1. 写命中Write Hit当 CPU 准备写入一个字且该字所在的块已经缓存在 Cache 中时有两种更新策略直写Write-through机制立即将的副本写回到低一层如主存中。优缺点最简单但缺点极其明显。每次写操作都会引发一次总线事务产生巨大的总线流量拖慢系统速度。写回Write-back机制尽可能地推迟更新。只更新当前 Cache 行中的副本而不立即写回低一层。只有当替换算法判定这一行需要被驱逐替换时才把它写回低一层。硬件开销每一行必须额外维护一个修改位/脏位Dirty bit用来表明这个缓存块是否被修改过。优缺点显著减少了总线流量但增加了硬件实现的复杂性。2. 写不命中Write Miss当 CPU 准备写入一个字但该字所在的块不在 Cache 中时同样有两种处理方式写分配Write-allocate机制先从低一层加载包含的块到 Cache 中然后更新这个缓存块。设计意图试图利用接下来的空间局部性之后可能还会读写这个块内的其他数据。非写分配Not-write-allocate机制避开 Cache直接把这个字写到低一层存储中不把数据块加载到 Cache 里。