ARC lru_论文笔记:[FAST'03] ARC: A Self-Tuning, Low Overhead...

ARC算法是一种缓存替换策略,其表现优于常见的LRU算法,同时实现难度和复杂度相近。ARC通过动态调整recency与frequency之间的平衡,无需预设参数即可适应不同负载环境。本文详细介绍了ARC的基本思想、算法流程及其优势。

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

论文笔记:[FAST'03] ARC: A Self-Tuning, Low Overhead Replacement Cache

ARC 是一种缓存替换算法,在很多种负载环境的表现优于常用的 LRU 算法,并且实现难度和算法复杂度与 LRU 近似。

ARC 算法具有以下优良特性:

  1. 在 recency 和 frequency 之间持续的进行动态(在线)调整
  2. 无需事先指定特别的参数(先验知识)
  3. 具有全局优化策略(意译,不确定翻译的对不对,原文 empirically universal)
  4. 可以(在某种程度上)抵抗线性扫描(scan-resistant)

这篇文章中还存在一点问题没来的及修改,L1和L2不是固定大小为c,而是尽可能的维护L1和L2的大小为c,将cache项从L1中挪到L2的过程中会减小L1增加L2。

这篇文章中的公式较多,建议去我的博客阅读:论文笔记:[FAST'03] ARC: A Self-Tuning, Low Overhead Replacement Cache

原论文中花费了很多篇幅介绍之前人们试图提出比 LRU 更好的算法的一些工作,并进行了一番对比,本文限于篇幅和精力略过这部分内容。

基本思想

LRU 的问题在于其只考虑了 recency 而完全没有考虑 frequency。一般而言,保存 freqency 信息的代价又比较大。ARC 的最根本的思路(以下称为 DBL)在于使用 2 个 LRU,其中一个 L1存放最近被访问 1 次的数据(也就是传统的 LRU),另一个 L2 存放最近被访问 2 次及以上的数据。这一近似减少了维护 frequency 的成本。最终目标是维持 |L1|=|L2|=c,但是论文中并没有解释为什么要这么做。

我们把整个 Cache 分为以下 2 个部分:

  1. Cache Directory
  2. Cache Item

其中 Cache Directory 用于进行索引,Cache Item 真正表示在缓存中的内容。对于传统 LRU 算法而言,以上 2 者的大小是一样的(Directory 索引的项的数量和真正在缓存中的内容的数量)。对于上面提到的 DBL 而言,以上 2 者的大小也是一样的,为 |L1|+|L2|=2c。特别的,在 DBL 种,L1 就是一个传统的 LRU,其大小为 c。

上面提到的 DBL 算法的问题是使用了 2 倍于 LRU 的空间,其效果比 LRU 好是显然的,能不能使用和 LRU 一样多的空间得到更好的效果呢?一个直接的想法是使用 2c的 Cache Directory 但是只保留 c 个 Cache Item 常驻内存,那么在这 2c 个索引项中如何取舍就是一个问题。如 [figure-1] 所示,我们将 L1 和 L2 分别拆分为 T1,B1,T2,B2。直觉上,T1 比 B1 更有价值,T2 比 B2 更有价值,如果我们能够维持 |T1|+|T2|≤c,则应当将 T1∪T2中的元素保留在 Cache 中。

80ef65ecdf9875af9d406b8a3c45dc97.png

剩下的问题,在于如何在 L1∪L2中调节 T1∪T2 的位置(即调节 |B1| 和 |B2|),以及如何在 T1∪T2 中调节两者的大小分配。特别的,当 |L1|=|T1|=c时,该算法等价于 LRU,也就是说如果调节得当的话,该算法至少能做到和 LRU 一样好。

ARC 算法

由于我们要求保持性质 |L1|=|L2|=c,因此当调节 T1 和 T2 的关系时,实际上就调节了 T1∪T2 在 L1∪L2 中的位置。直觉上相当于在 L1 和 L2 中间的地方有一个固定大小的滑块,我们在调节这个滑块的位置。不妨令 |T1|=p,则 |B1|=|T2|=c−p 且 |B2|=p。

接下来,我们需要思考一下 B1 和 B2 对我们而言意味着什么。回想上面提到的 DBL,L1 意味着最近只命中了 1 次的缓存项索引,其中 T1 是我们保留在缓存中的内容,B1 是我们没有保留在缓存中的内容。因此,如果查询请求命中了 B1,意味着我们应当把这次命中当作命中了 L1 来看,并且相应的,说明我们的 |T1| 可能小了,没能将这次命中的数据存起来。论文中,当 |B1|>|B2| 时,p 增加 1,当 |B1|<|B2| 时,p 增加 |B2|/|B1|,但是论文中没有解释为什么这么设计。

完整的 ARC 算法如 [figure-2] 所示,其中 Case I-III 对应于 DBL 命中的情况,Case IV 对应于 DBL 未命中的情况。

cc5a9a459f63c2b62066e500d23928cd.png

问题回顾

之前提到了一些问题,在此继续探讨一下。

L1 和 L2 的大小是否可以调整

在目前的设计中,|L1|=|L2|=c,由于我们最多只保留 |T1|+|T2|=c 项内容,在极端情况下 |L1|=|T1|=c,此时进一步增大 L1 也不能再进行什么调整了(受限于 Cache 大小),从这个角度来看在 L1 和 L2 之间进行调整可能是没什么意义的。

但是在论文中特别提到了 E. Extra History,讨论了这样一个问题:

An interesting question is whether we can incorporate more history information in ARC to further improve it.

论文中给出了一种利用额外的空间的方法,似乎暗示了这么做是有用的,但是没有论证对比这一方法的效果如何。

p 的调整为什么不是固定的

推测是因为当 B1 小的时候命中 B1 是比较困难的,要对此做一个加成。但是为什么这么做论文并没有给出一个解释,也没有什么对比。

Scan-Resistant

直觉上 ARC 是可以抵御扫描 Pattern 的请求的,因为 ARC 不仅考虑了 recency,还考虑了 frequency。具体而言,有以下 2 个原因:

  1. 扫描会刷新 L1 的内容,但是不会影响 L2 的内容
  2. 扫描会更多的命中 B2(相较于 B1),因此会进一步减小 |T1|,进而使得扫描造成的影响比单纯考虑第 1 点更小

不总结总觉得少点什么

总的来说,ARC 算法通过一种比较巧妙的方法,在不显著增加实现成本和算法时间复杂的情况下,较为显著的改进了 LRU 算法。使用 LRU 来承载最近遇到 2 次以上的数据来近似捕获 frequency 信息是一大亮点。动态平衡的方案直觉上有效,但是细节上缺乏有力的论证,不确定是不是一个最优的算法。对于动态变化的数据而言,动态平衡的算法优于静态的算法是可以预期的。

References

  • [1] MEGIDDO N, MODHA D S. ARC: A Self-Tuning, Low Overhead Replacement Cache[C]//CHASE J. Proceedings of the {FAST} ’03 Conference on File and Storage Technologies, March 31 - April 2, 2003, Cathedral Hill Hotel, San Francisco, California, {USA}. USENIX, 2003.
  • [2] NIMROD MEGIDDO, MODHA D S. one up on LRU[J]. ;Login:, 2003, 28(4).
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值