(王道计算机组成原理)第三章存储系统-第五节3:Cache替换算法(FIFO,近期最少使用算法-LRU,LFU)

本文介绍了Cache的替换算法,包括随机算法RAND、先进先出FIFO、近期最少使用LRU和最不经常使用LFU。LRU算法基于局部性原理,通常提供最高的Cache命中率,而LFU则可能不遵循局部性,导致不理想的性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本节思维导图

在这里插入图片描述

第六节2:Cache和主存的映射方式(全相联映射、直接映射和组相连映射)中我们讲到了Cache和主存之间的映射关系,细致分析了三种映射方式各自的特点。那么下一个亟待解决的问题就是:Cache是很小的,主存却很大,如果Cache满了应该怎么办? 这就是本节的主题——Cache的替换算法。当然,不同的映射方式其替换机制也会有所不同

  • 全相联映射:Cache完全满了才需要替换,需要在全局中选择替换哪一块
  • 直接映射:如果对应位置为空则直接替换,无需考虑替换算法
  • 组相联映射:分组内满了才需要替换,需要在分组内选择替换哪一块

本节以全相联映射为例,介绍以下四种替换算法

  • 随机算法(RAND)
  • 先进先出算法(FIFO)
  • 近期最少使用算法(LRU)
  • 最不频繁使用算法(LFU)

在讲解之前大家一定明白一点,CPU每访问一个内存块,都会立即把该内存块调入Cache中

一:随机算法(RAND)

随机算法(RAND):若Cache已满,则随机选择一块进行替换

  • 通过以下叙述可知:随机算法十分简单,但是它完全没有考虑到局部性原理,命中率很低,实际效果很不稳定

如下有4个Cache块,初始状态下4个Cache块均为空,采用全相连映射,CPU访问主存块的顺序为:{1,2,3,4,1,2,5,1,2,3,4,5},CPU每访问一个内存块,都会立即把该内存块调入Cache中,前四次调入时由于都有空闲Cahce块,所以不会发生替换

访问主存块123412512345
Cache #01111
Cache #1222
Cache #233
Cache #34
是否命中?
是否替换?

继续访存,{1,2,3,4,1,2,5,1,2,3,4,5}。由于1,2主存块已经被调入了Cache,所以直接命中

访问主存块123412512345
Cache #0111111
Cache #122222
Cache #23333
Cache #3444
是否命中?
是否替换?

继续访存,{1,2,3,4,1,2,5,1,2,3,4,5}。对于5号主存块,它并没有调入Cache中,因此需要立即被调入,但此时已经没有空闲Cache块了,所以需要使用替换算法选择一块换出,然后再把5号主存块调入。这里采用的是随机算法,所以我们可以任意挑选一块调入,比如把3号主存块给替换出去

访问主存块123412512345
Cache #01111111
Cache #1222222
Cache #233335
Cache #34444
是否命中?
是否替换?

继续访存,{1,2,3,4,1,2,5,1,2,3,4,5}。直接命中

访问主存块123412512345
Cache #0111111111
Cache #122222222
Cache #23333555
Cache #3444444
是否命中?
是否替换?

继续访存,{1,2,3,4,1,2,5,1,2,3,4,5}。和上面一样,随机挑选一块换出

访问主存块123412512345
Cache #01111111111
Cache #1222222222
Cache #233335555
Cache #34444443
是否命中?
是否替换?

继续访存,也即{1,2,3,4,1,2,5,1,2,3,4,5}。和上面一样,随机挑选一块换出

访问主存块123412512345
Cache #011111111114
Cache #12222222222
Cache #2333355555
Cache #344444433
是否命中?
是否替换?

访存结束,{1,2,3,4,1,2,5,1,2,3,4,5}。直接命中

访问主存块123412512345
Cache #0111111111144
Cache #122222222222
Cache #23333555555
Cache #3444444333
是否命中?
是否替换?

二:先进先出算法(FIFO)

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

  • 通过以下叙述可知:先进先出算法实现也很简单,但该算法依然没有考虑到局部性原理,因为最先被调入的Cache块也有可能是会频繁访问到的。而且此算法容易产生抖动现象(—刚换上去的块又立马被换下)

仍然采用之前的例子,直接进行到这一步

访问主存块123412512345
Cache #0111111
Cache #122222
Cache #23333
Cache #3444
是否命中?
是否替换?

继续访存,{1,2,3,4,1,2,5,1,2,3,4,5}。对于5号主存块,它并没有调入Cache中,因此需要立即被调入,但此时已经没有空闲Cache块了,所以需要使用替换算法选择一块换出,然后再把5号主存块调入。这里采用的是FIFO算法,根据先进先出原则,最先被调入Cache的最先被替换,因此1号被替换

