什麼是 LRU
LRU Cache是一個Cache的置換演算法,含義是“最近最少使用”,把滿足“最近最少使用”的資料從Cache中剔除出去,並且保證Cache中第一個資料是最近剛剛訪問的,因為這樣的資料更有可能被接下來的程式所訪問。
LRU的應用比較廣泛,最基礎的記憶體頁置換中就用了,對了,這裡有個概念要清楚一下,Cache不見得是CPU的快取記憶體的那個Cache,這裡的Cache直接翻譯為快取,就是兩種儲存方式的速度有比較大的差別,都可以用Cache快取資料,比如硬碟明顯比記憶體慢,所以常用的資料我們可以Cache在記憶體中。
LRU 基本演算法描述
前提:
由於我只是簡單實現一下這個演算法,所以資料都用int代替,下一個版本會改成模板形式的,更加通用。
要求:
- 只提供兩個介面,一個獲取資料getValue(key),一個寫入資料putValue(key,value)
- 無論是獲取還是寫入資料,當前這個資料要保持在最容易訪問的位置
- 快取數量有限,最長時間沒被訪問的資料應該置換出快取
演算法:
為了滿足上面幾個條件,實際上可以用一個雙向連結串列來實現,每次訪問完資料(不管是獲取還是寫入),調整雙向連結串列的順序,把剛剛訪問的資料調整到連結串列的最前方,以後再訪問的時候速度將最快。
為了方便,提供一個頭和一個尾節點,不存具體的數,連結串列的基本形式如下面的這個簡單表述
Head <===> Node1 <===> Node2 <===> Node3 <===> Near
OK,就這麼些,比較簡單,實現起來也不難,用c++封裝一個LRUCache類,類提供兩個方法,分別是獲取和更新,初始化類的時候傳入Cache的節點數。
先定義一個存資料的節點資料結構
1 2 3 4 5 6 7 8 9 |
typedef struct _Node_{ int key; //鍵 int value; //資料 struct _Node_ *next; //下一個節點 struct _Node_ *pre; //上一個節點 }CacheNode; |
類定義:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class LRUCache{ public: LRUCache(int cache_size=10); //建構函式,預設cache大小為10 ~LRUCache();<span style="white-space:pre"> </span> //解構函式 int getValue(int key); //獲取值 bool putValue(int key,int value); //寫入或更新值 void displayNodes(); //輔助函式,顯示所有節點 private: int cache_size_; //cache長度 int cache_real_size_; //目前使用的長度 CacheNode *p_cache_list_head; //頭節點指標 CacheNode *p_cache_list_near; //尾節點指標 void detachNode(CacheNode *node); //分離節點 void addToFront(CacheNode *node); //將節點插入到第一個 }; |
類實現:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
LRUCache::LRUCache(int cache_size) { cache_size_=cache_size; cache_real_size_=0; p_cache_list_head=new CacheNode(); p_cache_list_near=new CacheNode(); p_cache_list_head->next=p_cache_list_near; p_cache_list_head->pre=NULL; p_cache_list_near->pre=p_cache_list_head; p_cache_list_near->next=NULL; } LRUCache::~LRUCache() { CacheNode *p; p=p_cache_list_head->next; while(p!=NULL) { delete p->pre; p=p->next; } delete p_cache_list_near; } void LRUCache::detachNode(CacheNode *node) { node->pre->next=node->next; node->next->pre=node->pre; } void LRUCache::addToFront(CacheNode *node) { node->next=p_cache_list_head->next; p_cache_list_head->next->pre=node; p_cache_list_head->next=node; node->pre=p_cache_list_head; } int LRUCache::getValue(int key) { CacheNode *p=p_cache_list_head->next; while(p->next!=NULL) { if(p->key == key) //catch node { detachNode(p); addToFront(p); return p->value; } p=p->next; } return -1; } bool LRUCache::putValue(int key,int value) { CacheNode *p=p_cache_list_head->next; while(p->next!=NULL) { if(p->key == key) //catch node { p->value=value; getValue(key); return true; } p=p->next; } if(cache_real_size_ >= cache_size_) { cout << "free" <<endl; p=p_cache_list_near->pre->pre; delete p->next; p->next=p_cache_list_near; p_cache_list_near->pre=p; } p=new CacheNode();//(CacheNode *)malloc(sizeof(CacheNode)); if(p==NULL) return false; addToFront(p); p->key=key; p->value=value; cache_real_size_++; return true; } void LRUCache::displayNodes() { CacheNode *p=p_cache_list_head->next; while(p->next!=NULL) { cout << " Key : " << p->key << " Value : " << p->value << endl; p=p->next; } cout << endl; } |
說在後面的話
其實,程式還可以優化,首先,把資料int型別換成模板形式的通用型別,另外,資料查詢的時候複雜度為O(n),可以換成hash表來存資料,連結串列只做置換處理,這樣查詢新增的時候速度將快很多。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!