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
}
};
linux cache lru回收,LRU cache 算法相关推荐
- buffer cache中的LRU链+
转自晶晶小妹的博客:http://space.itpub.net/13095417/viewspace-204007 一.LRU链介绍 HASH是快速查找时,常用的算法.Oracle中几乎在所有需要快 ...
- linux强制回收内存,Linu系统cache强制回收
LINUX的内存管理机制,一般情况下不需要特意去释放已经使用的cache.Cache机制的存在,使得Linux对磁盘的读写速度是有较大的好处的. 在 Linux 操作系统中,当应用程序需要读取文件中的 ...
- linux那些事之LRU(4)
当物理内存实际比较紧张时,内存水位处于较低water level时,会触发间接回收内存kswapd或者直接回收内存,从inactive LRU list中将不常用的内存 置换到内存中,整个调用过程大概 ...
- linux那些事之LRU(3)
继续承接<linux那些事之LRU(2)>,shrink_active_list()函数中需要将调用isolate_lru_pages函数将active LRU中从链表尾部隔离出nr_to ...
- linux那些事之LRU(1)
一个物理页被分配之后,page其所挂载的链表从buddy空闲链表中摘除,会进入到active 或者inactive链表中,当物理内存water mark存在压力时会根据LRU(least recent ...
- Ehcache 1.5.0 User Guide - Cache Eviction Algorithms (缓存移出算法)
Ehcache 1.5.0 User Guide - Cache Eviction Algorithms (缓存移出算法) Ehcache 1.5.0 用户指南) E_mail:jianglike1 ...
- linux那些事之page cache
page cache page cache又称高速缓存,主要是针对文件文件系统,为了减少不必要的磁盘IO操作(读/写)造成卡顿问题,内核将磁盘文件中的内容缓存到内存中,并选择适当时机对磁盘进行读写操作 ...
- linux内核学习6:Linux的CPU高速缓存cache和页高速缓存cache,buffer
一.CPU高速缓存(cache) 参考:https://blog.csdn.net/u014470361/article/details/80060701 参考:https://blog.csdn.n ...
- lru调度算法例题_lru算法(lru算法及例题讲解)
lru算法参考例子如下: include using namespace std; int ans[1000]://存放原序列 int block[1000]://机器分给程序的内存块 int num ...
最新文章
- (转)Python 用hashlib求中文字符串的MD5值
- Java 的 IO 流
- 判断iframe是否加载完毕的方法(兼容ie和Firefox)
- 当初互联网大佬给的几块钱“羊毛”,现在又要我们加倍还回去!
- JS~~~ 前端开发一些常用技巧 模块化结构 命名空间处理 奇技淫巧!!!!!!...
- codeforces-1201 C Maximum Median
- 《Reids 设计与实现》第十一章 服务器
- 测试一个链表是否为空表 C语言,【链表测试面试题】面试问题:C语言单链表的… - 看准网...
- C盘\用户目录下\管理员文件夹 如何重命名?
- 接口解读:你的姓名+身份证号撑起了超11亿元市场规模,你造吗?
- 美赛数模论文之表的格式
- 一些U盘启动盘电脑热键的对应关系
- 斜体(oblique italic)
- 分布式缓存(Redis)连杀
- 毕业设计 单片机智能药箱系统 - STM32
- 返回上一步编辑或者上几步或者想恢复删除的代码
- Matlab 基于遗传算法优化的VMD信号去噪算法 创新点:基于样本熵作为适应度函数
- 微信小程序注册与登录
- Java初学者入门简介
- wow服务器维护8月14,2008年8月14日维护公告
热门文章
- java中synchronized修饰静态方法和非静态方法有什么区别?
- 传统jdbc存在的问题总结
- pyqt5如何循环遍历控件名_如何用 PyQt5 快速构建一个简单的 GUI 应用
- cdialog创建后马上隐藏_都2020了,你还没用上这些Windows的“隐藏神技能”?
- mmap和shm共享内存的区别和联系
- python 模拟键盘_python+selenium模拟键盘输入
- mybatis sql标签_这谁顶得住?Mybatis 十八连环问
- php对象持久化,在 Oracle 中完成 PHP5 对象的持久
- PHP程序员的学习路线
- Java项目实训报告