diff --git a/app/cache/eviction_sieve.py b/app/cache/eviction_sieve.py index 4fd0fb7..fc50523 100644 --- a/app/cache/eviction_sieve.py +++ b/app/cache/eviction_sieve.py @@ -11,45 +11,49 @@ class Node: class SieveCache(Cache): def __init__(self, limit: int): super().__init__(limit) - self.limit = limit # Fix: Store limit properly + self.limit = limit self.cache = {} # Hash map for O(1) access self.head = None self.tail = None - self.hand = None + self.hand = None # Pointer for eviction - def print_cache_state(self): - #print("Current cache state:") - node = self.head - if not node: - #print("Cache is empty.") - return - for _ in range(len(self.cache)): - #print(f"Key: {node.key}, Value: {node.value}, Visited: {node.visited}") - node = node.next + def invalidate(self, key: str) -> bool: + """Removes a specific key from cache if it exists.""" + if key in self.cache: + node = self.cache.pop(key) + if node == self.head: - break + self.head = node.next + if node == self.tail: + self.tail = node.prev + if node.next: + node.next.prev = node.prev + if node.prev: + node.prev.next = node.next + + return True # Successfully invalidated + + return False # Key not found def get(self, key: str) -> str: if key in self.cache: node = self.cache[key] - node.visited = True - #self.print_cache_state() + node.visited = True # Mark node as accessed return node.value - self.print_cache_state() return None - + def put(self, key: str, val: str) -> bool: if key in self.cache: node = self.cache[key] node.value = val node.visited = True - #self.print_cache_state() return False # No eviction needed new_node = Node(key, val) if len(self.cache) >= self.limit: self.evict() + # Insert new node in circular doubly linked list if not self.head: self.head = self.tail = new_node new_node.next = new_node.prev = new_node @@ -62,54 +66,31 @@ class SieveCache(Cache): self.cache[key] = new_node if not self.hand: - self.hand = self.head - #self.print_cache_state() + self.hand = self.head # Initialize hand pointer return False - - def invalidate(self, key: str) -> bool: - if key in self.cache: - node = self.cache.pop(key) - if node == self.head: - self.head = node.next - if node == self.tail: - self.tail = node.prev - if node.next: - node.next.prev = node.prev - if node.prev: - node.prev.next = node.next - #self.print_cache_state() - return True - return False - - def next_hand(self): - self.hand = self.hand.next if self.hand.next else self.head - + def evict(self): + if not self.hand: + return # No elements to evict + + # Find the first unvisited node to evict while self.hand.visited: - self.hand.visited = False - self.next_hand() + self.hand.visited = False # Reset visited flag + self.hand = self.hand.next # Move to next node + obj_to_evict = self.hand - self.next_hand() - + self.hand = self.hand.next # Move hand forward + + # Remove from cache dictionary if exists + if obj_to_evict.key in self.cache: + del self.cache[obj_to_evict.key] + + # Evict the node from linked list if obj_to_evict == self.head: self.head = obj_to_evict.next if obj_to_evict == self.tail: self.tail = obj_to_evict.prev - if obj_to_evict.next: - obj_to_evict.next.prev = obj_to_evict.prev - if obj_to_evict.prev: - obj_to_evict.prev.next = obj_to_evict.next - - del self.cache[obj_to_evict.key] - #self.print_cache_state() -# Basic API demo for future testing -if __name__ == "__main__": - cache = SeiveCache(3) - cache.put("a", "1") - cache.put("b", "2") - cache.put("c", "3") - cache.get("a") - cache.put("d", "4") # Should evict "b" - assert "b" not in cache.cache, f"Eviction failed, cache contents: {cache.cache.keys()}" - print("SeiveCache eviction test passed.") + obj_to_evict.prev.next = obj_to_evict.next + obj_to_evict.next.prev = obj_to_evict.prev + diff --git a/tests/results_sieve b/tests/results_sieve new file mode 100644 index 0000000..72e3344 --- /dev/null +++ b/tests/results_sieve @@ -0,0 +1,47 @@ +--- random_read Results --- +hits: 1019 misses: 8981 ratio: 0.1019 +average response time (ms) : 13.882123780250549 +average cache hit response time (ms) : 0.0010722986732310707 +average cache miss response time (ms): 15.457092209125651 +cache throughput (requests / s) : 72.03508741383314 +real throughput (requests / s) : 63.77470400544125 + +--- read_heavy Results --- +hits: 803 misses: 7222 ratio: 0.10006230529595016 +average response time (ms) : 13.242761249482818 +average cache hit response time (ms) : 0.000984849846674823 +average cache miss response time (ms): 14.715088367858312 +cache throughput (requests / s) : 75.51295240930618 +real throughput (requests / s) : 58.581188594379874 + +--- write_heavy Results --- +hits: 210 misses: 1808 ratio: 0.10406342913776016 +average response time (ms) : 12.672739766161554 +average cache hit response time (ms) : 0.0010308765229724702 +average cache miss response time (ms): 14.14456436064391 +cache throughput (requests / s) : 78.9095348324106 +real throughput (requests / s) : 24.94878943653593 + +--- frequent_users Results --- +hits: 7235 misses: 2765 ratio: 0.7235 +average response time (ms) : 4.018647408485412 +average cache hit response time (ms) : 0.0006961427067754675 +average cache miss response time (ms): 14.532165458361883 +cache throughput (requests / s) : 248.83994497464258 +real throughput (requests / s) : 181.31597211320386 + +--- frequent_after_write Results --- +hits: 1103 misses: 3827 ratio: 0.22373225152129816 +average response time (ms) : 11.080196082712913 +average cache hit response time (ms) : 0.0008352215681309497 +average cache miss response time (ms): 14.273437532893913 +cache throughput (requests / s) : 90.2511104077101 +real throughput (requests / s) : 50.809783230994924 + +--- weighted_friend_readonly Results --- +hits: 3403 misses: 6597 ratio: 0.3403 +average response time (ms) : 9.799546551704406 +average cache hit response time (ms) : 0.000995711092033913 +average cache miss response time (ms): 14.854036245596161 +cache throughput (requests / s) : 102.04553799747733 +real throughput (requests / s) : 86.61266112308168