缓存算法的 Python 实现

实现FIFO缓存置换算法(Python) 关于FIFO 淘汰缓存时,把最先进入链表的结点淘汰掉。 具体实现(Python) (代码中引进的双向链表内容请查看-->https://blog.youkuaiyun.com/huanglei305/article/details/99422314) # -*- encoding=utf-8 -*- from computer_principle.DoubleLinkedList impor... 阅读详情

这篇文章描述了怎么用 Python 实现复杂度为 O(1) 的「最不常用」(Least Frequently Used, LFU)缓存回收算法。在 Ketan Shah、Anirban Mitra 和 Dhruv Matani的论文中有算法描述。实现中的命名是按照论文中的命名。


LFU 缓存回收机制对于 HTTP 缓存网络代理是非常有用的,我们可以从缓存中移除那些最不常使用的条目。


本文旨在设计一个其所有操作的时间复杂度都只有 O(1)的 LFU 缓存算法,这些操作包括了插入、访问和删除(回收)。


这个算法中用了双向链表。其一是用于访问频率,链表中的每个结点都包含一个链表,其中的元素有相同的访问频率。假设缓存中有5个元素。有两个元素被访问了一次,三个元素被访问了两次。在这个例子中,访问频率列表有两个结点(频率为1和2)。第一个频率结点的链表中有两个结点,第二个频率结点的链表中有三个结点。



我们要怎么构建它呢?我们需要的第一个对象是结点:


class Node(object):

"""Node containing data, pointers to previous and next node."""

def __init__(self, data):

self.data = data

self.prev = None

self.next = None


接下来是双向链表。每个结点有 prev 和 next 属性,分别等于前一个和下一个结点。head 被设为第一个结点,tail 被设为最后一个结点。



我们可以为双向链表定义方法来在链表尾部加入结点,插入结点,删除结点以及获得链表所有结点的数据。



class DoublyLinkedList(object):

def __init__(self):

self.head = None

self.tail = None

# Number of nodes in list.

self.count = 0

def add_node(self, cls, data):

"""Add node instance of class cls."""

return self.insert_node(cls, data, self.tail, None)

def insert_node(self, cls, data, prev, next):

"""Insert node instance of class cls."""

node = cls(data)

node.prev = prev

node.next = next

if prev:

prev.next = node

if next:

next.prev = node

if not self.head or next is self.head:

self.head = node

if not self.tail or prev is self.tail:

self.tail = node

self.count += 1

return node

def remove_node(self, node):

if node is self.tail:

self.tail = node.prev

else:

node.next.prev = node.prev

if node is self.head:

self.head = node.next

else:

node.prev.next = node.next

self.count -= 1

def get_nodes_data(self):

"""Return list nodes data as a list."""

data = []

node = self.head

while node:

data.append(node.data)

node = node.next

return data


访问频率双向链表中的每个结点都是一个频率结点(下图中的Freq Node)。它是一个结点,同时也是一个包含有相同频率的元素(下图中Item node)的双向性链表。每个条目结点都有一个指向其频率结点父亲的指针。




class FreqNode(DoublyLinkedList, Node):

"""Frequency node containing linked list of item nodes with

same frequency."""

def __init__(self, data):

DoublyLinkedList.__init__(self)

Node.__init__(self, data)

def add_item_node(self, data):

node = self.add_node(ItemNode, data)

node.parent = self

return node

def insert_item_node(self, data, prev, next):

node = self.insert_node(ItemNode, data, prev, next)

node.parent = self

return node

def remove_item_node(self, node):

self.remove_node(node)

class ItemNode(Node):

def __init__(self, data):

Node.__init__(self, data)

self.parent = None


条目结点的数据等于我们要存储的元素的键,这个键可以是一条HTTP请求。内容本身(例如HTTP响应)存储在字典中。字典中的每个值是LfuItem类型,”data”是缓存的内容,”parent”是指向频率结点的指针,”node”是指向频率结点下条目结点的指针。




class LfuItem(object):

def __init__(self, data, parent, node):

self.data = data

self.parent = parent

self.node = node


我们已经定义了数据对象类,现在可以定义缓存对象类了。它有一个双向链表(访问频率链表)和一个包含LFU条目(上面的LfuItem)的字典。我们定义两个方法:一个用来插入频率结点,一个用来删除频率结点。


class Cache(DoublyLinkedList):

def __init__(self):

DoublyLinkedList.__init__(self)

self.items = dict()

def insert_freq_node(self, data, prev, next):

return self.insert_node(FreqNode, data, prev, next)

def remove_freq_node(self, node):

self.remove_node(node)


下一步是定义方法来插入到缓存,访问缓存以及从缓存中删除。


我们来看看插入方法的逻辑。它以一个键和值为参数,例如HTTP请求和响应。如果没有频率为1的频率结点,它就被插入到访问频率双向链表的开头。一个条目结点被加入到频率结点的条目双向链表。键和值被加入到字典中。复杂度是O(1)。


def insert(self, key, value):

if key in self.items:

raise DuplicateException('Key exists')

freq_node = self.head

if not freq_node or freq_node.data != 1:

freq_node = self.insert_freq_node(1, None, freq_node)

freq_node.add_item_node(key)

self.items[key] = LfuItem(value, freq_node)


我们在缓存中插入两个元素,得到:



我们来看看访问方法的逻辑。如果键不存在,我们抛出异常。如果键存在,我们把条目结点移到频率加一的频率结点的链表中(如果频率结点不存在就增加这个结点)。复杂度是O(1)。


def access(self, key):

try:

tmp = self.items[key]

except KeyError:

raise NotFoundException('Key not found')

freq_node = tmp.parent

next_freq_node = freq_node.next

if not next_freq_node or next_freq_node.data != freq_node.data + 1:

next_freq_node = self.insert_freq_node(freq_node.data + 1,

freq_node, next_freq_node)

item_node = next_freq_node.add_item_node(key)

tmp.parent = next_freq_node

freq_node.remove_item_node(tmp.node)

if freq_node.count == 0:

self.remove_freq_node(freq_node)

tmp.node = item_node

return tmp.data


如果我们访问Key 1的条目,这个条目结点就被移动到频率为2的频率结点之下。我们得到:

如果我们访问Key 2的条目,这个条目结点就被移动到频率为2的频率结点之下。频率为1的频率结点会被删除(译注:因为它之下没有条目结点了),我们得到:

我们再看看delete_lfu方法。它把最不常使用的条目从缓存中删除。为此,它删除第一个频率结点下的第一个条目结点,同时从字典删除对应的LFUItem对象。如果此操作过后,频率结点的链表为空,就删除这个频率结点。




def delete_lfu(self):

"""Remove the first item node from the first frequency node.

Remove the item from the dictionary.

"""

if not self.head:

raise NotFoundException('No frequency nodes found')

freq_node = self.head

item_node = freq_node.head

del self.items[item_node.data]

freq_node.remove_item_node(item_node)

if freq_node.count == 0:

self.remove_freq_node(freq_node)


如果在缓存上调用delete_lfu,数据为Key 1的条目结点和它的LFUItem将被删除。我们得到:


实现LRU缓存置换算法(Python) 关于LRU算法 如下图,假设缓存4个子块,()表示使用的字块,[]表示淘汰的字块。 使用双向链表实现LRU 淘汰缓存时,把链表尾部的节点淘汰。 具体实现 (代码中引入的双向链表-->https://blog.youkuaiyun.com/huanglei305/article/details/99422314) # -*- encoding=utf-8 -*- from com... 阅读详情

相关推荐

实现LFU缓存置换算法(Python)

关于LFU 最不经常使用算法,淘汰缓存时,把使用频率最小的淘汰掉,当多个节点使用频率相同时,按照FIFO算法淘汰。 具体实现(Python) (代码中引进的双向链表内容请查看-->https://blog.youkuaiyun.com/huanglei305/article/details/99422314) # -*- encoding=utf-8 -*- from computer_p...

HenryLei的博客 4万+

Python使用缓存的方案

在开发Web应用或分布式系统时,缓存是常见的解决方案之一,它可以大幅提升系统性能。在Python中,我们可以使用内存缓存(例如使用)或者外部存储(例如使用Redis)来实现缓存功能。下面我们将从Django项目接入Redis、为视图提供缓存服务和缓存相关问题三个方面来介绍Python使用缓存的实践。

互联说 2180

缓存算法之FIFO、LRU、LFU的Python实现

                                    缓存算法之FIFO、LRU、LFU的Python实现         早期计算机内存有限,程序的运行与加载依赖页面置换算法, 其目的是将部分数据缓存在内存中, 在使用的时候就不必去磁盘上加载,这样可以提高运行的效率,虽然现在计算机的内存控件已经得到了很大的提升, 但是这种缓存策略一直被沿用, 尤其对于需要访问IO以及数据库等...

ybn6775的博客 2759

Python缓存神器cachetools:提高程序性能的利器,一文详解其缓存算法

春山如黄,琬琰满枝,烨烨清风,漫舞天地。

艰难困苦,玉汝于成。 9092

Python实现:详解LRU缓存淘汰算法

大家好,今天我们和大家聊一个非常常用的算法,叫做LRU。 LRU的英文全称是Least Recently Used,也即最不经常使用。我们看着好像挺迷糊的,其实这个含义要结合缓存一起使用。对于工程而言,缓存是非常非常重要的机制,尤其是在当下的互联网应用环境当中,起到的作用非常重要。为了便于大家更好地理解,我们从缓存的机制开始说起。 缓存 缓存的英文是cache,最早其实指的是用于CPU和主存数据交互的。早年这块存储被称为高速缓存,最近已经听不到这个词了,不知道是不是淘汰了。因为缓存的读写速度要高于CPU低于

梦子微信 813

[Python中的缓存技术及其应用]

在使用内存缓存时,需要注意缓存的存储时间和缓存的容量大小,以免造成内存溢出。在使用磁盘缓存时,需要注意缓存的路径和缓存的存储时间。在Python中,实现缓存的方式有很多种,其中最常见的方式是使用内存缓存和磁盘缓存。总结一下,在Python中,缓存是提高系统性能的重要手段之一,可以通过内存缓存和磁盘缓存来实现。同时,缓存也可以被广泛应用于数据查询、计算结果缓存、页面缓存等方面,有助于提高系统响应速度和减轻服务器负载压力。在实际应用中,缓存可以被广泛应用于数据查询、计算结果缓存、页面缓存等方面。

. 1411

python实现线下缓存最优算法

在这里可以考虑使用最远优先原则来进行算法实现,在已知未来数据访问的情况下,要调度内存中缓存的数据的时候,可以采用最远优先原则,也就是在决定从内存中迁出那条数据的时候,我们看当前在内存中的数据,在即将读取的数据中,哪一条离当前要写入的数据最远。而当数据是没有存储在内存当中的时候,那么CPU就会把数据从硬盘当中读取出来,然后存放在缓存当中,这里的问题主要在于如果内存已经被用完了,那么CPU就必须先将内存当中存储的某些数据输出到硬盘当中去,空出空间来以便存放要从硬盘当中读取的数据。

Mrsawyer的博客 984

实现LFU缓存算法的Python代码

LFU(Least Frequently Used)缓存算法是一种用于管理缓存中数据项的策略,它根据数据项的使用频率来决定哪些数据应该被保留在缓存中。方法淘汰一个数据项。如果不存在,我们创建一个新的数据项,并将其添加到缓存和使用计数器中。接着,我们添加第三个数据项到缓存中,由于缓存已满,会淘汰使用频率最低的数据项("key2"对应的实现LFU缓存算法的Python代码。方法中,我们获取使用频率最低的数据项列表,并选择列表中的第一个数据项进行淘汰。来存储缓存中的数据项,键为数据项的key,值为数据项对象。

Meta_C的博客 364

Python中使用内存缓存

在编写Python应用程序时,缓存很重要。使用缓存来避免重新处理数据或访问一个缓慢的数据库可以提高你的性能。在Python中,我们可以使用memcached 模块来在我们的脚本中加入内存缓存。本文将讨论准备内存缓存操作和主要的memcached 使用。我们还将学习使用Pythoncache 和set 的高级模式。

Python热爱者的博客 1345

Python实现LFU缓存算法及源码

而LFU(Least Frequently Used)缓存算法是一种流行的缓存算法之一,它优先淘汰最不频繁使用的缓存数据。本文介绍了 LFU 缓存算法的原理及 Python 实现源代码,并提供了一个简单的测试样例。LFU 算法虽然不是最优秀的缓存算法,但在某些场景下比 LRU 算法表现更好,尤其是当缓存命中率较高时。代码实现了 LFU 缓存算法的所有操作,包括 get 和 put 两个方法。在构造函数中,我们创建了 3 个字典用于记录缓存数据和其对应的使用次数和索引,以及不同使用次数对应的 key 列表。

BUG?不存在的! 411

基于Python实现的缓存淘汰替换策略算法,该算法将缓存分区

以下是一个基于Python实现的缓存淘汰替换策略算法,该算法将缓存分区,并根据不同分区的优先级进行淘汰,同时会自适应地调整缓存汰换的时机和力度,还会与GPTCache自带的LRU和FIFO策略进行对比。

AI的内部世界的博客 627

python实现缓存时间_Python实现以时间换空间的缓存替换算法

缓存是指可以进行高速数据交换的存储器,它先于内存与CPU交换数据,因此速度很快。缓存就是把一些数据暂时存放于某些地方,可能是内存,也有可能硬盘。在使用Scrapy爬网站的时候,产生出来的附加产物,因为在Scrapy爬取的时候,CPU的运行时间紧迫度不高(访问频次太高容易被封禁),借此机会难得来上一下,让自己的内存解放一下。算法原理:通过将要缓存的数据用二进制展开,得到的二进制数据映射到缓存字段上,...

weixin_35779845的博客 253

基于LRU算法的高效缓存管理与Python实现【LRU缓存淘汰策略的原理与应用】

虽然 LRU 是一种常见的缓存淘汰策略,但它并不适用于所有场景。根据应用的需求,可以根据具体情况修改缓存淘汰的规则。例如,我们可以基于访问频率、数据大小等因素来设计更复杂的缓存策略。除了基于使用频率和时间顺序来淘汰缓存外,还可以根据自定义的优先级来决定缓存中的数据。例如,可以为某些数据分配较高的优先级,使其更不容易被淘汰。return -1# 选择最小优先级的元素进行淘汰LRU 缓存算法在很多领域中都有着重要的应用,尤其是在需要频繁访问数据的场景中,通过高效的缓存淘汰策略,能够显著提升系统的性能。

博客 1836

缓存淘汰算法与 python 中 lru_cache 装饰器的实现

缓存淘汰算法与 python 中 lru_cache 装饰器的实现1. 引言2. 缓存淘汰算法2.1. 先进先出 -- FIFO2.2. 最不经常使用算法 -- LFU2.3. 最近最少使用算法 -- LRU2.4. 最近最常使用算法 -- MRU3. LRU 的实现 -- python&...

居士的优快云 2653

Python:实现lfu cache缓存算法(附完整源码)

Python:实现lfu cache缓存算法(附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 685

Python-一个采用Python开发的简单缓存服务器

一个采用Python开发的简单缓存服务器

LRU缓存算法:Python实现与工程应用最佳实践

LRU(Least Recently Used)缓存算法是一种高效的缓存淘汰策略,广泛应用于内存管理、数据库优化和Web开发中。本文将通过Python实现LRU缓存的两种方案,解析其核心原理,并探讨在实际工程中的应用技巧。 ## 一、LRU缓存核心原理与优势 LRU算法的核心思想是**当缓存空间满时,优先淘汰最久未使用的元素**。这种策略基于"最近使用的数据未来被访问的概率更高"的局部性原理,

gitblog_00153的博客 514

实战Nocobase插件开发:如何用yarn pm命令快速创建和测试API接口插件

本文详细介绍了如何使用Nocobase插件开发工具快速创建和测试API接口插件,涵盖从环境配置、插件骨架生成到数据模型定义和接口实现的完整流程。通过yarn pm命令简化开发步骤,帮助开发者高效构建支持CRUD的API插件,适用于低代码平台开发场景。

weixin_29313239的博客 457
上一篇: 八大排序算法的 Python 实现
下一篇: python 根据类的字符串名实例化对象
youbo_sun
博客等级 码龄12年 20粉丝 51原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值