缓存算法:页面置换算法
在地址映射过程中,若在页面中发现所要访问的页面不在内存中,则产生缺页中断。当发生缺页中断时,如果操作系统内存中没有空闲页面,则操作系统必须在内存选择一个页面将其移出内存,以便为即将调入的页面让出空间。而用来选择淘汰哪一页的规则叫做页面置换算法。
- FIFO: FIFO(First in First out),先进先出。其实在操作系统的设计理念中很多地方都利用到了先进先出的思想,比如作业调度(先来先服务),为什么这个原则在很多地方都会用到呢?因为这个原则简单、且符合人们的惯性思维,具备公平性,并且实现起来简单,直接使用数据结构中的队列即可实现。在FIFO Cache设计中,核心原则就是:如果一个数据最先进入缓存中,则应该最早淘汰掉。
- LRU: LRU全称是Least Recently Used,即最近最久未使用的意思。如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。
- LFU: LFU(Least Frequently Used)最近最少使用算法。它是基于“如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小”的思路。
| Algorithm | Comment |
|---|---|
| Optimal | Not implementable, but useful as a benchmark |
| NRU (Not Recently Used) | Very crude approximation of LRU |
| FIFO (First-In, First-Out) | Might throw out important pages |
| Second chance | Big improvement over FIFO |
| Clock | Realistic |
| LRU (Least Recently Used) | Excellent, but difficult to implement exactly |
| NFU (Not Frequently Used) | Fairly crude approximation to LRU |
| Aging | Efficient algorithm that approximates LRU well |
| Working set | Somewhat expensive to implement |
| WSClock | Good efficient algorithm |
LRU
// 哈希表和双向链表
struct myNode{
int key;
int value;
myNode* prev;
myNode* next;
myNode(int _key,int _value):key(_key),value(_value),prev(NULL),next(NULL){}
};
class LRUCache {
public:
unordered_map<int,int> um; //<key,value>
int size;
int capacity;
myNode* head;
myNode* tail;
LRUCache(int _capacity):capacity(_capacity) {
size=0;
head=new myNode(-1,-1); //伪头部和伪尾部
tail=new myNode(-1,-1);
head->next=tail;
tail->prev=head;
}
int get(int key) {
if(um.find(key)==um.end())return -1;
myNode* current=head->next;
while(current!=tail)
{
if(current->key==key)break;
current=current->next;
}
current->prev->next=current->next;
current->next->prev=current->prev;
head->next->prev=current;
current->next=head->next;
head->next=current;
current->prev=head;
return um[key];
}
void put(int key, int value) {
if(um.find(key)!=um.end())
{
um[key]=value;
myNode* current=head->next;
while(current!=tail)
{
if(current->key==key)break;
current=current->next;
}
current->value=value;
current->prev->next=current->next;
current->next->prev=current->prev;
head->next->prev=current;
current->next=head->next;
head->next=current;
current->prev=head;
}
else
{
um[key]=value;
myNode* current=new myNode(key,value);
head->next->prev=current;
current->next=head->next;
head->next=current;
current->prev=head;
size++;
}
if(size>capacity)
{
myNode* current=tail->prev;
current->prev->next=tail;
tail->prev=current->prev;
um.erase(current->key);
size--;
delete current;
current=NULL;
}
}
};
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache* obj = new LRUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/
LFU
\\ 哈希表和红黑树(set)
struct myNode
{
int key;
int value;
int cnt; // 出现次数
int ts; //timestamp, 时间戳,最新一次的出现时间
bool operator<(const myNode&rhs) const{
return (cnt==rhs.cnt)?ts<rhs.ts:cnt<rhs.cnt;
}
myNode(int _key,int _value,int _cnt,int _ts):key(_key),value(_value),cnt(_cnt),ts(_ts){}
};
class LFUCache {
public:
unordered_map<int,myNode> um;
set<myNode> se;
int curTime=0;
int size;
int capacity;
LFUCache(int _capacity):capacity(_capacity),size(0),curTime(0){
se.clear();
um.clear();
}
int get(int key) {
auto iter=um.find(key);
if(iter==um.end())return -1;
myNode res=iter->second;
se.erase(res);
res.ts=++curTime;
res.cnt++;
se.insert(res);
iter->second=res;
return res.value;
}
void put(int key, int value) {
if(capacity==0)return;
auto iter=um.find(key);
if(iter!=um.end())
{
myNode res=iter->second;
se.erase(res);
res.value=value;
res.ts=++curTime;
res.cnt++;
se.insert(res);
iter->second=res;
}
else
{
size++;
if(size>capacity)
{
auto beg=se.begin();
um.erase(beg->key);
se.erase(beg);
size--;
}
myNode current=myNode(key,value,1,++curTime);
um.insert(make_pair(key,current));
se.insert(current);
}
}
};
/**
* Your LFUCache object will be instantiated and called as such:
* LFUCache* obj = new LFUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/