我之前碰巧使用过 Simplescalar。其实Simplescalar已经实现了真正的LRU算法。
以下注释清楚地描述了函数 update_way_list。
/* insert BLK into the order way chain in SET at location WHERE */
static void
update_way_list(struct cache_set_t *set, /* set contained way chain */
struct cache_blk_t *blk, /* block to insert */
enum list_loc_t where) /* insert location */
您引用的代码来自访问缓存时的“缓存未命中”案例:
switch (cp->policy) {
case LRU:
case FIFO:
repl = cp->sets[set].way_tail;
update_way_list(&cp->sets[set], repl, Head);
break;
}
这里选择集合的最后一条路径作为牺牲品,并将其移动到集合的头部。
稍后,被替换的块数据被写回,然后受害者被新的数据块替换。
区分LRU和FIFO最重要的部分来自“缓存命中”的情况:
/* if LRU replacement and this is not the first element of list, reorder */
if (blk->way_prev && cp->policy == LRU)
{
/* move this block to head of the way (MRU) list */
update_way_list(&cp->sets[set], blk, Head);
}
因此,集合中的方式遵循年龄递减顺序:集合的头部是 MRU(最近使用)块,尾部是 LRU。
这正是真正的 LRU 算法:当有缓存命中时,命中块被提升为 MRU 方式,同时保持其他的顺序。当缓存未命中时,LRU 块被选为牺牲品,新块被放置在 MRU 方式中。如果我们删除之前的“缓存命中”代码,则不会记录访问历史记录,并且集合中的方式遵循访问顺序,从而提供 FIFO 行为。如果我们删除该行
update_way_list(&cp->sets[set], repl, Head);
在前面的“cache miss”代码中,那么新的block会以LRU方式放置,从而提供LIP(LRU Insertion Policy)行为。