LRU算法总结

无论是哪一层次的缓存都面临一个同样的问题:当容量有限的缓存的空闲空间全部用完后,又有新的内容需要添加进缓存时,如何挑选并舍弃原有的部分内容,从而腾出空间放入这些新的内容。解决这个问题的算法有几种,如最近使用算法(LRU)、先进先出算法(FIFO)、最近最少使用算法(LFU)、非最近使用算法(NMRU)等,这些算法在不同层次的缓存上执行时拥有不同的效率和代价,需根据具体场合选择最合适的一种。

最近使用算法, 顾名思义,可以将其理解为如果数据最近被访问过,那么将来被访问的几率也很高。它的实现有多种方式,比如LRULRU-KTwo queuesMutiple queues等。

LRU

常用的实现是使用下图中的方式,往头部加入新的数据,如果该数据存在则将其放到头部,如果加入时已满,则从底部淘汰掉数据。

这种方式虽然简单,在频繁访问热点数据的时候效率高,但是它的缺点在于如果是偶尔的批量访问不同的数据时其命中率就会很低。比如我频繁的访问A,接着访问不同的数据直到A被淘汰,此时我再访问A,则不得不又再次把A加入到Cache中,显然这种方式是不合时宜的,因为A已经访问了很多次了,不应该将其淘汰而把一堆只访问一次的数据加入到Cache中。

LRU-K

上面的LRU只会将最近使用的一次加入到缓存,因此需要将其进行优化,变成缓存k次的才加入到缓存中,于是我们需要维护一个历史队列,纪录其数据对应的访问次数,其根据访问次数来进行淘汰,如果访问次数达到了k次才从历史队列中删除加入到缓存中,缓存按照LRU的规则来淘汰数据。

它的命中率要比LRU要高,但是因为需要维护一个历史队列,因此内存消耗会比LRU多。

实际应用中LRU-2是综合各种因素后最优的选择,LRU-3或者更大的K值命中率会高,但适应性差,需要大量的数据访问才能将历史访问记录清除掉。

Two queues(2Q)

和LRU-k类似,但不同的是,其有两个缓存队列,一个是FIFO队列,一个是LRU队列。如图所示,FIFO队列纪录只有访问一次的数据,并且按照FIFO的规则来淘汰数据,如果数据被第二次访问,则会加入到缓存中,缓存队列则按照LRU的规则来淘汰数据。

缺点和LRU-2一样。

Muti Queue(MQ)

MQ算法根据访问频率将数据划分为多个队列,不同的队列具有不同的访问优先级,其核心思想是:优先缓存访问次数多的数据。需要注意的是,如果一个优先级中的数据在一定的时间内没有被访问,则会降低其的优先级到低等级的队列中。

MQ的缺点在于纪录每个数据的访问时间,需要定时的扫描数据,其代价要比LRU高。

最后

最后给一个LRU的双链表实现,新访问的数据放到链表的头部,尾部则是最近最少使用的数据。

class Node(object):
def __init__(self, key=None, value=None, next=None, prev=None):
self.key = key
self.value = value
self.next = next
self.prev = prev
class LRUCache(object):
def __init__(self, capacity):
"""
:type capacity: int
"""
self.capacity = capacity
# single linked list with a head node
# always put new node to the tail
# also move the revisted node to the tail
self.head = Node()
self.tail = self.head
self.head.next = self.tail
# <key, node.prev>
self.hash_table = {}
def pop_front(self):
del self.hash_table[self.head.next.key]
p_next = self.head.next.next
self.head.next = p_next
# update the reference for new front node
self.hash_table[self.head.next.key] = self.head
def append(self, node):
self.hash_table[node.key] = self.tail
self.tail.next = node
self.tail = node
def move_to_end(self, prev):
node = prev.next
if node == self.tail:
return
# disconnect node
prev.next = node.next
node.next = None
self.hash_table[prev.next.key] = prev
# append node
self.append(node)
def get(self, key):
"""
:type key: int
:rtype: int
"""
if key not in self.hash_table:
return -1
prev = self.hash_table[key]
val = prev.next.value
self.move_to_end(prev)
return val
def put(self, key, value):
"""
:type key: int
:type value: int
:rtype: void
"""
if key in self.hash_table:
prev = self.hash_table[key]
prev.next.value = value
self.move_to_end(prev)
else:
self.append(Node(key, value))
if len(self.hash_table) > self.capacity:
self.pop_front()

