Skip to content

Cache

思维导图

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 路以上

  • 当每组只有一行时,则为直接映射

  • 当只有一组时,则为全相联映射