当前位置: 首页 > news >正文

计算机组成与体系结构:替换策略(MRU LRU PLRU LFU)

目录

 🎲  MRU(最近最常使用)

🪜 操作流程:

 🎲  LRU(最近最少使用)

🪜 操作流程:

示例

🔍 Age Bits(年龄位)

核心思想:

模拟过程:

存储代价 

 🎲  PLRU(Pseudo-Least Recently Used)

为什么需要 Pseudo-LRU? 

🪜 操作流程:

情况一:Cache 未满 (初始填充阶段)

情况二:Cache 已满 但命中(访问命中的块)

情况三:Cache 已满 且未命中(发生替换)

查找 PLRU 块(≈ 最久未使用) 

查找 MRU 块(≈ 最近访问过) 

优点:

 🎲    LFU(Least Frequently Used)策略

Frequency-Based  Policy(频率驱动的策略)

 🪜 操作流程:


前面我们探讨了几种不同类型的缓存替换策略,包括简单随机选择的 Random 策略,基于块进入缓存先来后到顺序的 FIFO 和 LIFO 策略,以及理想但不可实现的基于未来访问预测的 Optimal 策略。

然而,许多高效的替换策略并非依赖于随机性或简单的年龄,而是利用块的访问历史信息来推测哪些块未来最可能被再次访问。接下来,我们将介绍一类广泛应用的策略,它们正是基于这种访问历史,特别是块的最近使用情况来做出决策,这其中包括 Most Recently Used (MRU)、Least Recently Used (LRU) 以及其近似实现 Pseudo Least Recently Used (PLRU)。 

 🎲  MRU(最近最常使用)

 每当需要替换缓存中的一个块时,总是淘汰最近刚刚被访问过的缓存块。

也就是说,谁最近被访问,就谁先被淘汰。

🪜 操作流程:

  1. CPU 访问某个数据地址;

  2. 若发生 Cache Miss 且缓存已满;

  3. 查找“最近刚被访问的缓存块”;

  4. 替换掉它,把新数据加载进来。

假设当前缓存为 [A, B, C],刚访问过 C,现在访问 D:

  • Cache 满了 → 采用 MRU → 替换 C(最近刚访问过);

  • 结果变成 [A, B, D]

 

适用场景:

MRU 适用于某些特定访问模式,如:

  • 数据被访问后,短期内很少再次被访问;

  • 循环访问模式 (Cyclic Patterns)。(例如图片中的1,2,3,4……1,2,3,4……)

 🎲  LRU(最近最少使用)

LRU(最近最少使用) 替换策略总是淘汰 最久没有被访问的缓存块。

它利用了程序访问的时间局部性原则:最近访问过的数据更可能在短期内再次使用,因此我们优先保留“最近用过的”,淘汰“最久未使用的”。

🪜 操作流程:

  • 每次访问缓存数据时,更新该数据的“最近使用时间”;

  • 当发生缓存替换时,淘汰那个最久没有被访问的块。

实现方式常见有:

  • 链表记录访问顺序;

  • 为每个块打“时间戳”;

  • 或用栈模拟。

示例

假设 Cache 容量为 4。访问序列:0, 1, 2, 3, 4, 2, 3, 1, 5, 6

我们按照 LRU  策略模拟整个访问过程,并说明关键点。

LRU 过程

步骤访问Cache 状态 (LRU, 旧→新)替换说明
10[0]-加入 0
21[0, 1]-加入 1
32[0, 1, 2]-加入 2
43[0, 1, 2, 3]-加入 3
54[1, 2, 3, 4]0 被替换0 最久没用了
62[1, 3, 4, 2]-2 已在,更新为最近使用
73[1, 4, 2, 3]-同上
81[4, 2, 3, 1]-同上
95[2, 3, 1, 5]4 被替换4 最久未访问
106[3, 1, 5, 6]2 被替换2 最久未使用

🔍 Age Bits(年龄位)

  • Age Bits 是用于记录每个 cache line “上次使用时间” 的一种方式。

  • 在全相联映射中,cache 的任意位置都可以放任意数据,故替换策略必须决定淘汰哪个。

  • Age bits 为每个 cache line 分配一个 计数器或编码值(如 2 位、3 位、4 位...),来表示“谁最老”。

核心思想:

在一个 n 路全相联缓存中,每个缓存块维护一个 age 编码值(0~n-1),表示这个块的“使用新旧程度”。

  • 最大 age(n-1) 表示“刚刚访问”(最新);

  • age = 0 表示“最久未访问”(最老);

  • 每次访问:

    • 被访问块设置为 n-1(最新)

    • 所有 age 比它大的块不变

    • 所有 age 比它小的块 统一减一

  • 当替换发生时,选择 age == 0 的块 进行淘汰。 

模拟过程:

