Skip to content

Instantly share code, notes, and snippets.

@emres
Created January 15, 2019 15:56
Show Gist options
  • Save emres/829450e58aa9e6eb48603951f32d81e1 to your computer and use it in GitHub Desktop.
Save emres/829450e58aa9e6eb48603951f32d81e1 to your computer and use it in GitHub Desktop.
Copy of ActiveState recipe: Adaptive Replacement Cache in Python
# Copy of ActiveState recipe: Adaptive Replacement Cache in Python
# Original URL, last visited on 15-Jan-2019:
# http://code.activestate.com/recipes/576532/
#
# For more details, please see the following:
# - http://u.cs.biu.ac.il/~wiseman/2os/2os/os2.pdf
# - https://www.usenix.org/conference/fast-03/arc-self-tuning-low-overhead-replacement-cache
# - https://www.usenix.org/conference/hotstorage15/workshop-program/presentation/santana
# - http://www.c0t0d0s0.org/archives/5329-Some-insight-into-the-read-cache-of-ZFS-or-The-ARC.html
# - https://blogs.oracle.com/roch/it-is-the-dawning-of-the-age-of-the-l2arc
# -
class Cache(object):
"""
>>> dec_cache = Cache(10)
>>> @dec_cache
... def identity(f):
... return f
>>> dummy = [identity(x) for x in range(20) + range(11,15) + range(20) +
... range(11,40) + [39, 38, 37, 36, 35, 34, 33, 32, 16, 17, 11, 41]]
>>> dec_cache.t1
deque([(41,)])
>>> dec_cache.t2
deque([(11,), (17,), (16,), (32,), (33,), (34,), (35,), (36,), (37,)])
>>> dec_cache.b1
deque([(31,), (30,)])
>>> dec_cache.b2
deque([(38,), (39,), (19,), (18,), (15,), (14,), (13,), (12,)])
>>> dec_cache.p
5
"""
def __init__(self, size):
self.cached = {}
self.c = size
self.p = 0
self.t1 = deque()
self.t2 = deque()
self.b1 = deque()
self.b2 = deque()
def replace(self, args):
if self.t1 and (
(args in self.b2 and len(self.t1) == self.p) or
(len(self.t1) > self.p)):
old = self.t1.pop()
self.b1.appendleft(old)
else:
old = self.t2.pop()
self.b2.appendleft(old)
del(self.cached[old])
def __call__(self, func):
def wrapper(*orig_args):
"""decorator function wrapper"""
args = orig_args[:]
if args in self.t1:
self.t1.remove(args)
self.t2.appendleft(args)
return self.cached[args]
if args in self.t2:
self.t2.remove(args)
self.t2.appendleft(args)
return self.cached[args]
result = func(*orig_args)
self.cached[args] = result
if args in self.b1:
self.p = min(
self.c, self.p + max(len(self.b2) / len(self.b1) , 1))
self.replace(args)
self.b1.remove(args)
self.t2.appendleft(args)
#print "%s:: t1:%s b1:%s t2:%s b2:%s p:%s" % (
# repr(func)[10:30], len(self.t1),len(self.b1),len(self.t2),
# len(self.b2), self.p)
return result
if args in self.b2:
self.p = max(0, self.p - max(len(self.b1)/len(self.b2) , 1))
self.replace(args)
self.b2.remove(args)
self.t2.appendleft(args)
#print "%s:: t1:%s b1:%s t2:%s b2:%s p:%s" % (
# repr(func)[10:30], len(self.t1),len(self.b1),len(self.t2),
# len(self.b2), self.p)
return result
if len(self.t1) + len(self.b1) == self.c:
if len(self.t1) < self.c:
self.b1.pop()
self.replace(args)
else:
del(self.cached[self.t1.pop()])
else:
total = len(self.t1) + len(self.b1) + len(
self.t2) + len(self.b2)
if total >= self.c:
if total == (2 * self.c):
self.b2.pop()
self.replace(args)
self.t1.appendleft(args)
return result
return wrapper
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment