Fork me on GitHub
0%

Cache原理

概念

局部性原理

在大多数程序运行过程中,CPU对于内存的访问会表现出明显的倾向性,具体体现在两个方面:

  • 时间局部性:时间维度看,被引用过的内存位置,不远的将来将大概率再次被访问
  • 空间局部性:空间维度看,被引用过的内存位置,不远的将来它附近的内存位置将大概率被访问

缓存块

linux查看缓存信息命令:getconf -a | grep CACHE

概念

根据cache的作用可知cache当中存储的是主存的副本,其必须是要和主存进行数据交换的。基于效率角度考量,根据程序局部性原理,CPU是不会按照一个字节一个字节进行数据的加载至缓存中,而是按块进行加载,而这每一块就叫做cache line,也称缓存块

  • 缓存块(cache line),管理缓存结构的最小存储单元,目前常用的是64Bytes(内存系统支持Burst读/写效率更高)

结构

image

  • 标识位V:表示该缓存块是否有效
  • 标识位M:表示这个缓存块是否被修改,也就是脏位
  • Tag:和内存块匹配的标识
  • Data:缓存数据内容

需要注意的是 V、M、Tag这几位是由硬件门电路实现的,并不占缓存空间。而Data才是真正的数据部分

缓存命中/缺失

  • Cache Hit:访存时,如果所需要的数据在cache中则成为缓存命中
  • Cache Miss:访存时,如果所需要的数据不在cache中则成为缓存缺失

缓存映射方式

视频链接:click here

从概念上讲,高速缓存是一个相联存储器(Associative Memory)。硬件电路实现方式及映射策略的不同,缓存的映射方式分为三种:

  • 全相连映射:主存块可以映射到任一缓存行(缓存只有一个组,所有的内存块都放在这一个组的不同路上)
  • 直接相连映射:主存块只能映射到固定的缓存行(缓存只有一个路,一个内存块只能放置在特定的组上)
  • 组组相连映射:主存块只能映射到固定的缓存组,组内任一缓存行(缓存同时由多个组和多个路)

片外的高速缓存通常是直接映射的,因为组相联需要更宽的接口,可以取得多个标签,并发匹配

特点

组织方式 特点 应用场景
全相联映射 cache利用率高
块冲突低
淘汰算法复杂
小容量cache
直接相连映射 cache利用率低
块冲突高
淘汰算法简单
大容量cache
组相联映射 cache利用率较高
块冲突率较低
硬件实现复杂
中等容量cache

映射算法

  • 全相联:cache行号 = random
  • 直接相联:cache行号 = 内存块号 % cache行数
  • 组相联:主存的数据块映射到Cache特定组的任意行,cache组号=主存块号 % cache组数

缓存块替换策略

  • 随机法(RAND)

    方法简单、易于实现,但命中率比较低。

  • 先进先出算法(FIFO)

    先进先出方法易于实现,命中率比随机法好些。

  • 最近最久未使用算法(LRU,Least Recently Used)

    可以比较好地遵循程序局部性原理

缓存写策略

write back 写回

将要写入的数据保留在高速缓存中,并进行标记(Dirty / Modified)。只有缓存块被替换的时候才会将数据写入主存

image

write through 写透/写穿/写直达

CPU总是将数据直接写到主内存中

note:如果等待写内存完成处理器的性能会急剧下降,通过写缓冲(Write Buffer)硬件可以弥补这一问题。将需要写入的数据存储在一个FIFO队列中(Write Buffer),由内存控制器进行写入操作

image

其它

由于效率原因,写更新和写不分配这两种策略在现实中比较少出现

  1. CPU 之间的更新策略

    当某个 CPU 的缓存中执行写操作,修改其中的某个值时,其他 CPU 的缓存所保有该数据副本的更新策略分为:写更新(Write Update)和写无效(Write Invalidate)。

  2. 写缓存时数据是否被加载

    当前要写入的数据不在缓存中时,根据是否要先将数据加载到缓存中,写策略分为:写分配(Write Allocate)和写不分配(Not Write Allocate)。

缓存效率

衡量标准

两个参数的乘积:

  1. 高速缓存缺失率:cache miss / 被执行的指令次数
  2. 高速缓存缺失重填的开销:cpu流水线重新计算从主存取数到缓存中的时间

cache miss的原因?

《see mips run》对于以下缺失类型分别称为:第一次访问;抖动;替换

  1. 强制缺失。第一次将数据块读入到缓存所产生的缺失,也被称为冷缺失(cold miss),因为当发生缓存缺失时,缓存是空的(冷的);
  2. 冲突缺失。由于缓存的相连度有限导致的缺失(指在同一组内的缺失);
  3. 容量缺失。由于缓存大小有限导致的缺失(描述范围是整个缓存)

如何提高利用率?

  • 软件
    1. 程序更小;
    2. 让程序经常执行的部分更小。对于一个程序错误处理/初始化代码等部分代码剥离出来,提高剩余程序的缓存命中率;
    3. 安排程序尽量不发生冲突缺失(抖动)
    4. 直到必须用到数据时才停止CPU。非阻塞读。取数操作执行完,继续执行不依赖该数据的代码(流水线乱序/分支指令等等)
    5. 多线程CPU
  • 硬件
    1. (1)让高速缓存变的更大(代价)
    2. (1)增加缓存相连度(4路以上效果就不明显了,更高路相连缓存设计目的在于其它原因(降低功耗,不使用时关掉整路))
    3. (1)增加额外层次的高速缓存(L2 L3 cache)
    4. (2)增加带宽,cpu与主存物理位置更近(代价)
    5. (2)增加内存burst带宽(现在很少使用)
    6. (2)尽早地重启CPU
    7. (2)关键字优先

管理缓存

对于MIPS CPU,synci指令做了以下所有工作(用户特权级)

上电之后CPU的高速缓存阵列内容通常是随机的。引导软件负责初始化高速缓存,系统启动并正常运行后,只有三种情况CPU必须进行干预:

  1. DMA设备从内存取数据之前。如果是Write Back式写策略,需要将在高速缓存中未写入到主存的数据写入得到正确数据
  2. DMA设备写数据到内存之前。需要将高速缓存中对应的缓存行的标志位置Invalid(无效的)
  3. 写指令。当cpu自身写一部分指令到内存用于稍后执行时,首先需要保证指令写入到内存中,其次保证 I-Cache 中对应的指令无效

视频推荐:

Cache地址映射与变换方法

一张图解决主存和cache的映射问题