📜  Python – LRU 缓存

📅  最后修改于: 2022-05-13 01:55:24.551000             🧑  作者: Mango

Python – LRU 缓存

LRU Cache 是最近最少使用的缓存,主要用于内存组织。在这种情况下,元素以先进先出格式出现。我们得到了可以参考的总页码。我们还给出了缓存(或内存)大小(缓存一次可以保存的页帧数)。 LRU 缓存方案是在缓存已满并引用缓存中不存在的新页面时删除最近最少使用的帧。 LRU Cache 通常有两个术语,让我们看看它们 -

  • 页面命中:如果在主存储器中找到所需的页面,则它是页面命中。
  • 页面错误:如果在主存储器中找不到所需的页面,则发生页面错误。

当一个页面被引用时,需要的页面可能在内存中。如果它在内存中,我们需要将链表的节点分离出来,放到队列的最前面。
如果所需的页面不在内存中,我们将其带入内存。简单来说,就是在队列的最前面增加一个新节点,并更新哈希中对应的节点地址。如果队列已满,即所有帧都已满,我们从队列的尾部移除一个节点,并将新节点添加到队列的前端。

示例 –考虑以下参考字符串:

1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5

使用具有 3 个页框的最近最少使用 (LRU) 页面替换算法查找页面错误数。
解释 -

LRU1
LRU2

使用Python的 LRU 缓存
您可以在队列的帮助下实现这一点。在此,我们使用了使用链表的队列。在 Pycharm IDE 中运行给定的代码。

import time
   
      
class Node:
      
    # Nodes are represented in n
    def __init__(self, key, val):
        self.key = key
        self.val = val
        self.next = None
        self.prev = None
   
   
class LRUCache:
    cache_limit = None
      
    # if the DEBUG is TRUE then it
    # will execute
    DEBUG = False
   
    def __init__(self, func):
        self.func = func
        self.cache = {}
        self.head = Node(0, 0)
        self.tail = Node(0, 0)
        self.head.next = self.tail
        self.tail.prev = self.head
   
    def __call__(self, *args, **kwargs):
          
        # The cache presents with the help
        # of Linked List
        if args in self.cache:
            self.llist(args)
              
            if self.DEBUG == True:
                return f'Cached...{args}\n{self.cache[args]}\nCache: {self.cache}'
            return self.cache[args]
   
        # The given cache keeps on moving.
        if self.cache_limit is not None:
              
            if len(self.cache) > self.cache_limit:
                n = self.head.next
                self._remove(n)
                del self.cache[n.key]
   
        # Compute and cache and node to see whether 
        # the following element is present or not 
        # based on the given input.
        result = self.func(*args, **kwargs)
        self.cache[args] = result
        node = Node(args, result)
        self._add(node)
          
        if self.DEBUG == True:
            return f'{result}\nCache: {self.cache}'
        return result
   
    # Remove from double linked-list - Node.
    def _remove(self, node):
        p = node.prev
        n = node.next
        p.next = n
        n.prev = p
   
    # Add to double linked-list - Node.
    def _add(self, node):
        p = self.tail.prev
        p.next = node
        self.tail.prev = node
        node.prev = p
        node.next = self.tail
   
    # Over here the result task is being done 
    def llist(self, args):
        current = self.head
          
        while True:
              
            if current.key == args:
                node = current
                self._remove(node)
                self._add(node)
                  
                if self.DEBUG == True:
                    del self.cache[node.key]  
                    self.cache[node.key] = node.val 
                break
              
            else:
                current = current.next
   
   
# Default Debugging is FALSE. For 
# execution of DEBUG is set to TRUE
LRUCache.DEBUG = True
   
# The DEFAULT test limit is NONE.
LRUCache.cache_limit = 3
   
  
@LRUCache
def ex_func_01(n):
    print(f'Computing...{n}')
    time.sleep(1)
    return n
   
   
print(f'\nFunction: ex_func_01')
print(ex_func_01(1))
print(ex_func_01(2))
print(ex_func_01(3))
print(ex_func_01(4))
print(ex_func_01(1))
print(ex_func_01(2))
print(ex_func_01(5))
print(ex_func_01(1))
print(ex_func_01(2))
print(ex_func_01(3))
print(ex_func_01(4))
print(ex_func_01(5))

输出:

Function: ex_func_01
Computing...1
1
Cache: {(1,): 1}
Computing...2
2
Cache: {(1,): 1, (2,): 2}
Computing...3
3
Cache: {(1,): 1, (2,): 2, (3,): 3}
Computing...4
4
Cache: {(1,): 1, (2,): 2, (3,): 3, (4,): 4}
Cached...(1,)
1
Cache: {(2,): 2, (3,): 3, (4,): 4, (1,): 1}
Cached...(2,)
2
Cache: {(3,): 3, (4,): 4, (1,): 1, (2,): 2}
Computing...5
5
Cache: {(4,): 4, (1,): 1, (2,): 2, (5,): 5}
Cached...(1,)
1
Cache: {(4,): 4, (2,): 2, (5,): 5, (1,): 1}
Cached...(2,)
2
Cache: {(4,): 4, (5,): 5, (1,): 1, (2,): 2}
Computing...3
3
Cache: {(5,): 5, (1,): 1, (2,): 2, (3,): 3}
Computing...4
4
Cache: {(1,): 1, (2,): 2, (3,): 3, (4,): 4}
Computing...5
5
Cache: {(2,): 2, (3,): 3, (4,): 4, (5,): 5}