Skip to content

Instantly share code, notes, and snippets.

@vidarh
Last active January 5, 2024 12:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vidarh/b72615c16673cb38630cab5148190557 to your computer and use it in GitHub Desktop.
Save vidarh/b72615c16673cb38630cab5148190557 to your computer and use it in GitHub Desktop.
Ruby port of the SIEVE caching algorithm using a ring instead of a linked list
# See https://cachemon.github.io/SIEVE-website/blog/2023/12/17/sieve-is-simpler-than-lru/
# for a description and the Python version
# *LARGELY UNTESTED*
Node = Struct.new(:value, :visited, :prev, :next)
class SieveCache
def initialize(capacity)
@capacity = capacity
@cache = {}
@hand = @head = Node.new
@head.next = @head
@head.prev = @head
# Yes, this is a dirty trick.
def @head.visited = true
end
def add_to_head(node)
node.prev = @head
node.next = @head.next
@head.next.prev = node
@head.next = node
end
def remove_node(node)
node.prev.next = node.next
node.next.prev = node.prev
end
def evict
while @hand.visited
@hand.visited = false
@hand = @hand.prev
end
@cache.delete(@hand.value)
@hand = remove_node(@hand)
end
def access x
if @cache[x]
@cache[x].visited = true
return
end
evict if @cache.size == @capacity
add_to_head(@cache[x] = Node[x])
end
def show_cache
current = @head
until current.next == @head do
current = current.next
print "#{current.value} (Visited: #{current.visited ? "True" : "False"})"
print " -> " if current.next != @head
end
puts
end
end
# Example usage
cache = SieveCache.new(3)
cache.access('A')
cache.access('B')
cache.access('C')
cache.access('D')
cache.show_cache()
cache.access('C')
cache.access('E')
cache.show_cache()
# See https://cachemon.github.io/SIEVE-website/blog/2023/12/17/sieve-is-simpler-than-lru/
# for a description and the Python version
# *LARGELY UNTESTED*
#
# Single linked list version
Node = Struct.new(:value, :visited, :next)
class SieveCache
def initialize(capacity)
@capacity = capacity
@cache = {}
@prev = @hand = @head = Node.new
@head.next = @head
# Yes, this is a dirty trick.
def @head.visited = true
end
def add_to_head(node)
node.next = @head.next
@head.next = node
end
def evict
while @hand.visited
@hand.visited = false
@prev = @hand
@hand = @hand.next
end
@cache.delete(@hand.value)
@hand = @prev.next = @prev.next.next
end
def access x
if @cache[x]
@cache[x].visited = true
return
end
evict if @cache.size == @capacity
add_to_head(@cache[x] = Node[x])
end
def show_cache
current = @head
until current.next == @head do
current = current.next
print "#{current.value} (Visited: #{current.visited ? "True" : "False"})"
print " -> " if current.next != @head
end
puts
end
end
# Example usage
cache = SieveCache.new(3)
cache.access('A')
cache.access('B')
cache.access('C')
cache.access('D')
cache.show_cache()
cache.access('C')
cache.access('E')
cache.show_cache()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment