本文已收录到 AndroidFamily,技术和职场问题,请重视大众号 [彭旭锐] 发问。

前言

大家好,我是小彭。

在上一篇文章里,咱们聊到了计算机存储器体系的金字塔结构,其间在 CPU 和内存之间有一层高速缓存,便是咱们今天要聊的 CPU 三级缓存。

那么,CPU Cache 的结构是怎样的,背后隐含着哪些规划思维,CPU Cache 和内存数据是怎么相关起来的,今天咱们将环绕这些问题打开。


思维导图:

我把 CPU 三级缓存的秘密,藏在这 8 张图里


1. 知道 CPU 高速缓存

1.1 存储器的金字塔结构

现代计算机体系为了寻求容量、速度和价格最大的性价比会选用分层架构,从 “CPU 寄存器 – CPU 高速缓存 – 内存 – 硬盘”自上而下容量逐步增大,速度逐步减慢,单位价格也逐步下降。

  • 1、CPU 寄存器: 存储 CPU 正在运用的数据或指令;
  • 2、CPU 高速缓存: 存储 CPU 近期要用到的数据和指令;
  • 3、内存: 存储正在运转或许将要运转的程序和数据;
  • 4、硬盘: 存储暂时不运用或许不能直接运用的程序和数据。

存储器金字塔

我把 CPU 三级缓存的秘密,藏在这 8 张图里

1.2 为什么在 CPU 和内存之间添加高速缓存?

我以为有 2 个原因:

  • 原因 1 – 弥补 CPU 和内存的速度差(首要): 因为 CPU 和内存的速度差距太大,为了拉平两者的速度差,现代计算机会在两者之间刺进一块速度比内存更快的高速缓存。只要将近期 CPU 要用的信息调入缓存,CPU 便可以直接从缓存中获取信息,然后进步拜访速度;

  • 原因 2 – 削减 CPU 与 I/O 设备争抢访存: 因为 CPU 和 I/O 设备会竞赛同一条内存总线,有或许呈现 CPU 等候 I/O 设备访存的状况。而假如 CPU 能直接从缓存中获取数据,就可以削减竞赛,进步 CPU 的效率。

1.3 CPU 的三级缓存结构

在 CPU Cache 的概念刚呈现时,CPU 和内存之间只要一个缓存,随着芯片集成密度的进步,现代的 CPU Cache 已经普遍选用 L1/L2/L3 多级缓存的结构来改善功能。自顶向下容量逐步增大,拜访速度也逐步下降。当缓存未命中时,缓存体系会向更底层的层次搜索。

  • L1 Cache: 在 CPU 中心内部,分为指令缓存和数据缓存,分隔寄存 CPU 运用的指令和数据;
  • L2 Cache: 在 CPU 中心内部,尺度比 L1 更大;
  • L3 Cache: 在 CPU 中心外部,所有 CPU 中心同享同一个 L3 缓存。

CPU 三级缓存

我把 CPU 三级缓存的秘密,藏在这 8 张图里


2. 了解 CPU 三级缓存的规划思维

2.1 为什么 L1 要将指令缓存和数据缓存分隔?

这个战略叫别离缓存,与之相对应的叫一致缓存:

  • 别离缓存: 指令和数据分别寄存在不同缓存中:
    • 指令缓存(Instruction Cache,I-Cache)
    • 数据缓存(Data Cache,D-Cache)
  • 一致缓存: 指令和数据一致寄存在一个缓存中。

那么,为什么 L1 缓存要把指令和数据分隔呢?我以为有 2 个原因:

  • 原因 1 – 避免取指令单元和取数据单元争夺访缓存(首要): 在 CPU 内核中,取指令和取数据指令是由两个不同的单元完成的。假如运用一致缓存,当 CPU 运用超前操控或流水线操控(并行执行)的操控办法时,会存在取指令操作和取数据操作一起争用同一个缓存的状况,下降 CPU 运转效率;

  • 原因 2 – 内存中数据和指令是相对聚簇的,别离缓存能进步命中率: 在现代计算机体系中,内存中的指令和数据并不是随机散布的,而是相对聚集地分隔存储的。因而,CPU Cache 中也选用别离缓存的战略更契合内存数据的现状;

2.2 为什么 L1 选用别离缓存而 L2 选用一致缓存?

我以为原因有 2 个:

  • 原因 1: L1 选用别离缓存后已经处理了取指令单元和取数据单元的争夺访缓存问题,所以 L2 是否运用别离缓存没有影响;

  • 原因 2: 当缓存容量较大时,别离缓存无法动态调理别离份额,不能最大化发挥缓存容量的运用率。例如数据缓存满了,但是指令缓存还有闲暇,而 L2 运用一致缓存则可以确保最大化运用缓存空间。

