快取演算法(頁面置換演算法) FIFO LFU LRU

2021-07-24 20:38:22 字數 2653 閱讀 3436

1. fifo -- 先進先出

如果乙個資料最先進入快取中,則應該最早淘汰掉。也就是說,當快取滿的時候,應當把最先進入快取的資料給淘汰掉。

實現:利用乙個雙向鍊錶儲存資料,當來了新的資料之後便新增到鍊錶末尾,如果cache存滿資料,則把鍊錶頭部資料刪除,然後把新的資料新增到鍊錶末尾。在訪問資料的時候,如果在cache中存在該資料的話,則返回對應的value值;否則返回-1。如果想提高訪問效率,可以利用hashmap來儲存每個key在鍊錶中對應的位置。

2. lfu -- 最近最少使用

基於「如果乙個資料在最近一段時間內使用次數很少,那麼在將來一段時間內被使用的可能性也很小」的思路。

lfu是基於訪問次數的。

實現:為了能夠淘汰最少使用的資料,lfu演算法最簡單的一種設計思路就是利用乙個陣列儲存資料項,用hashmap儲存每個資料項在陣列中對應的位置,然後為每個資料項設計乙個訪問頻次,當資料項被命中時,訪問頻次自增,在淘汰的時候淘汰訪問頻次最少的資料。這樣一來的話,在插入資料和訪問資料的時候都能達到o(1)的時間複雜度,在淘汰資料的時候,通過選擇演算法得到應該淘汰的資料項在陣列中的索引,並將該索引位置的內容替換為新來的資料內容即可,這樣的話,淘汰資料的操作時間複雜度為o(n)。

另外還有一種實現思路就是利用小頂堆+hashmap,小頂堆插入、刪除操作都能達到o(logn)時間複雜度,因此效率相比第一種實現方法更加高效。

3. lru -- 最近最久未使用

如果乙個資料在最近一段時間沒有被訪問到,那麼在將來它被訪問的可能性也很小。也就是說,當限定的空間已存滿資料時,應當把最久沒有被訪問到的資料淘汰。

實現:(1)用乙個陣列來儲存資料,給每乙個資料項標記乙個訪問時間戳,每次插入新資料項的時候,先把陣列中存在的資料項的時間戳自增,並將新資料項的時間戳置為0並插入到陣列中。每次訪問陣列中的資料項的時候,將被訪問的資料項的時間戳置為0。當陣列空間已滿時,將時間戳最大的資料項淘汰。

思路簡單,但是需要不停地維護資料項的訪問時間戳,另外,在插入資料、刪除資料以及訪問資料時,時間複雜度都是o(n)。

(2)利用鍊錶和hashmap。當需要插入新的資料項的時候,如果新資料項在鍊錶中存在(一般稱為命中),則把該節點移到鍊錶頭部;如果不存在,則新建乙個節點,放到鍊錶頭部。若快取滿了,則把鍊錶最後乙個節點刪除即可。在訪問資料的時候,如果資料項在鍊錶中存在,則把該節點移到鍊錶頭部,否則返回-1。這樣一來在鍊錶尾部的節點就是最近最久未訪問的資料項。

在已知要刪除的節點的情況下,如何在o(1)時間複雜度內刪除節點?

假如要刪除的節點是cur,通過cur可以知道cur節點的後繼節點curnext,如果交換cur節點和curnext節點的資料域,然後刪除curnext節點(curnext節點是很好刪除地),此時便在o(1)時間複雜度內完成了cur節點的刪除。

如何使得刪除末尾節點的複雜度也在o(1)?

利用雙向鍊錶,並提供head指標和tail指標,這樣一來,所有的操作都是o(1)時間複雜度。

參考實現:

(1)#include #include #include using namespace std;

struct node;

class lrucache

int get(int key)

else

return cachehead->value;

}void set(int key, int value)

else //否則,在map中查詢

else

}else

}else}}

void pushfront(node *cur) //雙向鍊錶刪除節點,並將節點移動鍊錶頭部,o(1)

cur->pre->next = cur->next; //刪除節點

if(cur->next!=null)

cur->next->pre = cur->pre;

cur->next = cachehead;

cur->pre = null;

cachehead->pre = cur;

cachehead = cur;

}void printcache()

cout<(2)用stl的list實現雙向鍊錶

#include #include #include #include using namespace std;

struct node;

class lrucache

int get(int key)

else //在cache中命中了

return cachelist.begin()->value;

}void set(int key, int value)

node newnode;

newnode.key = key;

newnode.value = value;

cachelist.push_front(newnode);

mp[key] = cachelist.begin();

}else //命中

}};

int main(void)

{ lrucache cache(3);

cache.set(1,1);

cache.set(2,2);

cache.set(3,3);

cache.set(4,4);

cout<

快取演算法(頁面置換演算法) FIFO LFU LRU

快取演算法 頁面置換演算法 fifo lfu lru 在前一篇文章中通過leetcode的一道題目了解了lru演算法的具體設計思路,下面繼續來 一下另外兩種常見的cache演算法 fifo lfu fifo first in first out 先進先出。其實在作業系統的設計理念中很多地方都利用到了...

快取演算法(頁面置換演算法) FIFO LFU LRU

fifo first in first out 先進先出。其實在作業系統的設計理念中很多地方都利用到了先進先出的思想,比如作業排程 先來先服務 為什麼這個原則在很多地方都會用到呢?因為這個原則簡單 且符合人們的慣性思維,具備公平性,並且實現起來簡單,直接使用資料結構中的佇列即可實現。在fifo ca...

快取演算法(頁面置換演算法) FIFO LFU LRU

在前一篇文章中通過leetcode的一道題目了解了lru演算法的具體設計思路,下面繼續來 一下另外兩種常見的cache演算法 fifo lfu fifo first in first out 先進先出。其實在作業系統的設計理念中很多地方都利用到了先進先出的思想,比如作業排程 先來先服務 為什麼這個原...