访问主存块123412512345
Cache #01111115
Cache #1222222
Cache #233333
Cache #34444
是否命中?
是否替换?

继续访存,{1,2,3,4,1,2,5,1,2,3,4,5}。此时应该替换2号

访问主存块123412512345
Cache #011111155
Cache #12222221
Cache #2333333
Cache #344444
是否命中?
是否替换?

后续步骤不再详细演示,最终结束状态如下

访问主存块123412512345
Cache #0111111555544
Cache #122222211115
Cache #23333332222
Cache #3444444333
是否命中?
是否替换?

三:近期最少使用算法(LRU)——效率最高

近期最少使用算法(LRU):该算法会为每一个Cache块设置一个计数器,用于记录每个Cache块究竟有多长时间没有被访问了。在替换时直接选取计数器最大的替换即可

  • 通过以下叙述可知:LRU算法是基于局部性原理的,近期访问过的主存块,在不久的将来很有可能会被再次访问到,因此这种淘汰机制是合理的。LRU算法的实际运行效果也很优秀,Cache命中率也高

计数器的变化规则如下

  • 命中时:所命中的块的计数器清零,计数器比其低的块的计数器+1,其余不变
  • 未命中且还有空闲块时:新装入的块的计数器置为0,其余非空闲块的计数器全+1
  • 未命中且没有空闲块时:计数器最大的块被淘汰,新装入块的计数器置为0,其余块的计数器+1

如下表格表示初始状态

计时器访问主存块123412512345
0Cache #0
0Cache #1
0Cache #2
0Cache #3
是否命中?
是否替换?

继续访存,{1,2,3,4,1,2,5,1,2,3,4,5}。由于1装入了第一个Cache块,属于未命中且还有空闲块,因此该块计数器置为0,其余非空闲块计数器全+1

计时器访问主存块123412512345
0Cache #01
0Cache #1
0Cache #2
0Cache #3
是否命中?
是否替换?

继续访存,{1,2,3,4,1,2,5,1,2,3,4,5}。情况同上

计时器访问主存块123412512345
1Cache #011
0Cache #12
0Cache #2
0Cache #3
是否命中?
是否替换?

第三、四个主存块亦是如此

计时器访问主存块123412512345
3Cache #01111
2Cache #1222
1Cache #233
0Cache #34
是否命中?
是否替换?

继续访存,{1,2,3,4,1,2,5,1,2,3,4,5}。此时Cache命中,因此需要将所命中块的计数器清零,比其低的块的计数器+1,其余不变

  • 这一点其实就体现了LRU算法的核心,它能保证最近访问的块的计数器一定很低
计时器访问主存块123412512345
0Cache #011111
3Cache #12222
2Cache #2333
1Cache #344
是否命中?
是否替换?

继续访存,{1,2,3,4,1,2,5,1,2,3,4,5},情况同上

计时器访问主存块123412512345
1Cache #0111111
0Cache #122222
3Cache #23333
2Cache #3444
是否命中?
是否替换?

继续访存,也即{1,2,3,4,1,2,5,1,2,3,4,5}。此时属于 “未命中且没有空闲行”,所以计数器最大的块会被淘汰(淘汰3号主存块),新装入块的计数器置为0,其余块计数器全+1

计时器访问主存块123412512345
2Cache #01111111
1Cache #1222222
0Cache #233335
3Cache #34444
是否命中?
是否替换?

继续访存,{1,2,3,4,1,2,5,1,2,3,4,5}。直接命中

  • 注意:只需要将“比该块计数器值小的块的计数器+1”即可,大的不变,因此上面表格中的3号Cache的计时器就不用动了,这里很容易犯错
计时器访问主存块123412512345
0Cache #011111111
2Cache #12222222
1Cache #2333355
3Cache #344444
是否命中?
是否替换?

继续访存,{1,2,3,4,1,2,5,1,2,3,4,5}。直接命中

计时器访问主存块123412512345
1Cache #0111111111
0Cache #122222222
2Cache #23333555
3Cache #3444444
是否命中?
是否替换?

继续访存,{1,2,3,4,1,2,5,1,2,3,4,5}。未命中,且没有空行

计时器访问主存块123412512345
2Cache #01111111111
1Cache #1222222222
3Cache #233335555
0Cache #34444443
是否命中?
是否替换?

继续访存,也即{1,2,3,4,1,2,5,1,2,3,4,5}。未命中,且没有空行

计时器访问主存块123412512345
3Cache #011111111111
2Cache #12222222222
0Cache #2333355554
1Cache #344444433
是否命中?
是否替换?

继续访存,也即{1,2,3,4,1,2,5,1,2,3,4,5}。未命中,且没有空行

