【体系结构】缓存替换策略,用O(n)位实现的近似 LRU算法【Cache replacement: Pseudo LRU】

前言

       首先,你需要了解E-ways组关联缓存的组织以及普通LRU替换算法。本文介绍一种,【近似】的LRU算法,它是借助一颗完全二叉树实现的。

       考虑一个set中有n行的缓存(n-ways)。它们最近访问的时间有n!种组合(就是将它们按照最近访问时间排序)。比如3-ways的缓存,各行为ABC,就有ABC,ACB,BCA,BAC,CAB,CBA 6种。注意:我们不能简单地记录LRU的行,比如,CBAAB这个访问序列,我们需要记录(CAB)而不是(C),以便之后有新的访问后对该序列更新。显然,对每一个set我们需要O(log n!)=O(nlogn)位记录。

Pseudo LRU:

以4-ways 为例,一个set中有ABCD四行。

1)LRU的算法用一颗二叉树表示:

表示L2L1L0替换L2L1L0
Ax11 x00
Bx01 x10
C1x0 0x1
D0x0 1x1

 

①本来4-ways有24种排列,我们需要5位来维护,现在我们只用3位(L2,L1,L0)来维护。注意,我们不需要每一行都存这个二叉树。对于一个n-ways的缓存,不妨设n=2^t,二叉树需要t层,共有节点(2^t-1)个,也就是说我们只需要记录n-1位。

②如果最新访问了A行我们就按上表中【表示】来更新L2,L1,L0,x表示不更新。

③当我们要需要替换一行时,根据上表中【替换】来决定替换哪一行,比如,(1,1,0)表示替换B行。

④【替换】其实是【表示】取反。

具体的算法二叉树怎么画是随意的,比如你可以用L0=1表示AD,L0=0表示BC,只要这棵树没有逻辑错误就行。

2)实例:初始化(0,0,0),现在有一个访问序列ABCDACDAB,让我们来比较一下Pseudo LRU和LRU算法的结果。

 (L2,L1,L0)Pseudo LRULRU 
A011   
B001   
C100   
D000AA
A011CBC虽然不是最长时间未访问的,但是是“第二”最长时间未访问的
C110BB
D010BB
A011CBC虽然不是最长时间未访问的,但是是“第二”最长时间未访问的
B001CC

3)总结来说,我们每次更新一些位数,而保留一些位数,使得任一时刻(L2,L1,L0)既包含着最新访问的信息,又包含着过去访问的信息。并且直观来说,因为【替换】是【表示】的取反,我们一定不会替换掉最新访问的行。所以,我们要么成功地替换了和严格LRU算法相同的行,要么替换了一个相对而言比较长时间没有用的行。考虑到LRU并不一定是最优的决定,Pserdo LRU在研究实验中整体而言表现优秀。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的 Verilog 实现,其中使用了一个 LRU 计数器来确定要替换缓存行: ``` module Cache ( input [31:0] addr, input [31:0] data_in, input wr_en, output [31:0] data_out ); parameter NUM_SETS = 256; parameter NUM_LINES = 4; parameter NUM_WORDS = 8; parameter TAG_WIDTH = 20; reg [TAG_WIDTH-1:0] tag [NUM_SETS-1:0][NUM_LINES-1:0]; reg [NUM_WORDS*8-1:0] data [NUM_SETS-1:0][NUM_LINES-1:0]; reg [NUM_LINES-1:0] lru [NUM_SETS-1:0]; wire [NUM_WORDS*8-1:0] data_out; assign data_out = data[addr[NUM_SETS+1:3]][lru[addr[NUM_SETS+1:3]]]; always @ (posedge clk) begin if (wr_en) begin int set_index = addr[NUM_SETS+1:3]; int line_index = 0; int lru_line = 0; int lru_count = 0; for (int i = 0; i < NUM_LINES; i = i + 1) begin if (tag[set_index][i] == addr[TAG_WIDTH+NUM_SETS+2:NUM_SETS+3]) begin line_index = i; break; end if (lru[set_index][i] > lru_count) begin lru_count = lru[set_index][i]; lru_line = i; end end if (line_index != 0) begin tag[set_index][line_index] = addr[TAG_WIDTH+NUM_SETS+2:NUM_SETS+3]; data[set_index][line_index] = data_in; lru[set_index][line_index] = 0; for (int i = 0; i < NUM_LINES; i = i + 1) begin if (i != line_index) begin lru[set_index][i] = lru[set_index][i] + 1; end end end else begin tag[set_index][lru_line] = addr[TAG_WIDTH+NUM_SETS+2:NUM_SETS+3]; data[set_index][lru_line] = data_in; lru[set_index][lru_line] = 0; for (int i = 0; i < NUM_LINES; i = i + 1) begin if (i != lru_line) begin lru[set_index][i] = lru[set_index][i] + 1; end end end end end endmodule ``` 这个实现为每个组维护了一个 LRU 计数器,用于确定要替换缓存行。当写入数据时,它会查找与地址匹配的缓存行,如果找到则更新缓存行的标记、数据和 LRU 计数器。如果没有找到匹配的缓存行,则使用 LRU 计数器确定要替换缓存行,并将新数据写入该行。注意,这个实现是为了说明 LRU 替换算法而设计的,它可能需要根据实际需求进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值