页面置换算法LRU和LFU的C++实现

1,996 阅读3分钟

缓存算法:页面置换算法

在地址映射过程中,若在页面中发现所要访问的页面不在内存中,则产生缺页中断。当发生缺页中断时,如果操作系统内存中没有空闲页面,则操作系统必须在内存选择一个页面将其移出内存,以便为即将调入的页面让出空间。而用来选择淘汰哪一页的规则叫做页面置换算法。

  • FIFO: FIFO(First in First out),先进先出。其实在操作系统的设计理念中很多地方都利用到了先进先出的思想,比如作业调度(先来先服务),为什么这个原则在很多地方都会用到呢?因为这个原则简单、且符合人们的惯性思维,具备公平性,并且实现起来简单,直接使用数据结构中的队列即可实现。在FIFO Cache设计中,核心原则就是:如果一个数据最先进入缓存中,则应该最早淘汰掉。
  • LRU: LRU全称是Least Recently Used,即最近最久未使用的意思。如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。
  • LFU: LFU(Least Frequently Used)最近最少使用算法。它是基于“如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小”的思路。
AlgorithmComment
OptimalNot 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 chanceBig improvement over FIFO
ClockRealistic
LRU (Least Recently Used)Excellent, but difficult to implement exactly
NFU (Not Frequently Used)Fairly crude approximation to LRU
AgingEfficient algorithm that approximates LRU well
Working setSomewhat expensive to implement
WSClockGood efficient algorithm

LRU

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

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);
 */