LRU is the cache replacement algorithm that removes the least recently used data and stores the new data. Suppose we have a cache space of 10 memory frames. And each frame is filled with a file. Now if we want to store the new file, we need to remove the oldest file in the cache and add the new file. This is how LRU works.
LRU cache consists of Queue and Dictionary data structures.
- Queue: to store the most recently used to least recently used files
- Hash table: to store the file and its position in the cache
Refer to the below articles to get more information about the topic:
What is decorator?
A decorator is a function that takes a function as its only parameter and returns a function. This is helpful to “wrap” functionality with the same code over and over again.
Note: For more information, refer to Decorators in Python.
Now, after getting the basic idea about the LRU and Decorators in Python, let’s have a look at the implementation of the LRU cache Decorator in Python.
Python3
from collections import deque # LRU cache implementation class LRUCache: def __init__( self , size = 5 ): self .size = size self .container = deque() self . map = dict () def reallocate( self ): # to reallocate the hashmap for # every access of file access file # will reallocate the data in hashmap # according to the numbers position # in the container for every access, # hit and miss(evict) if len ( self .container) > 1 : for key, val in enumerate ( self .container): self . map [val] = key def access( self , val): # print("access "+str(val)) self .container.remove(val) self .container.appendleft(val) self .reallocate() def evict( self , val): # print("cache miss "+str(val)) if val in self . map : #del self.map[val] self .container.remove(val) else : x = self .container.pop() del self . map [x] self .normal_insert(val) def normal_insert( self , val): self .container.appendleft(val) self .reallocate() def insert( self , val): if val in self . map .keys(): # if value in present in # the hashmap then it is a hit. # access function will access the # number already present and replace # it to leftmost position self .access(val) else : # if value is not present in # the hashtable if ( len ( self . map .keys()) = = self .size): # if the size of the queue # is equal to capacity and # we try to insert the number, # then it is a miss then, # evict function will delete the # right most elements and insert # the latest element in the # leftmost position self .evict(val) else : # normal_insert function will normally # insert the data into the cache.. self .normal_insert(val) def print ( self ): lru_elements = [x for x in self .container] print (lru_elements) # definition of lru decorator def LRUDecorator(size): lru = LRUCache(size) def decorator(function): def functionality(num): lru.insert(num) lru. print () # to check the num pageframe(position) # uncomment the below statement # print(lur.map) print (num, function(num)) return functionality return decorator # Using LRU Decorator @LRUDecorator (size = 4 ) 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 )) |
Output:
Function: ex_func_01 [1] Computing...1 1 1 None [2, 1] Computing...2 2 2 None [3, 2, 1] Computing...3 3 3 None [4, 3, 2, 1] Computing...4 4 4 None [1, 4, 3, 2] Computing...1 1 1 None [2, 1, 4, 3] Computing...2 2 2 None [5, 2, 1, 4] Computing...5 5 5 None [1, 5, 2, 4] Computing...1 1 1 None [2, 1, 5, 4] Computing...2 2 2 None [3, 2, 1, 5] Computing...3 3 3 None [4, 3, 2, 1] Computing...4 4 4 None [5, 4, 3, 2] Computing...5 5 5 None