Cache的设计和实现 LRU Cache
来源:互联网 发布:网络骂人的歌曲最火的 编辑:程序博客网 时间:2024/05/21 10:45
Cache的设计和实现 LRU Cache
Cache的应用非常广泛,其实Cache是一种思想,一个广义的词汇,一种在性能和金钱的权衡上的思想。
Cache的思想用在很多地方,使用的载体也不同,都是位于相对高速设备和相对低速设备之间,起到缓存的作用。
1、最常用处:用在内存和CPU之间,以SRAM作为Cache的载体。(内存是DRAM,CPU是寄存器)
2、用在内存和硬盘之间。
3、用在硬盘和网络之间。
Cache的操作方法:
1、外部操作:数据访问,Get和Set。高速设备要找一个数据,先从Cache找,如果有就直接用;如果没有则去低速设备找,并调入到Cache。
2、内部操作:Cache更新。Cache的大小必定是有限的。所以需要一个淘汰算法(使得命中率尽可能高),最常用的是LRU。当Cache满了却又要加入新的数据,就需要淘汰旧的数据。
Cache的典型实现:
LRU的典型实现是 hash map + double linked list
为什么要用两种数据结构?
理由:hash表是用来访问数据的,因为hash的查找速度快,O(1)。双向链表是用来记录最近访问情况的,因为Hash做不到排序。
如果访问键值为key的数据已经在Cache中,那就访问hash中的该数据,同时要将存储该key的结点移到双向链表头部。
如果访问的数据不在Cache中:
若此时Cache还没满,那么我们将新结点插入到链表头部, 同时在哈希表添加(键值,结点地址)。
若此时Cache已经满了,我们就将链表中的最后一个结点(注意不是尾结点)的内容替换为新内容, 然后移动到头部,更新哈希表中对应的(键值,结点地址)。
查询操作的时间是O(1),直接通过hash找到节点,而不是通过链表顺序查找的。
对链表的操作插入操作和移动操作也都是O(1)。
LRUCache简单实例:
实现get(key) 函数:如果key对应的value存在则返回value,否则返回-1。 Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
实现set(key, value)函数:设置或插入value值。 Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.
哈希表用map实现,结点存储的是key和对应的双向链表结点指针。
双向链表用list实现,结点存储的是key和value。
这样,由key可以用哈希表快速找到链表结点,进行数据访问和结点位置更新。
class LRUCache{public: LRUCache(int capacity) { cap = capacity; size = 0; hash_table.clear(); double_list.clear(); } int get(int key) { map<int, list<pair<int, int> >::iterator>::iterator it = hash_table.find(key); if(it != hash_table.end()) //update double_list { int value = (*( (*it).second ) ).second; double_list.splice(double_list.begin(), double_list, (*it).second); return value; } else //Not in Cache { return -1; //In fact, we should go to another place to get it. } } void set(int key, int value) { map<int, list<pair<int, int> >::iterator>::iterator it = hash_table.find(key); if(it != hash_table.end()) //update double_list and hash_table's value { double_list.splice(double_list.begin(), double_list, (*it).second); (*( (*it).second ) ).second = value; } else { if(size == cap) //invalidate the lru one { int oldkey = double_list.back().first; list<pair<int, int> >::iterator oldone = double_list.end(); oldone--; double_list.splice(double_list.begin(), double_list, oldone); hash_table.erase(oldkey); (*double_list.begin()).first = key; (*double_list.begin()).second = value; hash_table[key] = double_list.begin(); } else // insert new one to cache { double_list.push_front(pair<int, int>(key, value)); hash_table[key] = double_list.begin(); size++; } } } private: //注意:对双向链表的操作不要使迭代器失效 map<int, list<pair<int, int> >::iterator> hash_table; //存储:(关键词key , 双链表中的结点指针) list<pair<int, int> > double_list; //结点存储:(key, value) int size; int cap;};
注意细节:
对双向链表list的修改绝对不可以使原有的迭代器失效,否则hash里存储的内容就失效了。
这里使用的是splice方法来将中间的某个结点移动到首位置。
list::splice实现list拼接的功能。将源list的内容部分或全部元素删除,拼插入到目的list。
函数有以下三种声明:
void splice ( iterator position, list<T,Allocator>& x ); // void splice ( iterator position, list<T,Allocator>& x, iterator i );void splice ( iterator position, list<T,Allocator>& x, iterator first, iterator last );
函数说明:在list间移动元素:
将x的元素移动到目的list的指定位置,高效的将他们插入到目的list并从x中删除。目的list的大小会增加,增加的大小为插入元素的大小。x的大小相应的会减少同样的大小。前两个函数不会涉及到元素的创建或销毁。第三个函数会。指向被删除元素的迭代器会失效。
参数:position目的list的位置,用来标明 插入位置
x源list、first,lastx里需要被移动的元素的迭代器。区间为[first, last).包含first指向的元素,不包含last指向的元素。
0 0
- Cache的设计和实现 LRU Cache
- Cache的设计和实现 LRU Cache
- LRU cache的实现
- LRU Cache的实现
- LRU Cache的实现
- LRU Cache的实现
- LRU Cache 设计与实现
- LRU Cache 设计与实现
- LRU Cache 设计与实现
- LRU Cache原理和实现
- LRU的cache的实现
- LRU cache的简单实现
- LRU Cache的一种实现
- LRU cache的另一种实现
- LRU Cache的C++实现
- LRU Cache的简单实现
- LRU cache 算法的实现
- 设计并实现一个LRU Cache
- 浅谈我对“活在当下”的理解
- 二叉树
- 发帖测试2.3
- UVa 11178 Morley's Theorem(几何)
- Python快速学习08:模块的操作
- Cache的设计和实现 LRU Cache
- 【并查集 偏移量】HDU 1829 A Bug's Life
- sublime利用ClosureCompiler和YUICompressor本地压缩
- 关于ubuntu卸载ibus后导致系统设置不能打开
- 山东省第二届ACM大学生程序设计竞赛(原题) D:Binomial Coeffcients
- com初始化
- 斐波那契数列的递归和非递归算法
- c++继承中的内存布局
- UVa 10820 Send a Table(欧拉函数)