计时器访问主存块123412512345
0Cache #0111111111115
3Cache #122222222222
1Cache #23333555544
2Cache #3444444333
是否命中?
是否替换?

四:最不经常使用算法(LFU)

最不经常使用算法(LFU):该算法会为每一个Cache块设置一个计数器,用于记录每个Cache块被访问过几次。在替换时直接选取计数器最小的替换即可

  • 通过以下叙述可知:LFU算法并没有很好地遵循局部性原理,比如微信聊天相关的块,在某个时间段内使用率会很高,但是一段时间后使用率会很低,并不科学

计数器的变化规则为:

  • 新调入的块计数器为0,之后每访问一次计数器就+1。需要替换时,选择计数器最小的一行替换
  • 若有多个计数器最小的行,可以按照行号递增或FIFO策略进行选择
计时器访问主存块123412512345
0Cache #0
0Cache #1
0Cache #2
0Cache #3
是否命中?
是否替换?

继续访存,也即{1,2,3,4,1,2,5,1,2,3,4,5}

计时器访问主存块123412512345
0Cache #01111
0Cache #1222
0Cache #233
0Cache #34
是否命中?
是否替换?

继续访存,也即{1,2,3,4,1,2,5,1,2,3,4,5}。发生命中,计数器+1

计时器访问主存块123412512345
1Cache #0111111
1Cache #122222
0Cache #23333
0Cache #3444
是否命中?
是否替换?

继续访存,也即{1,2,3,4,1,2,5,1,2,3,4,5}。选择计数器最小的那一行,但是这里有两行相同(都是0),所以再按照FIFO策略选择3号主存块淘汰

计时器访问主存块123412512345
1Cache #01111111
1Cache #1222222
0Cache #233335
0Cache #34444
是否命中?
是否替换?

继续访存,也即{1,2,3,4,1,2,5,1,2,3,4,5}。1,2命中,计数器+1

计时器访问主存块123412512345
2Cache #0111111111
2Cache #122222222
0Cache #23333555
0Cache #3444444
是否命中?
是否替换?

继续访存,也即{1,2,3,4,1,2,5,1,2,3,4,5}。需要进行替换,这里我们再采用行号递增的规则淘汰5号主存块

计时器访问主存块123412512345
2Cache #01111111111
2Cache #1222222222
0Cache #233335553
0Cache #34444444
是否命中?
是否替换?

继续访存,也即{1,2,3,4,1,2,5,1,2,3,4,5}。命中,计时器+1

计时器访问主存块123412512345
2Cache #011111111111
2Cache #12222222222
0Cache #2333355533
1Cache #344444444
是否命中?
是否替换?

继续访存,也即{1,2,3,4,1,2,5,1,2,3,4,5}。需要替换,只剩一个最小的了,替换3号主存块即可

计时器访问主存块123412512345
2Cache #0111111111111
2Cache #122222222222
0Cache #23333555335
1Cache #3444444444
是否命中?
是否替换?
好的,我明白了。这是一道计算机组成原理方面的问题,我会尽力回答。 (1) 主存地址结构: 总共有 512KB 的主存容量,每行内有 16B,所以主存总共有 512KB / 16B = 32K 行。每行的地址需要用 15 位二进制数来表示,其中低 4 位表示字节偏移,高 11 位表示行号。因此,主存地址结构如下: ``` | 11 位行号 | 4 位字节偏移 | ``` (2) Cache地址结构: Cache 采用直接映射方式,容量为 8KB,每行内有 16B,所以 Cache 总共有 8KB / 16B = 512 行。由于是直接映射方式,每行只能映射到主存的某一行,因此需要用 9 位二进制数来表示行号。每行内的字节偏移仍然需要用 4 位二进制数来表示。此外,还需要一个有效位来表示该行是否有效。因此,Cache地址结构如下: ``` | 1 位有效位 | 9 位行号 | 4 位字节偏移 | ``` (3) 将主存的第513块调入Cache,对应Cache字块号是多少? 由于主存容量为 512KB,每行内有 16B,所以主存总共有 512KB / 16B = 32K 行。因此,第513块对应的行号是 512 + 16 = 528。由于 Cache 采用直接映射方式,行号需要取模 512,因此对应的 Cache 行号是 528 mod 512 = 16。因此,将主存的第513块调入 Cache 后,对应的 Cache 字块号是 16。 (4) CPU访问主存地址04011H是否在Cache命中? 主存地址 04011H 对应的行号是 0401H,字节偏移是 1。由于 Cache 采用直接映射方式,对应的 Cache 行号是 0401H mod 512 = 129。因此,CPU访问主存地址 04011H 不在 Cache 命中。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快乐江湖

创作不易,感谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值