LRU快取淘汰演算法分析與實現
阿新 • • 發佈:2019-02-07
概述
記錄一下LRU快取淘汰演算法的實現。
原理
LRU(Least recently used,最近最少使用)快取演算法根據資料最近被訪問的情況來進行淘汰資料,其核心思想是“如果資料最近被訪問過,那麼將來被訪問的機率也更高”。
介紹
下圖中,介紹了一個快取空間為5的快取佇列,當訪問資料的順序是:1,2,3,4,5,6,7,6,4,0時空間中資料的變化過程。
可以發現:
1. 當快取空間未滿時,資料一直往新的空間寫;
2. 當快取滿,並且快取中沒有需要訪問的資料時,最先進入快取的資料被淘汰掉;
3. 當快取滿,並且快取中有需要訪問的資料時,做了一個數據交換,把訪問的資料拿出來,其餘資料往下壓,最後把訪問的資料放到頂部
在這裡,可能有疑問,就是把“資料交換”於“資料完全新增和刪除”有什麼區別呢?答案是效能,前者是移動指標,後者是更新整個記憶體空間,後者所花費的系統開銷遠比前者大得多。
實現
看了演算法的介紹,我們想到的資料結構就是連結串列了。
- 雙向連結串列的資料結構
/**
* 雙向連結串列資料結構
*/
private class NodePair{
NodePair frontNode;
NodePair postNode;
int data;
}
- 逆序查詢連結串列
/**
* 根據資料逆序查詢連結串列中是否有此節點,有,則把該點提出來,放到current的位置
* 當匹配到的時候,返回true
* @param data
*/
public boolean searchNode(int data){
boolean flag = false;
NodePair tempNode = current;
//不匹配,即沒找到,則繼續查詢
while(tempNode.frontNode != null || tempNode.data != data){
tempNode = tempNode.frontNode;
}
//這個判讀表示匹配到了
if(tempNode.data == data){
tempNode.frontNode.postNode = tempNode.postNode;
tempNode.postNode.frontNode = tempNode.frontNode;
current = tempNode;
flag = true ;
}
return flag;
}
- 空間滿了,並且快取中沒有待訪問的資料,刪除最下面的節點,再新增一個節點,相當於重新賦值最下面的節點,如圖
紅線表示,head將要指向倒數第二個點了,即,倒數第二個點要變成現在最底下的點了。
/**
* 給head節點重新賦值操作
* 實現細節是:
* 0.倒數第二個點(head的下一個點)的frontNode引用指向null
* 1.給head所指節點重新賦值
* 2.current節點的frontNode引用指向head
* 3.把current節點指向head
* 4.把head指向head的下一個節點(即,倒數第二個點)
*/
public void resetHeadNode(int data){
NodePair secondNode = head.postNode;
head.postNode.frontNode = null;
head.data = data;
head.frontNode = current;
head.postNode = null;
current.postNode = head;
current = head;
head = secondNode;
}
- 快取滿了,查詢快取中是否有待訪問資料,有的話,同時把有的資料放到current指標所指位置。
/**
* 根據資料逆序查詢連結串列中是否有此節點,有,則把該點提出來,放到current的位置
* 當匹配到的時候,返回true
* @param data
*/
public boolean searchNode(int data){
boolean flag = false;
NodePair tempNode = current;
//不匹配,即沒找到,則繼續查詢
while(tempNode.frontNode != null || tempNode.data != data){
tempNode = tempNode.frontNode;
}
//這個判讀表示匹配到了
if(tempNode.data == data){
tempNode.frontNode.postNode = tempNode.postNode;
tempNode.postNode.frontNode = tempNode.frontNode;
current = tempNode;
flag = true;
}
return flag;
}
- 新增節點
/**
* 往LRU快取中插入資料
* @param data
*/
public void addNode(int data){
//快取未滿,不需要刪除,直接插入
if(length <= size){
NodePair tempNode = new NodePair();
tempNode.frontNode = current;
tempNode.postNode = null;
tempNode.data = data;
current = tempNode;
length++;
}
//快取滿了,查詢快取中有沒有資料
else{
if(!searchNode(data)){
//快取中沒有,需要給head節點重新賦值
resetHeadNode(data);
}
}
}
LRU演算法的缺點
如果有幾個不符合“如果資料最近被訪問過,那麼將來被訪問的機率也更高”的規律時,會破壞快取,導致效能下降。
總結
寫演算法時,通過畫圖,寫步驟,先產生一個清晰的思路,然後一步步去做實現剛才思考的步驟。