2.3 L3 缓存是多中心同享的,放在芯片外有区别吗?

集成在芯片内部的缓存称为片内缓存,集成在芯片外部(主板)的缓存称为片外缓存。开始,因为受到芯片集成工艺的约束,片内缓存不或许很大,因而 L2 / L3 缓存都是规划在主板上,而不是在芯片内的。

后来,L2 / L3 才逐步集成到 CPU 芯片内部后的。片内缓冲和片外缓存是有区别的,首要体现在 2 个方面:

  • 区别 1 – 片内缓存物理间隔更短: 片内缓存与取指令单元和取数据单元的物理间隔更短,速度更快;

  • 区别 2 – 片内缓存不占用体系总线: 片内缓存运用独立的 CPU 片内总线,可以减轻体系总线的负担。


3. CPU Cache 的根本单位 —— Cache Line

CPU Cache 在读取内存数据时,每次不会只读一个字或一个字节,而是一块块地读取,这每一小块数据也叫 CPU 缓存行(CPU Cache Line)。这也是对局部性原理的运用,当一个指令或数据被拜访过之后,与它相邻地址的数据有很大概率也会被拜访,将更多或许被拜访的数据存入缓存,可以进步缓存命中率。

当然,块长也不是越大越好(一般是取 4 到 8 个字长,即 64 位):

  • 前期:当块长由小到大增长时,随着局部性原理的影响使得命中率逐步进步;
  • 后期:但随着块长继续增大,导致缓存中承载的块个数削减,很或许内存块刚刚装入缓存就被新的内存块覆盖,命中率反而下降。并且,块长越长,追加的部分间隔被拜访的字越远,近期被拜访的或许性也更低,无济于事。

区分几种容量单位:

  • 字节(Byte): 字节是计算机数据存储的根本单位,即使存储 1 个位也需求按 1 个字节存储;
  • 字(Word): 字长是 CPU 在单位时刻内可以一起处理的二进制数据位数。多少位 CPU 便是指 CPU 的字长是多少位(比如 64 位 CPU 的字长便是 64 位);
  • 块(Block): 块是 CPU Cache 办理数据的根本单位,也叫 CPU 缓存行;
  • 段(Segmentation)/ 页(Page): 段 / 页是操作体系办理虚拟内存的根本单位。

事实上,CPU 在拜访内存数据的时分,与计算机中对于 “缓存规划” 的一般性规则是相同的: 对于基于 Cache 的体系,对数据的读取和写入总会先拜访 Cache,查看要拜访的数据是否在 Cache 中。假如命中则直接运用 Cache 上的数据,不然先将底层的数据源加载到 Cache 中,再从 Cache 读取数据。

我把 CPU 三级缓存的秘密,藏在这 8 张图里

那么,CPU 怎么知道要拜访的内存数据是否在 CPU Cache 中,在 CPU 中的哪个方位,以及是不是有用的呢?这便是下一节要讲的内存地址与 Cache 地址的映射问题。


4. 内存地址与 Cache 地址的映射

无论对 Cache 数据查看、读取仍是写入,CPU 都需求知道拜访的内存数据对应于 Cache 上的哪个方位,这便是内存地址与 Cache 地址的映射问题。

事实上,因为内存块和缓存块的巨细是相同的,所以在映射的进程中,咱们只需求考虑 “内存块索引 – 缓存块索引” 之间的映射联系,而具体拜访的是块内的哪一个字,则运用相同的偏移在块中寻找。举个例子:假设内存有 32 个内存块,CPU Cache 有 8 个缓存块,咱们只需求考虑 紫色 部分标识的索引怎么匹配即可。

目前,首要有 3 种映射计划:

  • 1、直接映射(Direct Mapped Cache): 固定的映射联系;
  • 2、全相联映射(Fully Associative Cache): 灵活的映射联系;
  • 3、组相联映射(N-way Set Associative Cache): 前两种计划的折中办法。

内存块索引 - 缓存块索

我把 CPU 三级缓存的秘密,藏在这 8 张图里

4.1 直接映射

直接映射是三种办法中最简略的映射办法,直接映射的战略是: 在内存块和缓存块之间建立起固定的映射联系,一个内存块总是映射到同一个缓存块上。

具体办法:

  • 1、将内存块索引对 Cache 块个数取模,得到固定的映射方位。例如 13 号内存块映射的方位便是 13 % 8 = 5,对应 5 号 Cache 块;

  • 2、因为取模后多个内存块会映射到同一个缓存块上,产生块冲突,所以需求在 Cache 块上添加一个 组符号(TAG),符号当时缓存块存储的是哪一个内存块的数据。其实,组符号便是内存块索引的高位,而 Cache 块索引便是内存块索引的低 4 位(8 个字块需求 4 位);

  • 3、因为初始状况 Cache 块中的数据是空的,也是无效的。为了标识 Cache 块中的数据是否已经从内存中读取,需求在 Cache 块上添加一个 有用位(Valid bit) 。假如有用位为 0,则 CPU 可以直接读取 Cache 块上的内容,不然需求先从内存读取内存块填入 Cache 块,再将有用位改为 1。

