共计 8289 个字符,预计需要花费 21 分钟才能阅读完成。
[toc]
1. 什么是缓存
缓存又叫高速缓存,是计算机存储器中的一种,实质上和硬盘是一样的,都是用来 <font color=#0000FF size=3> 存储数据和指令的 </font>。它们最大的区别在于 <font color=#0000FF size=3> 读取速度的不同。</font> 程序个别是放在内存中的,当 CPU 执行程序的时候,执行完一条指令须要从内存中读取下一条指令,读取内存中的指令要花费 100000 个时钟周期(缓存读取速度为 200 个时钟周期,相差 500 倍),如果每次都从内存中取指令,CPU 运行时将破费大量的工夫在读取指令上。这显然是一种资源节约。
如何解决这个问题呢?有人必定会问,<font color=#0000FF size=3> 间接把程序存储在缓存中不行吗?</font>
答案是能够的。然而,缓存的造价太贵了。具体如下图所示。以 2015 年的售价为例,1GB SRAM 的价格大概为 327680 美元,而 1GB 一般硬盘的价格仅仅为 0.03 美元。用缓存来存储程序老本太高了,得失相当。
于是,有人就提出了这样一种办法,<font color=#0000FF size=3> 在 CPU 和内存之间增加一个高速内存,</font> 这个高速内存容量小,只用来存储 CPU 执行时罕用的指令。既保证了硬件老本,又进步了 CPU 的访问速度。这个高速内存就是缓存(高速缓存)。
2. 缓存的定义
高速缓存是一个小而疾速的 <font color=#0000FF size=3> 存储设备 </font>,它作为存储在更大更慢的设 备中的数据对象的缓冲区域。<font color=#0000FF size=3> 应用高速缓存的过程称为缓存 </font>。
具体如下图所示,主存能够作为一个存储设备,L3 是主存的缓冲区域,从 L3 存取数据的过程就叫做缓存。
3. 计算机中的高速缓存
3.1 高速缓存相干名词
如下图所示,数据总是以 <font color=#0000FF size=3> 块为单位 </font> 在高速缓存和主存之间来回复制。
如果咱们的程序申请一个数据字,这个数据字存储在编号为 10 的块中。将分以下几种状况思考:
1. 高速缓存行中为空,这叫做 <font color=#0000FF size=3> 冷不命中 </font>。
2. 高速缓存中有数据块,但没有数据块 10,这叫做 <font color=#0000FF size=3> 缓存不命中 </font>。接下来缓存申请主存将该块复制到高速缓存,高速缓存接管到之后将替换一个现有的数据块,从而存储新的数据块在高速缓存中。最初,高速缓存将数据块 10 返回给 CPU。
3. 高速缓存中有数据,将内存中的数据块搁置到高速缓存中时,产生了抵触,这叫做 <font color=#0000FF size=3> 抵触不命中 </font>。
搁置策略中最罕用的是:第 k + 1 层的块 i 必须放在第 k 层的块(i mod 4)中。比方,第 k + 1 层的 0,4,8,12 会映射到第 k 层的块 0。块 1,5,9,13 会映射到块 1。
4. 缓存中有数据块 10,则间接返回给 CPU。这叫做 <font color=#0000FF size=3> 缓存命中 </font>。
3.2 计算机中的高速缓存存储器模型
高速缓存齐全由硬件治理,硬件逻辑必须要晓得,如何查找缓存中的块,并确定是否蕴含特定块。因而,必须以十分严格且简略的形式去构建高速缓存。在计算机中,高速缓存模型如下图所示。
咱们能够将高速缓存存储器视为有 $S = {2^s}$ 个高速缓存组的 <font color=#0000FF size=3> 数组 </font>。每个组蕴含 $E = {2^e}$ 个 <font color=#0000FF size=3> 高速缓存行 </font>。每个行是由一个 $B = {2^b}$ 字节的数据块组成的。
一般而言,高速缓存的构造能够用元组(S,E,B,m)来形容。高速缓存的大小(或容量)C 指的是所有块的大小的和。<font color=#0000FF size=3> 标记位和无效位不包含在内 </font>。因而,C=S×E×B。
每个高速缓存存储器有 m 位,能够组成 $M = {2^m}$ 个不同的地址,$m = t + s + b$。每个数据块由以下三局部形成。
无效位 :无效位为 t 位,t 个别为 1,指明这个行是否蕴含无效信息。
标记位 :标记位为 s 位。惟一的标识了存储在高速缓存中的块(数组索引)。
块偏移 :数据块为 $B = {2^b}$ 字节。指明 CPU 申请的内容在数据块中的偏移。
上面对以上内容呈现的参数做个总结:
参数 | 形容 |
---|---|
$S = {2^s}$ | 组数 |
$E$ | 每个组的行数 |
$B = {2^b}$ | 块大小(字节) |
$m = {\log _2}(M)$ | 物理地址位数 |
$M = {2^m}$ | 内存地址的最大数量 |
$s = {\log _2}(S)$ | 组索引位数量 |
$b = {\log _2}(B)$ | 块偏移位数量 |
$t = m – (s + b)$ | 标记位数量 |
$C = B \times E \times S$ | 不包含像无效位和标记位这样开销的高速缓存大小(字节) |
3.3 计算机中有哪些缓存
下表为古代计算机中用到的各种缓存。
类型 | 缓存什么 | 被缓存在何处 | 提早(周期数) | 由谁治理 |
---|---|---|---|---|
CPU 寄存器 | 4 字节或 8 字节 | 芯片上的 CPU 寄存器 | 0 | 编译器 |
TLB | 地址翻译 | 芯片上的 TLB | 0 | 硬件 MMU |
L1 高速缓存 | 64 字节块 | 芯片上的 L1 高速缓存 | 4 | 硬件 |
L2 高速缓存 | 64 字节块 | 芯片上的 L2 高速缓存 | 10 | 硬件 |
L3 高速缓存 | 64 字节块 | 芯片上的 L3 高速缓存 | 50 | 硬件 |
虚拟内存 | 4KB 页 | 主存 | 200 | 硬件 |
缓冲区缓存 | 局部文件 | 主存 | 200 | OS |
磁盘缓存 | 磁盘扇区 | 磁盘控制器 | 100000 | 控制器固件 |
网络缓存 | 局部文件 | 本地磁盘 | 10000000 | NFS 客户 |
浏览器缓存 | Web 页 | 本地磁盘 | 10000000 | Web 浏览器 |
Web 缓存 | Web 页 | 近程服务器磁盘 | 1000000000 | Web 代理服务器 |
3.4 硬件读取高速缓存的过程
当一条加载指令批示 CPU 从主存地址 A 中读取一个字 w 时,会将该主存地址 A 发送到高速缓存中,则高速缓存会依据以下步骤判断地址 A 是否命中:
组抉择 :依据地址划分,将两头的 s 位示意为无符号数作为 <font color=#0000FF size=3> 组的索引 </font>,可失去该地址对应的组。
行匹配 :依据地址划分,可失去 t 位的标记位,因为组内的任意一行都能够蕴含任意映射到该组的数据块,所以就要线性搜寻组中的每一行,<font color=#0000FF size=3> 判断是否有和标记位匹配且设置了无效位的行 </font>,如果存在,则缓存命中,否则缓冲不命中。
字抽取 :如果找到了对应的高速缓存行,则能够将 b 位示意为无符号数作为 <font color=#0000FF size=3> 块偏移量 </font>,失去对应地位的字。
当高速缓存命中时,会很快抽取出字 w,并将其返回给 CPU。如果缓存不命中,CPU 会进行期待,高速缓存会向主存申请蕴含字 w 的数据块,当申请的块从主存达到时,高速缓存会将这个块保留到它的一个高速缓存行中,而后从被存储的块中抽取出字 w,将其返回给 CPU。
4. 间接映射高速缓存
下面咱们介绍了计算机中的高速缓存模型,咱们能够依据每个组的高速缓存行数 E,将高速缓存分成不同的类型。上面咱们看下间接映射高速缓存(E=1)的具体例子。
4.1 组抉择
组抉择示意图如下所示。假如有 S 组,每组由一行组成,缓存块为 8 字节。CPU 收回地址要取数据字,高速缓存将该地址合成为三局部,对于图中的地址来说,<font color=#FF4500 size=3> 块偏移量为 4。组索引是 1,粉红色的为 t 位标记位。</font> 因而,高速缓存提取的组索引为 1,即图中第二行。
4.2 行匹配
而后,查看地址中的标记位与缓存行中的标记位是否匹配。如果匹配,将进行下一步字抉择。如果不匹配,则示意未命中。在未命中时,<font color=#0000FF size=3> 高速缓存必须从内存中从新取数据块,</font> 在行中笼罩此块。
4.3 字抉择
当标记位匹配时,示意命中,接着查看地址中的块偏移为 4,即要从缓存行数据块的第 5 位开始取值,并返回给 CPU。
4.4 模仿间接映射缓存
上面,咱们模仿下间接映射高速缓存的过程,以便加深了解高速缓存是如何工作的。假如,内存地址为 <font color=#FF4500 size=3>4 字节,S= 4 组,E= 1 行 / 组,B= 2 字节 / 块。</font> 其结构图如下所示。
咱们模仿 CPU 要从高速缓存中读取地址为 0,1,7,8,0 的数据。上面是具体的过程。
地址 | 二进制 | 是否命中 |
---|---|---|
0 | [${0000_2}$](t=0,s=00,b=0) | |
1 | [${0001_2}$](t=0,s=00,b=1) | |
7 | [${0111_2}$](t=0,s=11,b=1) | |
8 | [${1000_2}$](t=1,s=00,b=0) | |
0 | [${0000_2}$](t=00,s=0,b=0) |
1. 读地址 0 的数据。<font color=#FF4500 size=3> 标记位为 0,索引位为 00,偏移位为 0,块号为 0。</font> 缓存行中没有数据,组 0 的无效位为 0,地址的标记位和组 0 的标记位不匹配,因而,未命中。而后,高速缓存从内存中取出块 0,块 1,共 2 字节,并存储在组 0 中。具体如下图所示。
2. 读地址 1 的数据。<font color=#FF4500 size=3> 标记位为 0,索引位为 00,偏移位为 1,块号 1。</font> 缓存行中已有数据数据,组 0 的无效位为 1,地址 1 的标记位和组 0 的标记位匹配,因而,命中。具体如下图所示。
3. 读地址 7 的数据。<font color=#FF4500 size=3> 标记位为 0,索引位为 11(3),偏移位为 1,块号为 3。</font> 缓存行中有数据,组 3 的无效位为 0,地址的标记位和组 0 的标记位不匹配,因而,未命中。而后,高速缓存从内存中取出块 6,块 7,共 2 字节,并存储在组 3 中。具体如下图所示。
4. 读地址 8 的数据。<font color=#FF4500 size=3> 标记位为 1,索引位为 00,偏移位为 0,块号为 4。</font> 缓存行中有数据,组 0 的无效位为 1,地址的标记位和组 0 的标记位不匹配,因而,未命中。而后,高速缓存从内存中取出块 8,块 9,共 2 字节,并存储在组 0 中。具体如下图所示。
5. 读地址 0 的数据。<font color=#FF4500 size=3> 标记位为 0,索引位为 00,偏移位为 0,块号为 0。</font> 缓存行中有数据,组 0 的无效位为 1,地址的标记位和组 0 的标记位不匹配,因而,未命中。而后,高速缓存从内存中取出块 0,块 1,共 2 字节,并存储在组 0 中。具体如下图所示。
最终后果如下:缓存命中率为 20%。
地址 | 二进制 | 是否命中 |
---|---|---|
0 | [${0000_2}$](t=0,s=00,b=0) | 否 |
1 | [${0001_2}$](t=0,s=00,b=1) | 是 |
7 | [${0111_2}$](t=0,s=11,b=1) | 否 |
8 | [${1000_2}$](t=1,s=00,b=0) | 否 |
0 | [${0000_2}$](t=00,s=0,b=0) | 否 |
留神:块大小为 2 字节,所以从内存中取数据总是以偶数倍开始的,所以会看到 M[8-9], 而不是 M[7-8]。
如果你看懂了上述高速缓存的整个过程,思考下 <font color=red size=3> 如何编程来模仿高速缓存呢?</font> 前面的文章我会具体解说如何用 C 语言模仿高速缓存,欢送关注我的公众号【嵌入式与 Linux 那些事】,第一工夫获取更新。
4.5 间接映射高速缓存的缺点
察看以上过程其实能够发现,在第 5 步,读地址 0 的数据的时候,咱们又得 <font color=#0000FF size=3> 从新从内存中取数据到缓存行中。</font> 在读地址 8 的数据的时候,M[8-9] 替换了缓存行中的 M[0-1]。
<font color=#0000FF size=3> 最次要的起因是每一个组中只容许寄存一行缓存。</font> 假如,E = 2,每组中有 2 个缓存行,M[8-9] 和 M[0-1] 就有很大可能同时存在于组 0 中。咱们在第 5 步拜访时,就不须要从新从内存中取数据了。因而,就有了 E = 2 的两路相联高速缓存。
5. 两路相联高速缓存
间接映射高速缓存中抵触不命中造成的问题源于每个组只有一行这个限度。组相联高速存放松了这条限度,所以每个组都保留有多于一个的高速缓存行。如下图所示为两路相联的高速缓存。
5.1 组抉择
它的组抉择与间接映射高速缓存的组抉择一样,组索引位标识组。具体如下图所示,这里不再赘述。
5.2 行匹配
组相联高速缓存中的行匹配比间接映射高速缓存中的更简单,因为它必须每次查看 <font color=#0000FF size=3> 多个行 </font> 的标记位和无效位,以确定所申请的字是否在汇合中。具体如下图所示。
5.3 字抉择
字抉择的过程和间接映射高速缓存中的形式一样,这里就不再赘述。
5.4 模仿两路相联高速缓存
上面,咱们模仿下两路相联高速缓存的过程,以便加深了解高速缓存是如何工作的。假如,内存地址为 4 字节,S= 2 组,E= 2 行 / 组,B= 2 字节 / 块。其结构图如下所示。
咱们模仿 CPU 要从高速缓存中读取地址为 0,1,7,8,0 的数据。上面是具体的过程。
地址 | 二进制 | 是否命中 |
---|---|---|
0 | [${0000_2}$](t=00,s=0,b=0) | |
1 | [${0001_2}$](t=00,s=0,b=1) | |
7 | [${0111_2}$](t=01,s=1,b=1) | |
8 | [${1000_2}$](t=10,s=0,b=0) | |
0 | [${0000_2}$](t=00,s=0,b=0) |
1. 读地址 0 的数据。<font color=#FF4500 size=3> 标记位为 00,索引位为 0,偏移位为 0,块号为 0。</font> 缓存行中没有数据,组 0 的无效位为 0,地址的标记位和组 0 的第一行和第二行的标记位都不匹配,因而,未命中。而后,高速缓存从内存中取出块 0,块 1,共 2 字节,并存储在组 0 第一行中。具体如下图所示。
2. 读地址 1 的数据。<font color=#FF4500 size=3> 标记位为 00,索引位为 0,偏移位为 1,块号为 1。</font> 缓存行中已有数据数据,组 0 的第一行无效位为 1,地址 1 的标记位和组 0 的第一行标记位匹配,因而,命中。具体如下图所示。
3. 读地址 7 的数据。<font color=#FF4500 size=3> 标记位为 01,索引位为 1,偏移位为 1,块号为 1。</font> 缓存行中有数据,组 1 的无效位为 0,地址的标记位和组 1 中的第一行和第二行的标记位不匹配,因而,未命中。而后,高速缓存从内存中取出块 6,块 7,共 2 字节,并存储在组 1 中。具体如下图所示。
4. 读地址 8 的数据。<font color=#FF4500 size=3> 标记位为 10,索引位为 0,偏移位为 0,块号为 0。</font> 缓存行中有数据,组 0 的第一行无效位为 1,第二行无效位为 0,地址的标记位和组 0 的第一行和第二行的标记位不匹配,因而,未命中。而后,高速缓存从内存中取出块 8,块 9,共 2 字节,并存储在组 0 的第二行中。具体如下图所示。
5. 读地址 0 的数据。<font color=#FF4500 size=3> 标记位为 00,索引位为 0,偏移位为 0,块号为 0。</font> 缓存行中有数据,组 0 的第一行无效位为 1,地址的标记位和组 0 的第一行的标记位匹配,因而,命中。具体如下图所示。
地址 | 二进制 | 是否命中 |
---|---|---|
0 | [${0000_2}$](t=00,s=0,b=0) | 否 |
1 | [${0001_2}$](t=00,s=0,b=1) | 是 |
7 | [${0111_2}$](t=01,s=1,b=1) | 否 |
8 | [${1000_2}$](t=10,s=0,b=0) | 否 |
0 | [${0000_2}$](t=00,s=0,b=0) | 是 |
两路相联高速缓存与间接映射高速缓存相比,在每组中减少了一行,缓存命中率晋升了 15%。防止了缓存频繁从内存中存取数据的状况,进步了程序运行速度。
6. 全相联高速缓存
全相联高速缓存中的行匹配和字抉择与组相联高速缓存中的是一样的,过程就不再赘述,其结构图如下所示。
相联度越高越好吗?
答案是否定的。较高的相联度会造成较高的老本。<font color=#FF4500 size=3> 实现难度大,价格昂贵,而且很难使之速度变快。</font> 较高的相联度会减少命中工夫,因为复杂性减少了,另外,还会减少不命中处罚,因为抉择就义行的复杂性也减少了。
相联度的抉择最终变成了命中工夫和不命中处罚之问的折中。一般来讲,<font color=#FF4500 size=3> 高性能零碎会为 L1 高速缓存抉择较低的相联度 </font>(这里的不命中处罚只是几个周期),而在不命中处罚比拟高的较低层上应用比拟小的相联度。例如,Intel Core i7 零碎中,L 和 L2 高速缓存是 8 路组相联的,而 L3 高速缓存是 16 路组相联的。
7. 实在计算机系统中的缓存
在此之前,咱们始终假如高速缓存只保留数据。不过,实际上,高速缓存既保留数据,也保留指令。只保留指令的高速缓存称为 <font color=#0000FF size=3> i-cache </font>。只保留程序数据的高速缓存称为 <font color=#0000FF size=3> d-cache </font>。既保留指令又包含数据的高速缓存称为 <font color=#0000FF size=3> 对立的高速缓存 </font>。
如下图所示为 Intel Core i7 处理器的高速缓存层次结构。每个 CPU 芯片有四个核。每个核有本人的 L1 i-cache,L1 d-cache 和 L2 对立的高速缓存。所有的核共享片上 L3 对立的高速缓存。其具体参数如下表所示。
缓存 | 大小 | 内部结构 | 拜访工夫 |
---|---|---|---|
L1 | 32KB | 8 路相联 | 4 时钟 |
L2 | 256KB | 8 路相联 | 10 时钟 |
L3 | 8M | 16 路相联 | 40-75 时钟 |
8. 缓存的评估指标
最初介绍下掂量高速缓存性能的一些指标:
8.1 不命中率
在一个程序执行或程序的一部分执行期间,内存援用不命中的比率,它等于:<font color=#0000FF size=3> 不命中数量 / 援用数量。</font>
8.2 命中率
命中的内存援用比率。它等于:
8.3 命中工夫
从高速缓存传送一个字到 CPU 所需的工夫,包含组抉择、行确认和字抉择的工夫。一般来讲,L1 缓存的命中工夫为:4 个时钟。L2 缓存的命中工夫为:10 个时钟。
8.4 未命中惩办
未命中须要的额定工夫。对于主存来说,个别为 <font color=#0000FF size=3> 50 ~ 200 个时钟周期。</font>
举个例子:
假如缓存命中工夫为 1 个时钟周期,缓存未命中惩办为 100 个时钟周期。
上面计算下 97% 缓存命中率和 99% 的缓存命中率的均匀拜访工夫为多少?计算公式为命中工夫加上未命中处罚乘以百分系数。
97% 的命中率:$1 + 0.03 \times 100 = 4$ 时钟。
99% 的命中率:$1 + 0.01 \times 100 = 2$ 时钟。
<font color=#0000FF size=3> 论断:命中率减少 2%,均匀拜访工夫缩小了 50%。</font>
9. 总结
计算机中存在着各种各样的缓存,比方,<font color=#0000FF size=3> 文件缓存 </font> 把一些须要高速存取的变量缓存在内存中,每次拜访间接读出即可。<font color=#0000FF size=3> 浏览器缓存 </font> 依据一套与服务器约定的规定进行工作,如果在浏览过程中后退或后退时拜访到同一个图片,这些图片能够从浏览器缓存中调出而即时显示。<font color=#0000FF size=3> 数据库缓存 </font> 常常须要从数据库查问的数据、或常常更新的数据放入到缓存中,这样下次查问时,间接从缓存间接返回,加重数据库压力。
咱们理解这么多基本概念有什么用呢?如果咱们了解了计算机系统是如何将数据在内存中组织和挪动的,那么在写程序时就能够把数据项存储在适合的地位,CPU 能更快地拜访到它们,进步程序的执行效率。
下一篇文章咱们将介绍 <font color=#0000FF size=3> 如何写出高效的代码,让程序运行的更快!</font> 欢送关注我的公众号,第一工夫获取更新!
养成习惯,先赞后看!如果感觉写的不错,欢送关注,点赞,在看,转发,谢谢!
如遇到排版错乱的问题,能够通过以下链接拜访我的 CSDN。
**CSDN:[CSDN 搜寻“嵌入式与 Linux 那些事”]