linux cache lru回收,LRU cache 算法

上周末同学问了一些操作系统的问题,涉及到LRU cache,顺便复习了一下。

LRU是least recently used的缩写,意思是最近最少使用,是一种内存页面置换算法。根据程序设计局部性的原则,如果一个页面在最近的一段时间一直没有被使用,那这个页面在将来一段时间内不会被用到的概率就比较大;同样的,如果一个页面最近频繁的被使用,那它在接下来也有很大概率会被用到(例如循环)。

LRU 算法的基本原理很简单,为每个物理页面绑定一个计数器,用以标识该页面的访问频度。操作系统内核进行页面回收的时候就可以根据页面的计数器的值来确定要回收哪些页面。然而,在硬件上提供这种支持的体系结构很少,Linux 操作系统没有办法依靠这样一种页计数器去跟踪每个页面的访问情况,所以,Linux 在页表项中增加了一个 Accessed 位,当页面被访问到的时候,该位就会被硬件自动置位。该位被置位表示该页面还很年轻,不能被换出去。此后,在系统的运行过程中,该页面的年龄会被操作系统更改。在 Linux 中,相关的操作主要是基于两个 LRU 链表以及两个标识页面状态的标志符。

后来Leetcode根据LRU cache出了一道【146. LRU Cache】,也因此使得这个算法被更多非体系结构方向的人所知。不过个人感觉这道#146的题目远没有Hard难度。

背景介绍到此结束,先看题:

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get

and set.

get(key)

Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.set(key, value)

Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

这道题的就是要实现一个LRU支持set和get的函数,分析一下:

如果直接用数组来实现LRU,数组的每个位置除了保存数据外维护一个时间戳,这样做比较简单,但是插入、删除都会以O(n)的复杂度去维护这个时间戳。

更常用的做法是使用链表和HashMap。每次访问数据线检查HashMap,如果命中,则通过hash函数找到在链表的位置,pop出来追加到表尾;如果没命中,则取决于当前链表长度,如果长度小于capacity,直接追加到表尾,如果长度已经达到上限了,则要把表头元素pop出去,并把新元素追加到表尾,最后更新HashMap。

啊,下面是code时间,最近写Python比较多,还是以Python实现为例吧(可能真实情况是...半年没用过C++,代码不会写了/(ㄒoㄒ)/~~)。

首先是链表节点的结构,比较经典,记住就好了:

class LinkedNode(object):

def __init__(self, key=None, value=None, next=None):

self.key = key

self.value = value

self.next = next

接下来是LRU的对象,应该保存一个哈希表(对于python来说常用的实现好像就是字典TAT),一对表头、表尾,还要有一个标记cache容量的capacity变量:

class LRUCache(object):

def __init__(self, capacity):

self.hash_table = {}

self.head = LinkedNode()

self.tail = self.head

self.capacity = capacity

数据结构实现之后应该清楚出了题目要求的get和set,应该实现一个visit()函数以及_pop()、_push()两个内部工具函数,其中当cache容量满了请求未命中的时候,_pop()负责把表头元素弹出去,类似的,_push(node)将未命中的元素追加到表尾。

def push(self, node):

self.hash_table[node.key] = self.tail

self.tail.next = node

self.tail = node

def pop(self):

del self.hash_table[self.head.next.key]

self.head.next = self.head.next.next

self.hash_table[self.head.next.key] = self.head

def visit(self, prev):

node = prev.next

if node == self.tail:

return

prev.next = node.next

if node.next:

self.hash_table[node.next.key] = prev

node.next = None

self.push(node)

def get(self, key):

if key not in self.hash_table.keys():

return -1

self.visit(self.hash_table[key])

return self.hash_table[key].next.value

def set(self, key, value):

if key in self.hash_table.keys():

self.visit(self.hash_table[key])

self.hash_table[key].next.value = value

else:

self.push(LinkedNode(key, value))

if len(self.hash_table) > self.capacity:

self.pop()

leetcode提交后跑了1296 ms...果然还是比cxx慢好多...

然后给出用C++ standard STL的实现版本,63ms:

class LRUCache{

size_t m_capacity;

unordered_map>::iterator> m_map; //m_map_iter->first: key, m_map_iter->second: list iterator;

list> m_list; //m_list_iter->first: key, m_list_iter->second: value;

public:

LRUCache(size_t capacity):m_capacity(capacity) {

}

int get(int key) {

auto found_iter = m_map.find(key);

if (found_iter == m_map.end()) //key doesn't exist

return -1;

m_list.splice(m_list.begin(), m_list, found_iter->second); //move the node corresponding to key to front

return found_iter->second->second; //return value of the node

}

void set(int key, int value) {

auto found_iter = m_map.find(key);

if (found_iter != m_map.end()) //key exists

{

m_list.splice(m_list.begin(), m_list, found_iter->second); //move the node corresponding to key to front

found_iter->second->second = value; //update value of the node

return;

}

if (m_map.size() == m_capacity) //reached capacity

{

int key_to_del = m_list.back().first;

m_list.pop_back(); //remove node in list;

m_map.erase(key_to_del); //remove key in map

}

m_list.emplace_front(key, value); //create new node in list

m_map[key] = m_list.begin(); //create correspondence between key and node

}

};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值