LRU和LFU的实现

LFULRU是两种典型的缓存页面置换算法,了解其底层以及运行机制是CSer的必修课。

缓存是计算机中广泛应用的一种技术,包括CPU L1/L2/L3 cache,RAM中的cache,网络协议tcp缓冲区,OS页面置换算法,redis缓存等等。需要形成对“缓存”的一个整体认识。

LRU(Least Recently Used): 最近最久未使用算法,即最久不使用的元素最先淘汰。

LFU(Least Frequently Used):最近最不频繁使用算法,即考虑访问次数,如果访问次数相同,考虑时间先后。

实际上,这两者都考虑的是程序的局部性原理

 

LRU:adopts 'bidirectional list' and 'hash table' technology

LFU:adopts double hash table and bidirectional list,

   can also use hash table and rbtree(set), but the time complexity is O(logn).

 

  1 /** 
  2 *  author:  ray'
  3 *  date:    2021/07/15
  4 *  contact: 2582171339@qq.com
  5 *  blog:    https://www.cnblogs.com/rayss
  6 *  github:  https://github.com/XuanRay
  7 *  
  8 *  description: LRU和LFU的实现 
  9 *               LRU code copyed from "小林coding"
 10 *               LFU is coded by ray'
 11 */
 12 
 13 #include <iostream>
 14 #include <list>
 15 #include <unordered_map>
 16 #include <string>
 17 
 18 // LRU:adopts 'bidirectional list' and 'hash table' technology
 19 template <typename KeyT, typename ValueT>
 20 class LRUCache {
 21 public:
 22     typedef std::pair<KeyT, ValueT> Pair;      // using Pair = std::pair<KeyT, ValueT>;
 23     typedef std::list<Pair> List;
 24     typedef std::unordered_map<KeyT, typename List::iterator> uMap;
 25 
 26     explicit LRUCache(int cap) : m_capacity(cap) {}
 27 
 28     bool put(KeyT key, ValueT value);          //add element to LRUCache 
 29     bool get(KeyT key, ValueT* pValue);        //visit element in LRUCache
 30 
 31     std::size_t size() const {
 32         return m_capacity;
 33     }
 34 
 35     LRUCache(const LRUCache&) = delete;
 36     LRUCache& operator=(const LRUCache&) = delete;
 37 
 38     ~LRUCache() {
 39         m_map.clear();
 40         m_list.clear();
 41     }
 42 
 43 private:
 44     std::size_t m_capacity;  // list capacity
 45     List m_list;     // bidirectional list
 46     uMap m_map;      // hash table
 47 };
 48 
 49 
 50 template <typename KeyT, typename ValueT>
 51 bool LRUCache<KeyT, ValueT>::put(KeyT key, ValueT value) {
 52     // find key from hash table
 53     typename uMap::iterator iter = m_map.find(key);
 54 
 55     // if key is existed
 56     if (iter != m_map.end()) {
 57         // delete from bi-list and hash table
 58         m_list.erase(iter->second);
 59         m_map.erase(iter);
 60     }
 61 
 62     // insert the element into bi-list front and hash table
 63     m_list.push_front(std::make_pair(key, value));
 64     m_map[key] = m_list.begin();
 65 
 66     // judge whether exceed capacity
 67     if (m_list.size() > m_capacity) {
 68         KeyT endKey = m_list.back().first;  // unlike member list::end, which returns an iterator just past this element
 69                                             // list::back returns a direct reference
 70         m_list.pop_back();
 71         m_map.erase(endKey);
 72     }
 73 
 74     return true;
 75 }
 76 
 77 template <typename KeyT, typename ValueT>
 78 bool LRUCache<KeyT, ValueT>::get(KeyT key, ValueT* pvalue) {
 79     // find whether key is in hash table
 80     typename uMap::iterator mapIter = m_map.find(key);
 81     if (mapIter == m_map.end()) {
 82         return false;
 83     }
 84     
 85     // get bi-list node
 86     typename List::iterator listIter = mapIter->second;
 87 
 88     ValueT value = listIter->second;
 89 
 90     // delete node from bi-list and insert into the front.
 91     m_list.erase(listIter);         //这个迭代器已经失效了,还能push_front? 迭代器失效问题
 92     m_list.push_front(std::make_pair(key, value));  
 93 
 94     // hash table changed
 95     m_map[key] = m_list.begin();
 96 
 97     // save the value
 98     *pvalue = value;
 99 
100     return true;
101 }
102 
103 
104 
105 // FLU adopted double hash table and bidirectional list
106 
107 // Node information
108 template <typename KeyT, typename ValueT>
109 struct Node {
110     KeyT key;
111     ValueT value;
112     int freq;
113 
114     Node(KeyT k, ValueT v, int f) : key(k), value(v), freq(f) {}
115 };
116 
117 // LFUCache
118 template<typename KeyT, typename ValueT>
119 class LFUCache {
120 public:
121     typedef std::unordered_map<int, std::list<Node<KeyT, ValueT>>> Freq_Table;
122     typedef std::unordered_map<KeyT, typename std::list<Node<KeyT, ValueT>>::iterator> Key_Table;
123 
124     // ctor
125     LFUCache(int cap = 1) : m_minfreq(0), m_capacity(cap) {}
126 
127     // put and get
128     bool get(KeyT key, ValueT* pValue);
129     bool put(KeyT key, ValueT value);
130 
131     std::size_t get_MinFreq() const {
132         return this->m_minfreq;
133     }
134 
135     std::size_t get_Capacity() const {
136         return this->m_capacity;
137     }
138 
139     // dtor
140     ~LFUCache() {
141         m_freqTable.clear();
142         m_keyTable.clear();
143     }
144 
145 private:
146     std::size_t m_minfreq;
147     std::size_t m_capacity;
148     Freq_Table m_freqTable;
149     Key_Table m_keyTable;
150 };
151 
152 template<typename KeyT, typename ValueT>
153 bool LFUCache<KeyT, ValueT>::get(KeyT key, ValueT* pValue) {
154     // find whether key is in key hash table
155     auto iter = m_keyTable.find(key);
156     if (iter == m_keyTable.end()) {
157         return false;
158     }
159 
160     typename std::list<Node<KeyT, ValueT>>::iterator node = iter->second;
161     *pValue = node->value;
162     int freq = node->freq;
163 
164     // delete this node
165     m_freqTable[freq].erase(node);
166 
167     // if current list is NULL, delete it from freq hash table
168     if (m_freqTable[freq].size() == 0) {
169         m_freqTable.erase(freq);
170         if (m_minfreq == freq) {
171             m_minfreq += 1;
172         }
173     }
174 
175     // insert the node to freq + 1 list
176     m_freqTable[freq + 1].push_front(Node<KeyT, ValueT>(key, *pValue, freq + 1));
177 
178     //m_freqTable[freq + 1].push_front(new Node<KeyT, ValueT>(key, val, freq + 1));
179     m_keyTable[key] = m_freqTable[freq + 1].begin();
180     
181     return true;
182 }
183 
184 template<typename KeyT, typename ValueT>
185 bool LFUCache<KeyT, ValueT>::put(KeyT key, ValueT value) {
186     typename Key_Table::iterator iter = this->m_keyTable.find(key);
187 
188     // if key is not found in key hash table
189     if (iter == m_keyTable.end()) {
190         // if full
191         if (m_capacity == m_keyTable.size()) {
192             auto it = m_freqTable[m_minfreq].back(); // list的back()返回的是reference
193             m_keyTable.erase(it.key);
194             m_freqTable[m_minfreq].pop_back();
195             if (m_freqTable[m_minfreq].size() == 0) {
196                 m_freqTable.erase(m_minfreq);
197             }
198         }
199         m_freqTable[1].push_front(Node<KeyT, ValueT>(key, value, 1));
200         m_keyTable[key] = m_freqTable[1].begin();
201         m_minfreq = 1;
202     }
203     else { // otherwise
204         auto node = iter->second;
205         int freq = node->freq;
206         m_freqTable[freq].erase(node);
207         if (m_freqTable[freq].size() == 0) {
208             m_freqTable.erase(freq);
209             if (m_minfreq == freq) {
210                 m_minfreq += 1;
211             }
212         }
213         m_freqTable[freq + 1].push_front(Node<KeyT, ValueT>(key, value, freq + 1));
214         m_keyTable[key] = m_freqTable[freq + 1].begin();
215     }
216 
217     return true;
218 }
219 
220 
221 
222 // test LRU
223 void testLRU() {
224     LRUCache<int, std::string> lruCache(3);
225 
226     lruCache.put(1, "r");
227     lruCache.put(2, "a");
228     lruCache.put(3, "y");
229 
230     std::string value;
231     bool ret = true;
232 
233     ret = lruCache.get(1, &value);
234     std::cout << "value = " << value << ", ret = " << ret << "\n";
235 
236     lruCache.put(4, "'");
237     value = "";
238     ret = lruCache.get(2, &value);
239     std::cout << "value = " << value << ", ret = " << ret << "\n";
240 }
241 
242 void testLFU() {
243     LFUCache<int, std::string> lfuCache(3);
244     lfuCache.put(1, "r");
245     lfuCache.put(2, "a");
246     lfuCache.put(3, "y");
247 
248     std::string value;
249     bool ret = true;
250 
251     ret = lfuCache.get(1, &value);
252     std::cout << "value = " << value << ", ret = " << ret << "\n";
253 
254     value = "";
255     ret = lfuCache.get(2, &value);
256     std::cout << "value = " << value << ", ret = " << ret << "\n";
257 
258     lfuCache.put(4, "s");
259 
260     value = "";
261     ret = lfuCache.get(3, &value);
262     std::cout << "value = " << value << ", ret = " << ret << "\n";
263 }
264 
265 
266 // test LRU and LFU
267 int main() {
268     testLRU();
269 
270     std::cout << "\n\n";
271     std::cout << "----------------------------------------------\n\n";
272 
273     testLFU();
274 
275     system("pause");
276     return 0;
277 }

 

posted @ 2021-07-16 11:18  Ray-ss  阅读(287)  评论(0编辑  收藏  举报