LRU算法总结的更多相关文章

  1. Android图片缓存之Lru算法

    前言: 上篇我们总结了Bitmap的处理,同时对比了各种处理的效率以及对内存占用大小.我们得知一个应用如果使用大量图片就会导致OOM(out of memory),那该如何处理才能近可能的降低oom发 ...

  2. 缓存淘汰算法--LRU算法

    1. LRU1.1. 原理 LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是"如果数据最近被访问过,那么将来被访问的几率也 ...

  3. 借助LinkedHashMap实现基于LRU算法缓存

    一.LRU算法介绍 LRU(Least Recently Used)最近最少使用算法,是用在操作系统中的页面置换算法,因为内存空间是有限的,不可能把所有东西都放进来,所以就必须要有所取舍,我们应该把什 ...

  4. LinkedHashMap实现LRU算法

    LinkedHashMap特别有意思,它不仅仅是在HashMap上增加Entry的双向链接,它更能借助此特性实现保证Iterator迭代按照插入顺序(以insert模式创建LinkedHashMap) ...

  5. LinkedHashMap 和 LRU算法实现

    个人觉得LinkedHashMap 存在的意义就是为了实现 LRU 算法. public class LinkedHashMap<K,V> extends HashMap<K,V&g ...

  6. 简单LRU算法实现缓存

    最简单的LRU算法实现,就是利用jdk的LinkedHashMap,覆写其中的removeEldestEntry(Map.Entry)方法即可,如下所示: java 代码 import java.ut ...

  7. memached 服务器lru算法

    1.LRU是Least Recently Used的缩写,即最近最少使用页面置换算法,是为虚拟页式存储管理服务的.LRU算法的提出,是基于这样一个事实:在前面几条指令中使用频繁的页面很可能在后面的几条 ...

  8. 用LinkedHashMap实现LRU算法

    (在学习操作系统时,要做一份有关LRU和clock算法的实验报告,很多同学都应该是通过数组去实现LRU,可能是对堆栈的使用和链表的使用不是很熟悉吧,在网上查资料时看到了LinkedHashMap,于是 ...

  9. 近期最久未使用页面淘汰算法———LRU算法(java实现)

    请珍惜小编劳动成果,该文章为小编原创,转载请注明出处. LRU算法,即Last Recently Used ---选择最后一次訪问时间距离当前时间最长的一页并淘汰之--即淘汰最长时间没有使用的页 依照 ...

  10. Android 图像压缩,和LRU算法使用的推荐链接

    近两日,看的关于这些方面的一些教程数十篇,最好的当属google原版的教程了.国内有不少文章是翻译这个链接的. 需要注意的一点是:Android的SDK中的LRU算法在V4包和Util包中各有一个,推 ...

随机推荐

  1. 关于ubuntu的图标创建以及快捷方式打开

    //这里个人要添加的的为微信小程序开发工具 1:终端命令: sudo gedit /usr/share/applications/MyChat.desktop 2:修改启动器配置如下: [Deskto ...

  2. maven如何修改本地仓库与中央仓库

    摘要: 运行Maven的时候,Maven所需要的任何构件都是直接从本地仓库获取的.如果本地仓库没有,它会首先尝试从远程仓库下载构件至本地仓库,然后再使用本地仓库的构件. 什么是Maven仓库 在不用M ...

  3. R + ggplot2 Graph Catalog(转)

    Joanna Zhao’s and Jenny Bryan’s R graph catalog is meant to be a complement to the physical book,Cre ...

  4. CSS 简单了解(二)

    我们第一天说了简单的HTML,第二天说了简单的CSS.那么今天.咱们就来说一说他们的结合如何使用吧! 首先说引用方式,和使用方法吧! 1.内部样式表.(放入<head>中) <hea ...

  5. APUE-文件和目录(七)符号链接

    符号链接 符号链接的用途 符号链接是对一个文件的间接指针,它与前面介绍的硬连接不同,硬连接指向文件的i节点.引入符号链接是为了避开硬连接的一些限制: 硬链接通常要求链接和文件位于同一文件系统中. 只有 ...

  6. 在ASP.Net MVC 中如何实现跨越Session的分布式TempData

    Hi,guys!Long time no see! 1.问题的引出 我相信大家在项目中都使用过TempData,TempData是一个字典集合,一般用于两个请求之间临时缓存数据或者页面之间传递消息.也 ...

  7. 处理jQuery append加入的元素 绑定事件无效的方法

    通过jquery append(或者before.after,一样)新添加进网页的元素,常用的事件不能触发,比如:append了id 为 abc 的元素,那么 $(#abc).click(functi ...

  8. node项目的基本构建流程或者打开一个node项目的流程

    1.  确立项目所需要的所有依赖.框架(比如bootstrap,vue,angular等) 2. 在项目的根目录下创建一个package.json文件,package.json文件是项目的最重要文件之 ...

  9. ionic 项目中使用ngCordova插件$cordovaCamera筛选手机图库图片显示出来并上传

    原文档请看http://www.ncloud.hk/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/ionic%E5%9B%BE%E7%89%87%E4%B8%8A%E4%B ...

  10. CNN压缩:为反向传播添加mask(caffe代码修改)

    神经网络压缩的研究近三年十分热门,笔者查阅到相关的两篇博客,博主们非常奉献的提供了源代码,但是发发现在使用gpu训练添加mask的网络上,稍微有些不顺,特此再进行详细说明. 此文是在 基于Caffe的 ...