【计算机组成原理】三、存储系统:4.高速缓冲存储器(Cache)(主存映射(全相联映射、直接映射 、组相联映射)、替换算法、Cache写策略)

8.高速缓冲存储器Cache

文章目录

高速缓冲存储器简称 Cache ,位于主存和CPU之间,用来存放正在执行的程序段和数据,以便CPU能高速地使用它们。

Cache的存取速度可与CPU的速度相匹配,但存储容量小、成本高、集成度低。

目前的计算机通常将它们制作集成在CPU中,用SRAM实现。

8.1局部性原理

局部性原理:cache能够有效工作的理论依据。

高速缓冲技术就是利用程序访问的局部性原理,把程序中正在使用的部分存放在一个高速的、容量较小的Cache中,使CPU的访存操作大多数针对Cache进行,从而大大提高程序的执行速度。

8.1.1空间局部性

在最近的未来要用到的信息,很可能与现在正在使用的信息在存储空间上是邻近的(相邻信息),因为指令通常是顺序存放、顺序执行的,数据一般也是以向量、数组等形式簇聚地存储在一起的。

8.1.2时间局部性

在最近的未来要用到的信息,很可能是现在正在使用的信息(同一个信息),因为程序中存在循环。

例:假定数组元素按行优先方式存储,对于下面的两个函数:

cpp 复制代码
程序A:
int sumarrayrows( int a[M][N] )
{
	int i,j,sum=0;
	for( i=0;i<M;i++ )
		for( j=0;j<N;j++ )
			sum+=a[i][j];
	return sum;
}
程序B:
int sumarraycols( int a[M][N] )
{
	int i,j,sum=0;
	for( j=0;j<N;j++ )
		for( i=0;i<M;i++ )
			sum+=a[i][j];
	return sum;
}

(1)对于数组a的访问,哪个空间局部性更好?哪个时间局部性更好?

(2)对于指令访问来说,for循环体的空间局部性和时间局部性如何?

答案:(1)程序A对数组a的访问顺序与存放顺序是一致的,因此空间局部性好。程序B对数组a的访问顺序与存放顺序不一致,因而没有空间局部性。两个程序的时间局部性都很差,因为每个数组元素都只被访问一次。

(2)对于for循环体,程序A和程序B中的访问局部性是一样的。因为循环体内指令按序连续存放,所以空间局部性好;内循环体被连续重复执行,因此时间局部性也好。

8.2性能指标

  • 命中率 H:CPU想访问的信息已经在Cache比率。

H = N c N c + N m H=\frac {N_c}{N_c+N_m} H=Nc+NmNc

一个程序执行中:
N c N_c Nc:访问cache的总2次数。
N m N_m Nm:访问主存的总次数。

  • 缺失(未命中)率 MM = 1 - H
  • 平均访问时间 T~a~

有2种策略:

t c t_c tc:命中cache的访问时间
t m t_m tm:未命中的访问时间(这里的未命中而不是访问主存的时间)

  1. 同时访问Cache和主存:

T a = H ⋅ t c + ( 1 − H ) ⋅ t m T_a = H · t_c + (1-H) · t_m Ta=H⋅tc+(1−H)⋅tm

  1. 先访问Cache,再访问主存:

T a = H ⋅ t c + ( 1 − H ) ⋅ ( t c + t m ) T_a = H · t_c + (1-H) · (t_c+t_m) Ta=H⋅tc+(1−H)⋅(tc+tm)

  • 系统的效率 e

e = t c T a e=\frac {t_c}{T_a} e=Tatc

【例】假设Cache的速度是主存的5倍,且Cache的命中率为95%,则采用Cache后,存储器性能提高多少(设Cache和主存同时被访问,若Cache命中则中断访问主存)?

解:设Cache的存取周期为t,则主存的存取周期为5tCache和主存同时访问,不命中时访问时间为5t故系统的平均访问时间为Ta=0.95×t+0.05×5t = 1.2t

设每个周期可存取的数据量为S,则存储系统带宽为S/1.2t ,不采用cache时的带宽为S/5t
S 1.2 t S 5 t = 5 t 1.2 t u ≈ 4.17 \cfrac {\cfrac {S}{1.2t}}{\cfrac {S}{5t}}=\cfrac {5t}{1.2t}u≈4.17 5tS1.2tS=1.2t5tu≈4.17

因为是使用cache是不使用的4.17倍,所以要减去1,提高了3.17倍。

❗8.3主存映射(查策略)

详见《计算机组成原理》唐朔飞,高等教育出版社

存放方式 直接映射 全相联映射
命中率 最低 最高
判断开销 最小 最大
所需时间 最短 最长
标记所占的额外空间开销 最少 最大
8.3.1全相联映射

空的位置随意放

  • 优点:Cache块的冲突概率低,空间利用率高,命中率高。
  • 缺点:标记的比较速度较慢,实现成本较高,需采用昂贵的按内容寻址的相联存储器进行地址映射。

地址结构:

标记 块内地址
8.3.2直接映射

对号入座:主存中的每一块只能装入Cache中的唯一位置。若这个位置已有内容,则产生块冲突,原来的块将无条件地被替换出去(无须使用替换算法)。

  • 缺点:直接映射的块冲突概率最高,空间利用率最低。

地址结构:

标记 Cache行号 块内地址
8.3.3组相联映射

按号分组,在自己组内随便放:将Cache空间分为大小相同的组,主存的一个数据块可以装入一组内的任何一个位置。

地址结构:

标记 组号 块内地址
CPU访存过程

