为什么需要 cache?

  • 因为 CPU 比 内存的速度要快得多.`

基本思路

  • 使用较小, 较快的 Cache 和相对较大,更为缓慢的 Memory
  • Cache 中包含了 Memory 中数据的副本
  • Cache 位于中央处理器和存储器之间,并可以被集成在 CPU 或者作为主板上的一个模块.

Cache 工作的原理

  • Check :当处理器试图读取内存中的一个字的时候,会先检查该字是否在 Cache 中.
  • Hit : 如果确实在, 这个字被传送给处理器.
  • Miss : 否则,由一定数量的字组成的一块( block )主存中的数据 被读入 Cache ,然后传给处理器.

  • 时间局部性:
    未来将要使用的信息(指令和数据), 可能是现在正在使用的信息.

  • 空间局部性:
    未来将要使用的信息, 很可能与正在使用的信息在存储空间上是邻近的(比如遍历一个一维数组).

判断 Hit 与 Miss

  • 冯诺依曼计算机的设计:
    内存中的内容按位置寻址,而不考虑其中的数据类型.
  • Cache 中有 标记(tag) 来判断需要读取的信息是否存在于 Cache.

移动”块”而不是字

证明 Cache 机制能够提高性能

  • 设命中率为 $p$, $T_C$ 为访问Cache 的时间,$T_M$ 为访问主存的时间,则总时间为(可以认为返回数据不需要时间, 寻找花时间)由上面可以推出,若 $T\frac{T_C}{T_M}$ ,虽然$\frac{T_C}{T_M}$非常小,但是 Cache 的容量更要远远小于主存的容量,这就体现了局部性的作用.

Cache 是否越大越好?

Cache 容量变大, 命中率增加会变得缓慢,因为搬来的数据可能关联性弱,对效果提升十分有限.此外,Cache 规模变大会使得 check 的时间变长.

映射功能

  • 因为 Cache 的行比主存块要少, 所以需要一个算法将主存中的块映射到 Cache 中的行.

1. 直接映射

  • 地址组成:
  • 定义: 内存中的一个块映射到 Cache 中固定的行.

  • 将主存中的每个块映射到固定的可用 Cache 中行.直接映射可以表示为:

    其中 i 为 Cache 行号, j 为主存块号, m 为 Cache 行数 .
    为了实现访问 Cache 每一个主存地址可以看作由三个域组成:

    • 低 $w$ 位标识某个块中的唯一一个存储单元(字节或字).
    • 剩余 $s$ 位标识了主存 $2^s$ 个块中的一个.
      • 其中 $r$ 位标识了 cache 中的行号(cache 的行数为 $m=2^r$)
      • $s-r$ 位为 tag 位.用以区分映射到同一行的不同块.

    总结:

    • 地址长度: $(s+w)$ 位
    • 可寻址单元数: $2^{s+w}$
    • 块大小=行大小=$2^w$ 个存储单元(字节或字)
    • 主存的块数:$\frac{2^{s+w}}{2^w}=2^s$
    • cache 行数:$m=2^r$
    • cache 容量: $2^{r+w}$ 个字或者字节
    • 标记长度: $(s-r)$ 位
  • 举例:
    $m=16K=2^{14},i=j\space mod\space 2^{14}$,用 16 进制 表示地址有

cache 行 主存块的起始地址
0 000000,010000,....FF0000
1 000004,010004,...,FF0004
... ...
21412^{14}-1 00FFFC,01FFFC,...,FFFFFFC

地址24位,其中,高8位为 tag 位,若当前存在该行的标记数与地址中的相同,则14位标识 cache 行号, 低2位标识行中的4个字节(或者字);否则,前22位标识为从主存中取一块.取的主存块的地址为22位加两位0(因为块都是以4倍数开始的,每个块有4个单元)

  • 优点:

    • check快,从主存中存取的时候快
    • tag 短,额外存储少.
    • 不会随着容量增大而变慢
    • 实现简单
  • 缺点:

    • 容易发生抖动:用到的两个块映射到 cache 中的同一行,于是频繁的替换.
    • 命中率低

2. 全相联映射

  • 地址图示:

  • 定义: 内存中的块可以映射到 Cache 中的任意一行.

  • 总结:

    • 地址长度 s+w 位
    • 可寻址单元数 $2^{s+w}$ 个字(字节)
    • 块大小=行大小=$2^w$ 个字(字节)
    • 主存的块数:$\frac{2^{s+w}}{2^w}=2^s$
    • cache 的行数: 不由地址格式决定
    • 标记长度=s 位
  • 优点:

    • 避免”抖动”.
    • 命中率高
  • 缺点:

    • check 慢, 从内存中放到 cache 中慢.
    • 实现较为复杂

3. 组相联映射

  • 图示:
  • 定义: Cache 中的行分成组(Set),内存中的块搬到固定的组,组中具体的哪一行不固定.第一步类似直接映射,第二部类似全相联映射. Cache 中分为 $v$ 个组,每组包含 $k$ 个行,则:其中: i 为组号, j 为主存块号,m 为主存块数,v 为组数,k 为每个组中的行数, 即 k路组(K-way Set).
  • 总结:
    • 地址长度 s+w 位
    • 可寻址单元数 $2^{s+w}$ 个字(字节)
    • 块大小=行大小=$2^w$ 个字(字节)
    • 主存的块数:$\frac{2^{s+w}}{2^w}=2^s$
    • cache 中每组行数= k
    • 组数 = v = $2^d$
    • cache 中的行数 = m = $k\times v=k\times 2^d$
    • 标记长度 s-d 位(同一个组中不会出现标记位相同的两个块)
  • 优点:

    • 固定组 check ,存取,速度比全相联快
    • 命中率比直接映射高
  • 缺点:

    • 实现复杂.
    • 若反复用的的块很多的话,仍然无法避免抖动.

替换算法

1.最近最少用

2.先进先出

3.使用最少

4.随机

写策略

1.写入

  • 每一次改了之后都更新内存
    • 好处:提升cache和主存一致性,保证主存中的内容都是最新的

2.写回

  • cache 中的行要被替换时不得不写回去,用 dirty bit 来判断数据是否发生过改写,若没有改过无需写回.

块大小(Cache Line Size)

  • 由极小变大,根据局部性原理命中率先是提升,因为每个块所能容纳的有用数据增多了.
  • 到极大时,且新取信息的概率小于重用信息概率时,命中率会减小.因为较大的块减少了块的个数,少量的块导致装入的数据很快会被改写;当块变大时,每个附加字距离所需字就更远,被使用的概率低.

cache 数目

  • 多级 cache 比单一大容量 cache 效率更高