Impl part 3/3 for Memo
Refactor the caching policy for the Memo by evicting the elements that have been the least-recently-accessed. Python's heapq module default to a min-heap. By storing our heap elements as (UnixTime, a), we can guarantee that when we call heappop, we will get the element with the lowest UnixTime value in heap (i.e. the oldest). When we call heappush, we use (time.time(), key) and these values -- by having the largest UnixTime, will propogate to the bottom of the min-heap.
This commit is contained in:
parent
a8b3a2d3c0
commit
155dff562a
1 changed files with 10 additions and 29 deletions
|
@ -1,40 +1,19 @@
|
||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
from collections import deque
|
from heapq import heappush, heappop
|
||||||
|
|
||||||
|
|
||||||
class BoundedQueue(object):
|
|
||||||
def __init__(self, size=0):
|
|
||||||
"""
|
|
||||||
Returns a queue of elements that will never exceed `size`
|
|
||||||
members. BoundedQueue evicts the oldest elements from itself before
|
|
||||||
adding new elements.
|
|
||||||
"""
|
|
||||||
self.xs = deque([None] * size)
|
|
||||||
|
|
||||||
def add(self, x):
|
|
||||||
"""
|
|
||||||
Add element `x` to the end of the queue. Evict the oldest element from
|
|
||||||
the queue.
|
|
||||||
"""
|
|
||||||
evicted = None
|
|
||||||
if self.xs:
|
|
||||||
evicted = self.xs.popleft()
|
|
||||||
self.xs.append(x)
|
|
||||||
return evicted
|
|
||||||
|
|
||||||
|
|
||||||
class Memo(object):
|
class Memo(object):
|
||||||
def __init__(self, size=1):
|
def __init__(self, size=1):
|
||||||
"""
|
"""
|
||||||
Create a key-value data-structure that will never exceed `size`
|
Create a key-value data-structure that will never exceed `size`
|
||||||
members. Memo evicts the oldest elements from itself before adding
|
members. Memo evicts the least-recently-accessed elements from itself
|
||||||
inserting new key-value pairs.
|
before adding inserting new key-value pairs.
|
||||||
"""
|
"""
|
||||||
if size <= 0:
|
if size <= 0:
|
||||||
raise Exception("We do not support an empty memo")
|
raise Exception("We do not support an empty memo")
|
||||||
self.xs = {}
|
self.xs = {}
|
||||||
self.q = BoundedQueue(size=size)
|
self.heap = [(0, None)] * size
|
||||||
|
|
||||||
def contains(self, k):
|
def contains(self, k):
|
||||||
"""
|
"""
|
||||||
|
@ -46,19 +25,21 @@ class Memo(object):
|
||||||
"""
|
"""
|
||||||
Return the memoized item at key `k`.
|
Return the memoized item at key `k`.
|
||||||
"""
|
"""
|
||||||
|
# "touch" the element in the heap
|
||||||
return self.xs[k]
|
return self.xs[k]
|
||||||
|
|
||||||
def set(self, k, v):
|
def set(self, k, v):
|
||||||
"""
|
"""
|
||||||
Memoize value `v` at key `k`.
|
Memoize value `v` at key `k`.
|
||||||
"""
|
"""
|
||||||
evicted = self.q.add(k)
|
_, to_evict = heappop(self.heap)
|
||||||
if evicted != None:
|
if to_evict != None:
|
||||||
del self.xs[evicted]
|
del self.xs[to_evict]
|
||||||
|
heappush(self.heap, (time.time(), k))
|
||||||
self.xs[k] = v
|
self.xs[k] = v
|
||||||
|
|
||||||
|
|
||||||
memo = Memo(size=3)
|
memo = Memo(size=10)
|
||||||
|
|
||||||
|
|
||||||
def f(x):
|
def f(x):
|
||||||
|
|
Loading…
Reference in a new issue