访问Cache 状态Age Bits替换说明
10[0][3, 0, 1, 2]第一次加入,设为3
21[0, 1][2, 3, 0, 1]0老化为2,1为新
32[0, 1, 2][1, 2, 3, 0]所有老化,2最新
43[0, 1, 2, 3][0, 1, 2, 3]所有顺延
54[1, 2, 3, 4][3, 0, 1, 2]✅ 替换00 的 age = 0,淘汰它
62[1, 2, 3, 4][2, 0, 3, 1]2 命中 → age=3,其它老化
73[1, 2, 3, 4][1, 0, 2, 3]3 命中 → age=3,其他减一
81[1, 2, 3, 4][0, 3, 1, 2]1 被访问,更新 age=3,其他老化
95[2, 3, 1, 5][3, 2, 0, 1]✅ 替换44 的 age = 0,被淘汰
106[3, 1, 5, 6][2, 1, 3, 0]✅ 替换2age=0 的 2 被淘汰

存储代价 

1️⃣ 为什么是 4!(4 的阶乘)? 

  • 在一个 4 路 set 中,有 4 个块;

  • 为了记录 LRU 的顺序,我们实际上是记录这 4 个块的相对“访问先后顺序”;

  • 所有访问顺序的排列组合就是 4 的阶乘:

    4!=4×3×2×1=24

  • 所以,你要记录某一时刻的完整 LRU 状态,就要能区分这 24 种顺序

2️⃣ 为什么需要 log₂(24) ≈ 4.58 ≈ 5 bit?

  • 用二进制编码这 24 个可能的访问顺序,需要的最少位数就是:

    log⁡2(24)≈4.58

  • 实际上只能使用整数位存储,所以你需要 至少 5 个 bit 才能编码所有顺序。

  • 每个 set 都要存这一份顺序信息,因此是 “5 bit per set”。

3️⃣ 如果关联度更高怎么办?

  • 关联度越高(如 8-way),排列组合就越多:

    8!=40320⇒log⁡2(8!)≈15.38! = 40320 \Rightarrow \log_2(8!) \approx 15.38!=40320⇒log2​(8!)≈15.3

  • 所以要记录 8-way LRU 顺序,需要 至少 16 bit per set。

  • 每个缓存 set 可能数以万计(比如 1024 sets),那么总共光记录顺序就占用了很多 bits。

 严格实现 LRU = 记录使用顺序 = 记录块的排列组合状态

  • 对于高关联度(如 8-way、16-way)的缓存:

    • 排列数量指数级上升

    • 对应的编码位数也线性上升(log₂(n!))

  • 所以:精确 LRU 的实现成本与缓存的关联度呈对数甚至阶乘级增长,最终带来巨大的硬件开销(huge overhead)

 🎲  PLRU(Pseudo-Least Recently Used)

为什么需要 Pseudo-LRU? 

在 n 路组相联缓存中,想要严格实现 LRU,就得记录每个 cache block 的访问顺序,需要 log₂(n!) bits per set。

例如:

  • 4 路组 → 24 种顺序 → 至少 5 bits

  • 8 路组 → 40320 种顺序 → 至少 16 bits

随着 n 增加,存储开销和更新逻辑急剧增加,对硬件极不友好。

定义:

Pseudo-LRU 是一种近似实现 LRU 的方法,不精确记录块的使用顺序,而是使用较少的状态信息

来大致估计哪个块最久未被使用,从而做出替换决策。

 它并不真正知道“谁最久没被用了”,但能用更少资源推测出“比较可能是最久未用的块”。

 这是一个 8-way set associative cache(每个 set 有 8 个块)使用的 Tree-based PLRU 替换策略。

🪜 操作流程:

  • 有 7 个方向指示位,分别是:

    • 层级 1:1 个 bit(根)

    • 层级 2:2 个 bit

    • 层级 3:4 个 bit

  • 每个 bit 表示“下一次替换要往哪边走”

    • 0 → Down(左)

    • 1 → Up(右)

  • 叶子节点 b₀ ~ b₇ 是 cache block,表示实际存放数据的 8 个位置。

情况一:Cache 未满 (初始填充阶段)

操作流程:

  1. 将当前 block 加入 cache;

  2. 通过树结构“访问路径”更新方向位(bits);

    在沿路径的每一个判断节点上,都将其方向设为“指向这次访问的方向”;
  3. 不执行任何替换,只更新路径。

🔹 关键点: 

Cache 未满时不会有替换,树结构的方向位起到“标记路径已使用”的作用,为未来做准备 。

情况二:Cache 已满 但命中(访问命中的块)

操作流程:

  1. 查找该块是否在 cache;

  2. 如果 命中,则无需替换;

  3. 仍然要更新“路径方向 bits”:

    • 访问路径上的每个 bit 更新为“指向这次使用的方向”。

 🔹 关键点: 

命中时仍然更新路径,使该块不会很快被淘汰,模仿 LRU“刚访问过”的行为。 