首先根据访存地址中间的组号找到对应的Cache组;将对应Cache组中每个行的标记与主存地址的高位标记进行比较;若有一个相等且有效位为1,则访问Cache命中, 此时根据主存地址中的块内地址,在对应Cache行中存取信息;

若都不相等或相等但有效位为0,则不命中,此时CPU从主存中读出该地址所在的一块信息送到对应Cache组的任意一个空闲行中,将有效位置。

8.4替换算法

Cache很小,主存很大。如果Cache满了,那么就需要使用替换算法来替换数据。

8.4.1随机算法(RAND)

随机算法(RAND, Random):若cache已满,则随机选择一块替换。

实现简单,但完全没考虑局部性原理,命中率低,实际效果很不稳定

8.4.2先进先出算法(FIFO)

先进先出算法FIFO, First In First Out):若Cache已满,则替换最先被调入Cache的块。

FIFO算法实现简单,最开始按#0#1#2#3放入cache,之后轮流替换#0#1#2#3。

FIFO依然没考虑局部性原理,最先被调入Cache的块也有可能是被频繁访问的

抖动现象:频繁的换入换出现象(刚被替换的块很快又被调入)。

❗8.4.3近期最少使用(LRU)

近期最少使用算法LRU , Least Recently Used):为每一个Cache块设置一个"计数器",用于记录每个Cache块已经有多久没被访问了。当cache满后替换"计数器"最大的。

这里命中了,然后比其低的计数器 +1,因为计数器的目的是用于寻找计数器最大的cache块。而这3已经是最大了,3+1=4也就没有意义,因为3足以判断找出计数器最大的cache块,所以3加1是没有意义的,所以这里不用加1。

【总结】Cache块的总数=2^n^,则计数器只需 n 位。且Cache装满后所有计数器的值一定不重复

LRU算波基于"局部性原理"。近期被访问过的主存块,在不久的将来也很有可能被再次访间,因此淘汰最久没被访问过的块是合理的。LRU算法的实际运行效果优秀,Cache命中率高。

但是若被频繁访问的主存块数量 > Cache行的数量,则还是有可能发生"抖动 "。

如:{1,2,3,4,5,1,2,3,4,5,1,2...}

【技巧】做题小妙招,可以看主存块前面的有哪些主存块,那么最后没有出现的那一个就是要替换的cache块。

8.4.4最近不经常使用(LFU)

最不经常使用算法 (LFU, LeastErequentlyUsed):为每一个Cache块设置一个"计数器",用于记录每个cache块被访问过几次。当Cache满后替换"计数器"最小的。

若有多个计数器最小的行,可按行号递增、或者FIFO策略进行选择。

LFU算法:曾经被经常访问的主存块在未来不一定会用到(如:微信视频聊天相关的块),并没有很好地遵循局部性原理,因此实际运行效果不如LRU。

8.5Cache写策略

读操作不会导致cache和主存的数据不一致,但是写操作会导致。

  • 写命中
    • 写回法
    • 全写法
  • 写不命中
    • 写分配法

    • 非写分配法

8.5.1写命中
1)写回法

写回法(write-back):当CPU对Cache写命中时,只修改Cache的内容,而不立即写入主存,只有当此块被换出时才写回主存。

未修改的块不用写回。

减少了访存次数,但存在数据不一致的隐患。

2)全写法

全写法 (写直通法,write-through):当CPU对Cache写命中时,必须把数据同时写入Cache和主存,一般使用写缓冲(write buffer)。

访存次数增加,速度变慢,但更能保证数据一致性。

但如果使用写缓冲,CPU写的速度很快,若写操作不频繁,则效果很好。若写操作很频繁,可能会因为写缓冲饱和而发生阻塞。

暂时放到写缓冲中,当CPU去做其他任务,这时候会有一个专门的电路把修改同步到主存。

8.5.2写不命中
1)写分配法

写分配法(write-allocate):有CPU对Cache写不命中时,把主存中的块调入Cache,在Cache中修改。

通常搭配写回法使用。

2)非写分配法

非写分配法(not-write-allocate):当CPU对Cache写不命中时,直接写入主存,不调入Cache。

通常搭配全写法使用。

8.6多级Cache

现代计算机常采用多级Cache

L1, L2, L3...

离CPU越近的速度越快,容量越小;

离CPU越远的速度越慢,容量越大。

相关推荐
浙江赛思电子科技有限公司2 小时前
自动驾驶为什么需要时间同步?高精度时间同步如何实现?
linux·科技·计算机网络·硬件架构·硬件工程·能源·信号处理
LeonNo113 小时前
软考:缓存持久化和过期策略,淘汰策略
java·开发语言·缓存
黄俊懿3 小时前
【架构师从入门到进阶】第一章:架构设计基础——第五节:架构演进(缓存到微服务)
分布式·后端·缓存·微服务·架构·系统架构·架构设计
宇将军6663 小时前
Javascript中如何实现函数缓存?函数缓存有哪些应用场景?
开发语言·javascript·缓存
一叶飘零_sweeeet3 小时前
Java 实现自定义 LRU 缓存
java·缓存·lru
飞升不如收破烂~4 小时前
rediss数据结构及其底层实现
数据库·redis·缓存
过去式的马马马19 小时前
AIPPT项目(提供完整API接入支持套壳)成熟产品线上运营
数据库·redis·缓存
日里安19 小时前
9. 基于 Redis 实现排行榜功能
数据库·redis·缓存
Mr.Demo.20 小时前
[Redis] Redis哨兵机制
数据库·redis·缓存
几何心凉1 天前
Service Worker 缓存未更新的原因与解决方案
javascript·缓存