LRU(最近最少使用)(python实现)
"""
python3 only
LRU cache
"""
from collections import OrderedDict
from functools import wraps def fib(n):
if n <= 1: # 0 or 1
return n
return f(n - 1) + f(n - 2) # 由于涉及到重复计算,这个递归函数在 n 大了以后会非常慢。 O(2^n) """
下边就来写一个缓存装饰器来优化它。传统方法是用个数组记录之前计算过的值,但是这种方式不够 Pythonic
""" def cache(func):
"""先引入一个简单的装饰器缓存,其实原理很简单,就是内部用一个字典缓存已经计算过的结果"""
store = {} @wraps(func)
def _(n): # 这里函数没啥意义就随便用下划线命名了
if n in store:
return store[n]
else:
res = func(n)
store[n] = res
return res
return _ @cache
def f(n):
if n <= 1: # 0 or 1
return n
return f(n - 1) + f(n - 2) """
问题来了,假如空间有限怎么办,我们不可能一直向缓存塞东西,当缓存达到一定个数之后,我们需要一种策略踢出一些元素,
用来给新的元素腾出空间。
一般缓存失效策略有
- LRU(Least-Recently-Used): 替换掉最近请求最少的对象,实际中使用最广。cpu缓存淘汰和虚拟内存效果好,web应用欠佳
- LFU(Least-Frequently-Used): 缓存污染问题(一个先前流行的缓存对象会在缓存中驻留很长时间)
- First in First out(FIFO)
- Random Cache: 随机选一个删除 LRU 是常用的一个,比如 redis 就实现了这个策略,这里我们来模拟实现一个。
要想实现一个 LRU,我们需要一种方式能够记录访问的顺序,并且每次访问之后我们要把最新使用到的元素放到最后(表示最新访问)。
当容量满了以后,我们踢出最早访问的元素。假如用一个链表来表示的话: [1] -> [2] -> [3] 假设最后边是最后访问的,当访问到一个元素以后,我们把它放到最后。当容量满了,我们踢出第一个元素就行了。
一开始的想法可能是用一个链表来记录访问顺序,但是单链表有个问题就是如果访问了中间一个元素,我们需要拿掉它并且放到链表尾部。
而单链表无法在O(1)的时间内删除一个节点(必须要先搜索到它),但是双端链表可以,因为一个节点记录了它的前后节点,
只需要把要删除的节点的前后节点链接起来就行了。
还有个问题是如何把删除后的节点放到链表尾部,如果是循环双端链表就可以啦,我们有个 root 节点链接了首位节点,
只需要让 root 的前一个指向这个被删除节点,然后让之前的最后一个节点也指向它就行了。 使用了循环双端链表之后,我们的操作就都是 O(1) 的了。这也就是使用一个 dict 和一个 循环双端链表 实现LRU 的思路。
不过一般我们使用内置的 OrderedDict(原理和这个类似)就好了,要实现一个循环双端链表是一个不简单的事情。 """ class LRUCache:
def __init__(self, capacity=128):
self.capacity = capacity
# 借助 OrderedDict 我们可以快速实现一个 LRUCache,OrderedDict 内部其实也是使用循环双端链表实现的
# OrderedDict 有两个重要的函数用来实现 LRU,一个是 move_to_end,一个是 popitem,请自己看文档
self.od = OrderedDict() def get(self, key, default=None):
val = self.od.get(key, default) # 如果没有返回 default,保持 dict 语义
self.od.move_to_end(key) # 每次访问就把key 放到最后表示最新访问
return val def add_or_update(self, key, value):
if key in self.od: # update
self.od[key] = value
self.od.move_to_end(key)
else: # insert
self.od[key] = value
if len(self.od) > self.capacity: # full
self.od.popitem(last=False) def __call__(self, func):
"""
一个简单的 LRU 实现。有一些问题需要思考下: - 这里为了简化默认参数只有一个数字 n,假如可以传入多个参数,如何确定缓存的key 呢?
- 这里实现没有考虑线程安全的问题,要如何才能实现线程安全的 LRU 呢?当然如果不是多线程环境下使用是不需要考虑的
- 假如这里没有用内置的 dict,你能使用 redis 来实现这个 LRU 吗,如果使用了 redis,我们可以存储更多数据到服务器。而使用字典实际上是缓存了Python进程里(localCache)。
- 这里只是实现了 lru 策略,你能同时实现一个超时 timeout 参数吗?比如像是memcache 实现的 lazy expiration 策略
- LRU有个缺点就是,对于周期性的数据访问会导致命中率迅速下降,有一种优化是 LRU-K,访问了次数达到 k 次才会将数据放入缓存
"""
def _(n):
if n in self.od:
return self.get(n)
else:
val = func(n)
self.add_or_update(n, val)
return val
return _ @LRUCache(10)
def f_use_lru(n):
if n <= 1: # 0 or 1
return n
return f(n - 1) + f(n - 2) def test():
import time
beg = time.time()
for i in range(34):
print(f(i))
print(time.time() - beg)
beg = time.time()
for i in range(34):
print(f_use_lru(i))
print(time.time() - beg) if __name__ == '__main__':
test()
LRU(最近最少使用)(python实现)的更多相关文章
- LRU算法的Python实现
http://flychao88.iteye.com/blog/1977653文章中介绍了常见的几种缓存淘汰策略 LRU:least recently used,最近最少使用算法.其实就是按使用时间倒 ...
- LRU最近最少使用算法
最近最少使用算法有两种实现方式: (1)记时法:对于每一页增设一个访问时间计时器.每当一个页面被访问时,当时的绝对时钟内容被复制到对应的访问时间计时器中.这样系统就记录了内存中所有页面最后一次被访问的 ...
- LRU(最近最少使用淘汰算法)基本实现
LRU(Least Recently Used) 出发点:在页式存储管理中,如果一页很长时间未被访问,则它在最近一段时间内也不会被访问,即时间局部性,那我们就把它调出(置换出)内存. 为了实现LRU ...
- java 自定义 LRU(最近最少使用)策略 实现 缓存机制
1. java提供了一个简单的方式实现LRU: LinkedHashMap 2. 自定义实现 LRU至少需要两个主要操作: 添加(add)和搜索(search) public class LRU ...
- LRU算法原理解析
LRU是Least Recently Used的缩写,即最近最少使用,常用于页面置换算法,是为虚拟页式存储管理服务的. 现代操作系统提供了一种对主存的抽象概念虚拟内存,来对主存进行更好地管理.他将主存 ...
- LeetCode--146--LRU缓存机制(python)
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - 如果密钥 (key) 存 ...
- 手撕LRU缓存了解一下
面试官:来了,老弟,LRU缓存实现一下? 我:直接LinkedHashMap就好了. 面试官:不要用现有的实现,自己实现一个. 我:..... 面试官:回去等消息吧.... 大家好,我是程序员学长,今 ...
- 手撕LRU缓存
面试官:来了,老弟,LRU缓存实现一下? 我:直接LinkedHashMap就好了. 面试官:不要用现有的实现,自己实现一个. 我:..... 面试官:回去等消息吧.... 大家好,我是程序员学长,今 ...
- 数据库存取缓冲区的LRU与MRU算法
数据库存取缓冲区的LRU与MRU算法 1.Cache Hit and Cache Miss 当使用者第一次向数据库发出查询数据的请求的时候,数据库会先在缓冲区中查找该数据,如果要访问的数据恰好已经在缓 ...
随机推荐
- ubuntu下把python脚本转为二进制字节码文件
ubuntu下把python脚本转为二进制字节码文件 听语音 原创 | 浏览:354 | 更新:2017-12-22 14:48 1 2 3 4 5 6 7 分步阅读 自己拥有个几个python脚本文 ...
- 高级UI-自定义Behavior
Behavior本身是一个抽象类,可以用于两个view之间的状态监听,也可用于某个view监听CoordinateLayout里面的所有控件滑动状态,实现自定义Behavior则可以实现任意两个vie ...
- web框架之Django
windows下安装了Django后,在cmd里输入django-admin会返回Django的所有命令
- mysql查看和设置最大连接数
.查看最大连接数 SHOW VARIABLES LIKE '%max_connections%'; .修改最大连接数 ;
- 2019年广东省赛gdccpc回顾
本次比赛状态一般般,热身赛单人挂机爆零让自己慌了一整天. 开题直接抓E题入手,准备交题后关机(辣鸡云桌面),开机后又告诉我要关机,心急连交两发结果都WA了,最后靠队员提醒救了回来.心态还算稳住了.后面 ...
- MaskRCNN 奔跑自己的数据
import os import sys import random import math import re import time import numpy as np import cv2 i ...
- kafka原理分析
#kafka为什么有高吞吐量 1 由于接收数据时可以设置request.required.acks参数,一般设定为1或者0,即生产者发送消息0代表不关心kafka是否接收成功,也就是关闭ack:1代表 ...
- NetworkX一个图论与复杂网络建模工具
NetworkX是一个图论与复杂网络建模工具,采用Python语言开发,内置了常用的图与复杂网络分析算法,可以方便的进行复杂网络数据分析.仿真建模等工作.(1)NetworkX支持创建简单无向图.有向 ...
- [转帖]Linux运维工程师的十个基本技能点
Linux运维工程师的十个基本技能点 https://cloud.tencent.com/developer/article/1115068 本人是Linux运维工程师,对这方面有点心得,现在我说 ...
- SQL语言(二)
SQL约束与策略 create table student( id int primary key, //主键约束 name ) not null, //非空约束 idCard ) unique, / ...