情况三:Cache 已满 且未命中(发生替换)

操作流程:

  1. Cache 已满,新 block 不在 cache;

  2. 从根节点开始,把其原本的方向位反转(0→1,1→0)。

  3. 按照反转后的值,依次读取每个指示位,按照指示的方向走向下一个子节点。

  4. 一直走到底,直到指向某个叶节点(即某个缓存块),将其视为“最久未使用块”,并进行替换。

在 替换操作执行完之后(或任意时刻),我们其实可以从树结构中推断出:

  • 当前哪个 block 是 PLRU 替换候选者(= 最久未使用(approximate LRU))

  • 当前哪个 block 是 MRU(Most Recently Used)(= 最刚刚被访问的)

查找 PLRU 块(≈ 最久未使用) 

从根开始,严格按照节点当前存储的 bit 指示方向(0=左,1=右)一路向下,直到某个叶子块 → 即为 PLRU 替换候选块。

原因:

  • 方向位每次访问时都会沿访问路径反转;

  • 所以当前 bit 指示的路径,是上次没有访问的方向;

  • 这一路路径就是“最久没有被访问的方向链”;

  • 所以走到底的叶节点,就是 Pseudo-LRU 块(最久未用的)。

查找 MRU 块(≈ 最近访问过) 

从根开始,按照每个节点 bit 的“反方向”走(即:如果 bit 是 0,就走右;如果 bit 是 1,就走左),直到叶节点 → 即为 MRU 块(最“新”访问过的)

原因:

  • 被访问的路径上的 bit 在访问后都会被反转;

  • 所以当前的“非选中方向”其实代表的是“最近走过的路径”;

  • 沿这个“非选中路径”走下去,就会抵达最近一次访问的块。 

优点:

优点说明
硬件开销低n 路组仅需 n–1 个 bits(比如 8 路组只用 7 bits)
访问/替换开销低每次访问只更新 log₂(n) 条路径
接近 LRU 效果在多数程序中命中率与 LRU 接近

 🎲    LFU(Least Frequently Used)策略

Frequency-Based  Policy(频率驱动的策略)

在缓存替换中,频率驱动的策略关注的是:每个缓存块被访问的“频率”而不是“时间顺序”。

即:“谁被访问得少,谁就先走。”

这种策略适用于访问热点长期稳定的场景,比如 Web 缓存、数据库缓存。其中最常用的是LFU策略。

LFU 替换策略始终淘汰访问频率最少的缓存块。每个缓存块都维护一个计数器,记录它被访问的次数。替换时,选择计数最小的块。

 🪜 操作流程:

  • 每个缓存块都有一个访问频率计数器 freq

  • 每次访问:

    • 如果 block 在缓存(hit) → freq++

    • 如果不在缓存(miss):

      • 若 cache 未满 → 插入该 block,freq = 1

      • 若已满 → 选择 freq 最小的块进行替换;

  • 插入新块时,初始化其 freq = 1

 

这里采用了FIFO策略: 

相关文章:

  • WebRTC技术下的EasyRTC音视频实时通话SDK,助力车载通信打造安全高效的智能出行体验
  • HDFS的概述
  • 竞品分析是什么,包括哪些内容?AI竞品分析生成器推荐!
  • 人工智能、深度学习、机器学习的联系与区别
  • 英语学习笔记
  • Java零基础学习Day14——面向对象进阶
  • 算法图表总结:查找、排序与递归(含 Mermaid 图示)
  • ORACLE 11.2.0.4 数据库磁盘空间爆满导致GAP产生
  • 力扣.1471数组的k个最强值,力扣.1471数组的k个最强值力扣1576.替换所有的问号力扣1419.数青蛙​编辑力扣300.最长递增子序列
  • 小程序映射逻辑处理
  • day 17 无监督学习之聚类算法
  • 各编程语言对正则表达式标准的支持对比
  • Python生成器:高效处理大数据的秘密武器
  • k8s(12) — 版本控制和滚动更新(金丝雀部署理念)
  • 集合进阶2
  • opencv4.11编译Debug提示缺少python312_d.lib或python3*_d.lib的解决办法
  • 金仓数据库使用sys_walfile_name函数时的时间线问题排查指南
  • C++ 基础知识点
  • Prometheus监控系统部署
  • 机器视觉对位手机中框点胶的应用
  • 临港新片区将新设5亿元启航基金:专门投向在临港发展的种子期、初创型企业
  • 俄外交部:俄乌伊斯坦布尔谈判改在当地时间15日下午举行
  • 因存在安全隐患,福特公司召回约27.4万辆SUV
  • 河南省委常委会会议:坚持以案为鉴,深刻汲取教训
  • 沈阳一超市疑借领养名义烹食流浪狗,当地市监局:已收到多起投诉
  • 哈马斯表示已释放一名美以双重国籍被扣押人员