赞
踩
如果主存与Cache之间采用全相联映射或者组相联映射方式,从主存向Cache传送一个新块。当Cache空间被占满后,需要使用替换算法置换Cache行。
注意:如果采用的直接映射方式,一个主存块只能放到固定的Cache行上(类似哈希方式),所以这种情况当Cache空间满了,不需要考虑替换算法。
常用的替换算法如下:
需要注意:
LFU方式会导致在Cache块中的数据无法及时换出。eg:切换运行程序后,Cache块内的数据需要加载新的程序,但是原来的数据计数器很大,使用这种算法不容易换出,所以LFU算法不是很优秀的替换算法。
Cache行设置一个计数器。用来记录主存块的使用情况,并根据计算数值淘汰某个块,计数值的位数与Cache组的数目有关(2路的话只需要1位LRU位,4路需要2位LRU位)。
计数器变化规则:
eg:
Cache采用4路组相联映射设有5块主存块{1,2,3,4,5},主存访问序列为{1,2,3,4,1,2,5,1}采用LRUCache缓存淘汰算法,替换过程如下:
需要注意:
当集中访问的存储区超过Cache组的大小时,LRU的命中率可能会变的非常低。这种现象称为抖动。
eg:
Cache采用4路组相联映射设有5块主存块{1,2,3,4,5},主存访问序列为{1,2,3,4,5,1,2,3,4,5}采用LRUCache缓存淘汰算法命中率经过画图计算可知为0
这个设计的难点是时间复杂度与空间复杂度都是O(1)
思路:
class LRUCache { public: LRUCache(int capacity) { this->capacity=capacity; } int get(int key) { auto pos=_lruHash.find(key); if(pos!=_lruHash.end()){ //Cache命中,调整计数器 更新key对应的节点的位置 list<pair<int,int>>::iterator ptr=pos->second; //转移节点库函数std::list::splice //void splice (iterator position, list& x, iterator i); //Transfers elements from x into the container, inserting them at position. //将x的第i个节点转移带position位置上 _lruCacheList.splice(_lruCacheList.begin(),_lruCacheList,ptr); return pos->second->second; } return -1; } void put(int key, int value) { auto pos=_lruHash.find(key); if(pos!=_lruHash.end()){ //更新Cache,更新对应节点的位置 list<pair<int,int>>::iterator ptr=pos->second; ptr->second=value;//更新数据 _lruCacheList.splice(_lruCacheList.begin(),_lruCacheList,ptr);//转移节点 } else{ //新增Cache if(capacity==_lruHash.size()){ //满了,替换删除尾上的数据 pair<int,int>&back=_lruCacheList.back(); _lruHash.erase(back.first); _lruCacheList.pop_back(); } _lruCacheList.push_front(make_pair(key,value)); _lruHash[key]=_lruCacheList.begin(); } } private: unordered_map<int,list<pair<int,int>>::iterator>_lruHash;//方便查找 list<pair<int,int>>_lruCacheList;//保存数据 size_t capacity; }; /** * Your LRUCache object will be instantiated and called as such: * LRUCache* obj = new LRUCache(capacity); * int param_1 = obj->get(key); * obj->put(key,value); */
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。