直接映射

我把 CPU 三级缓存的秘密,藏在这 8 张图里

4.2 全相联映射

了解了直接映射的办法后,咱们发现直接映射存在 2 个问题:

  • 问题 1 – 缓存运用不充分: 每个内存块只能映射到固定的方位上,即使 Cache 上有闲暇方位也不会运用;
  • 问题 2 – 块冲突率高: 直接映射会频频呈现块冲突,影响缓存命中率。

基于直接映射的缺陷,全相联映射的战略是: 允许内存块映射到任何一个 Cache 块上。 这种办法可以充分运用 Cache 的空间,块冲突率也更低,但是所需求的电路结构物更复杂,成本更高。

具体办法:

  • 1、当 Cache 块上有闲暇方位时,运用闲暇方位;
  • 2、当 Cache 被占满时则替换出一个旧的块腾出闲暇方位;
  • 3、因为一个 Cache 块会映射所有内存块,因而组符号 TAG 需求扩大到与主内存块索引相同的位数,并且映射的进程需求沿着 Cache 从头到尾匹配 Cache 块的 TAG 符号。

全相联映射

我把 CPU 三级缓存的秘密,藏在这 8 张图里

4.3 组相联映射

组相联映射是直接映射和全相联映射的折中计划,组相联映射的战略是: 将 Cache 分为多组,每个内存块固定映射到一个分组中,又允许映射到组内的任意 Cache 块。明显,组相联的分组为 1 时就等于全相联映射,而分组等于 Cache 块个数时就等于直接映射。

组相联映射

我把 CPU 三级缓存的秘密,藏在这 8 张图里


5. Cache 块的替换战略

在运用直接映射的 Cache 中,因为每个主内存块都与某个 Cache 块有直接映射联系,因而不存在替换战略。而运用全相联映射或组相联映射的 Cache 中,主内存块与 Cache 块没有固定的映射联系,当新的内存块需求加载到 Cache 中时(且 Cache 块没有闲暇方位),则需求替换到 Cache 块上的数据。此刻就存在替换战略的问题。

常见替换战略:

  • 1、随机法: 运用一个随机数生成器随机地选择要被替换的 Cache 块,实现简略,缺陷是没有运用 “局部性原理”,无法进步缓存命中率;

  • 2、FIFO 先进先出法: 记载各个 Cache 块的加载事件,最早调入的块最早被替换,缺陷同样是没有运用 “局部性原理”,无法进步缓存命中率;

  • 3、LRU 最近最少运用法: 记载各个 Cache 块的运用状况,最近最少运用的块最早被替换。这种办法相对比较复杂,也有类似的简化办法,即记载各个块最近一次运用时刻,最久未拜访的最早被替换。与前 2 种战略比较,LRU 战略运用了 “局部性原理”,均匀缓存命中率更高。


6. 总结

  • 1、为了弥补 CPU 和内存的速度差和削减 CPU 与 I/O 设备争抢访存,计算机在 CPU 和内存之间添加高速缓存,一般存在 L1/L2/L3 多级缓存的结构;

  • 2、对于基于 Cache 的存储体系,对数据的读取和写入总会先拜访 Cache,查看要拜访的数据是否在 Cache 中。假如命中则直接运用 Cache 上的数据,不然先将底层的数据源加载到 Cache 中,再从 Cache 读取数据;

  • 3、CPU Cache 是一块块地从内存读取数据,一块数据便是缓存行;

  • 4、内存地址与 Cache 地址的映射有直接映射、全相联映射和组相联映射;

  • 5、运用全相联映射或组相联映射的 Cache 中,当新的内存块需求加载到 Cache 中时且 Cache 块没有闲暇方位,则需求替换到 Cache 块上的数据。

今天,咱们首要评论了 CPU Cache 的根本规划思维以及 Cache 与内存的映射联系。具体 CPU Cache 是怎么读取和写入的还没讲,别的有一个 CPU 缓存一致性问题 说的又是什么呢?下一篇文章咱们具体打开评论,请重视。


参考资料

  • 浅显易懂计算机组成原理(第 37、38 讲) —— 徐文浩 著,极客时刻 出品
  • 计算机组成原理教程(第 7 章) —— 尹艳辉 王海文 邢军 著
  • 面试官:怎么写出让 CPU 跑得更快的代码? —— 小林 Coding 著
  • CPU cache —— Wikipedia
  • CPU caches —— LWN.net

我把 CPU 三级缓存的秘密,藏在这 8 张图里