1. LRU简介
LRU (Least recently used) 最近最少使用算法是缓存置换策略中使用比较多的算法之一。其利用程序的局部性原则:最近访问的缓存将来还会访问的可能性很大。LRU实现比较简单,而且对于实际问题也很实用,良好的运行时性能,命中率较高。
LRU算法的基本算法步骤为:
- 查找希望得到的元素;
- 元素在缓存中则将元素移动到缓存维护的链表的前端;
- 元素不在缓存中则新建节点将该节点添加到链表的前段,如果缓存满则移除链表的最后一个节点。
2. LRU实现
LRU算法的实现一般为双向链表和哈希表配合,哈希表用于查找元素,双向链表用于存储数据。当然也可以仅仅使用双向链表实现,但是查找元素的性能会大大下降。
LRU算法两个API的实现过程为:
get
:如果元素在缓存中,则将相关的节点移动到链表的表头;如果不在缓存中则抛出异常;set
:如果元素在缓存中,则将相关节点移动到链表表头;如果不在缓存中则新建节点将该节点插入到头节点,同时如果缓存满则删除尾节点的元素。
#include <list>
#include <map>
#include <iostream>
#include <exception>
#include <ctime>
#include <cstdlib>
#include <mutex>
#include <thread>
using namespace std;
class cache_algorithm
{
protected:
size_t _capacity;
size_t _size;
mutex _mutex;
public:
cache_algorithm(size_t sz) :_capacity(sz), _size(0) {}
virtual int get(int key) = 0;
virtual int set(int key, int value) = 0;
void lock()
{
_mutex.lock();
}
void unlock()
{
_mutex.unlock();
}
bool full() { return _size == _capacity; }
};
struct cache_node
{
int key;
int value;
cache_node(int key, int val)
{
this->key = key;
value = val;
}
bool operator==(const cache_node&rst)
{
return key == rst.key && value == rst.value;
}
};
class lru : public cache_algorithm
{
private:
list<cache_node> _data;
map<int, list<cache_node>::iterator> _table;
public:
lru(size_t size) : cache_algorithm(size) {}
map<int, list<cache_node>::iterator>::iterator find(int key)
{
return _table.find(key);
}
void refresh(list<cache_node>::iterator &it)
{
int key = it->key;
int val = it->value;
_data.remove(*it);
_data.insert(_data.begin(), cache_node(key, val));
_table[key] = _data.begin();
}
int get(int key)
{
map<int, list<cache_node>::iterator>::iterator it = find(key);
if (it == _table.end())
throw exception("cache miss");
lock();
refresh(it->second);
unlock();
return it->second->value;
}
int set(int key, int value)
{
map<int, list<cache_node>::iterator>::iterator it = find(key);
lock();
if (it == _table.end())
{
//未找到则添加
_data.insert(_data.begin(), cache_node(key, value));
_table[key] = _data.begin();
if (full())
{
//缓冲区已经满,需要移除一个元素
_table.erase(_data.rbegin()->key);
_data.remove(*_data.rbegin());
}
else
{
_size++;
}
}
else
{
refresh(it->second);
}
unlock();
return value;
}
};
void lru_performance(cache_algorithm *alg, int epoch = 1000)
{
int miss_times = 0;
int start_time = clock();
for (int i = 0; i < epoch; i++)
{
int key = rand() % 15;
try
{
int value = alg->get(key);
}
catch (exception& e)
{
miss_times++;
}
}
int end_time = clock();
cout << "cost time " << (end_time - start_time)/(CLOCKS_PER_SEC * 1.0) <<"s"<< endl;
cout << "miss per:" << miss_times * 1.0 / epoch << "%" << endl;
}
int main()
{
int n = 10;
lru lru1(n);
for (int i = 0; i < n; i++)
{
int key = i;
int val = rand() % 10;
lru1.set(key, val);
}
lru_performance(&lru1);
system("pause");
return 0;
};