Cache满载的LRU置换2
https://my.oschina.net/kkkkkkkkkkkkk/blog/710261
是这篇的改进型,时间复杂度修改为了O(1) 双向链表和 hash实现
hash实现的话 不利于大量存储,大量的话需要修改为std::map 时间复杂度就变为0(log(n)) 了
/* * Author: caoshanshan * Email: me@dreamyouxi.com LRU for manage cache */ #pragma once #include "global_def.h" #include <iostream> #include <unordered_map> #include <vector> #include "base/memory.h" #include <assert.h> #if _DEBUG //debug check void LRUIncNodeRefCount(); void LRUDecNodeRefCount(); void LRUIncRefCount(); void LRUDecRefCount(); void LRUCheckRefCount(); #endif /** * @brief LRU for cache ,you can use this to manage your cache-data * @note do not copy this */ //内部结构采用 std::unordered_map 不适用于 数量特别大的 结构 //大的结构需要用std::map 了 std::map 后面有需要的时候 再加吧 template<typename Key> class LRUHashMap { //double-linked list for LRU class delist_node { public: delist_node() { #if _DEBUG LRUIncNodeRefCount(); #endif } delist_node(const delist_node&other) = delete; ~delist_node() { #if _DEBUG LRUIncNodeRefCount(); #endif } delist_node *pre = nullptr; delist_node*next = nullptr; Key data; }; public: LRUHashMap() { #if _DEBUG LRUIncRefCount(); #endif } //disallow copy ctor LRUHashMap(const LRUHashMap&other) = delete; ~LRUHashMap() { #if _DEBUG Check(); LRUDecRefCount(); #endif while (DeleteTail() > 1); //head may be is equal to tail if (head && head != tail) { delete head; } if (tail) { delete tail; } } /** * @brief use one element key * @return elements's count in the LRU */ int Use(const Key & key) { #if _DEBUG Check(); #endif auto it_node = lru.find(key); if (it_node == lru.end()) { //has not find auto node = new delist_node(); node->data = key; node->next = head; if (head) { head->pre = node; } head = node; if (tail == nullptr) { tail = head; } lru[key] = node; #if _DEBUG Check(); #endif return lru.size(); } int size = lru.size(); delist_node *node = it_node->second; //case 1 if (size == 1) { assert(head == tail); assert(head->next == nullptr && head->pre == nullptr); head = node; tail = node; assert(node->pre == nullptr); assert(node->next == nullptr); node->next = nullptr; node->pre = nullptr; } //case 2 else if (size == 2) { assert(head != tail); assert(head->next == tail); assert(tail->pre == head); assert(head->pre == nullptr && tail->next == nullptr); if (head == node) { //use is head assert(node->pre == nullptr); } else if (node == tail) { //use is tail assert(node->next == nullptr); assert(tail != head); assert(head->next == tail); assert(head->pre == nullptr); assert(tail->pre == head); assert(tail->next == nullptr); auto tmp = head; head = tail; tail = tmp; head->next = head->pre; head->pre = nullptr; tail->pre = tail->next; tail->next = nullptr; } else { assert(false); } } //case 3 else { if (node == head) { //use is head } else if (node == tail) { //use is tail assert(node->next == nullptr); assert(node->pre != nullptr); auto tmp = tail; tail = tail->pre; tail->next = nullptr; head->pre = tmp; tmp->pre = nullptr; tmp->next = head; head = tmp; } else { assert(node->next); assert(node->pre); assert(node->next != node->pre); node->next->pre = node->pre; node->pre->next = node->next; node->pre = nullptr; head->pre = node; node->next = head; head = node; } } #if _DEBUG Check(); #endif return lru.size(); } /** * @brief delete key use */ void Delete(const Key & key) { #if _DEBUG Check(); #endif int size = lru.size(); if (size == 0) { assert(head == nullptr); assert(tail == nullptr); head = nullptr; tail = nullptr; return; } else if (size == 1) { assert(head == tail); assert(head->next == nullptr); assert(head->pre == nullptr); assert(tail->next == nullptr); assert(tail->pre == nullptr); if (head->data == key) { delete lru.begin()->second; lru.clear(); head = nullptr; tail = nullptr; } #if _DEBUG Check(); #endif return; } auto it_node = lru.find(key); if (it_node == lru.end()) { //this key is not exist ignore it } else { //has been exist delist_node *node = it_node->second; if (size == 2) { assert(head->pre == nullptr); assert(tail->next == nullptr); assert(head != tail); if (head->data == key) { //delete is head lru.erase(it_node); delete head; head = tail; tail->pre = nullptr; } else if (tail->data == key) { //delete is tail lru.erase(it_node); delete tail; tail = head; head->next = nullptr; } else { assert(false); } } else { //case 1 if (node == head) { //delete is head assert(head->next); assert(head->pre == nullptr); auto new_head = head->next; delete head; head = new_head; head->pre = nullptr; lru.erase(it_node); } else if (node == tail) { //delete is tail assert(node->next == nullptr); assert(node->pre); auto new_tail = node->pre; delete tail; lru.erase(it_node); tail = new_tail; tail->next = nullptr; } else { node->next->pre = node->pre; node->pre->next = node->next; delete node; lru.erase(it_node); } } } #if _DEBUG Check(); #endif } int Size() { return lru.size(); } int DeleteTail() { #if _DEBUG Check(); #endif if (tail) { assert(lru.find(tail->data) != lru.end()); assert(tail->next == nullptr); if (tail->pre) { assert(tail->pre->next == tail); assert(lru.size() > 1); lru.erase(tail->data); auto new_tail = tail->pre; new_tail->next = nullptr; delete tail; tail = new_tail; } else { assert(lru.size() == 1); delete tail; lru.clear(); tail = nullptr; head = nullptr; } } else { assert(lru.size() == 0); lru.clear(); if (head) { assert(false); delete head; } tail = nullptr; head = nullptr; } #if _DEBUG Check(); #endif return lru.size(); } bool HasTail() { return tail != nullptr; } /** * @brief please call this after HasTail() == true */ const Key & GetTail() { if (tail) { return tail->data; } return Key(); } #if _DEBUG void Check() { if (lru.size() == 0) { assert(head == nullptr); assert(tail == nullptr); } else if (lru.size() == 1) { assert(head&&head == tail); assert(head->next == nullptr); assert(head->pre == nullptr); } else if (lru.size() == 2) { assert(head != tail); assert(head->next == tail); assert(head->pre == nullptr); assert(tail->next == nullptr); assert(tail->pre == head); } else { //check head can reach tail { int loop = lru.size() + 3; auto node = head; while (node != tail) { --loop; if (loop < 0) { assert(false); } node = node->next; } } //check tail can reach head { int loop = lru.size() + 3; auto node = tail; while (node != head) { --loop; if (loop < 0) { assert(false); } node = node->pre; } } } } //------------------------------------------------------test case void AutoTest() { Use(1); Use(1); Use(2); Use(2); Use(1); for (int i = 0; i < 1000; i++) { Use(rand() % 10000 + 1); if (rand() % 3 == 2) { Delete(rand() % 20000); } if (rand() % 10 == 3) { DeleteTail(); } } while (DeleteTail()); for (int i = 0; i < 3; i++) { Use(rand() % 10 + 1); } } void PrintStatus() { std::vector <Key> _data; { auto node = head; while (node) { // std::cout << node->data << std::endl; _data.push_back(node->data); node = node->next; } // std::cout << " 111 " << std::endl; } std::vector<Key>_data2; _data2.resize(_data.size()); int index = _data.size(); { auto node = tail; while (node) { // std::cout << node->data << std::endl; _data2[--index] = node->data; node = node->pre; } // std::cout << " 222 " << std::endl; } { //检查 乱序否 int id = _data2.size(); for (int i = 0; i < _data2.size(); i++) { assert(_data2[i] == _data[i]); } } } #endif private: std::unordered_map<Key, delist_node*> lru; delist_node *head = nullptr;//least use delist_node*tail = nullptr;// has long time not use };