Untitled

 avatar
unknown
plain_text
a year ago
3.0 kB
10
Indexable
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Generic, TypeVar, Optional, Callable
from threading import Lock
from collections import deque

K = TypeVar('K')
V = TypeVar('V')

class Cache(Generic[K, V], ABC):
    @abstractmethod
    def get(self, key: K) -> Optional[V]:  
        pass

    @abstractmethod
    def put(self, key: K, value: V) -> None:
        pass

class CacheEvictionStrategy(Generic[K],ABC):
    @abstractmethod
    def get_key(self, key: K) -> None:
        pass
    @abstractmethod
    def put_key(self, key: K) -> None:
        pass
    @abstractmethod
    def evict_key(self) -> K:
        pass

@dataclass
class CacheSet(Generic[K, V]):  
    mapping: dict[K,V]
    key_eviction_strategy: CacheEvictionStrategy[K]
    lock: Lock = field(default_factory=Lock)

class NWaySetAssociativeCache(Cache[K, V]): 
    def __init__(self, set_count: int, set_limit: int, cache_eviction_strategy_provider: Callable[[], CacheEvictionStrategy]) -> None:
        self.cache_sets = [CacheSet(dict(), cache_eviction_strategy_provider(), Lock()) for _ in range(set_count)] 
        self.set_limit = set_limit

    def _get_set_index(self, key: K) -> int:
        return hash(key) % len(self.cache_sets)

    def get(self, key: K) -> Optional[V]:  
        set_index = self._get_set_index(key)
        cache_set = self.cache_sets[set_index]
        if key in cache_set.mapping:
            cache_set.key_eviction_strategy.get_key(key)
            return cache_set.mapping[key]
        return None

    def put(self, key: K, value: V) -> None: 
        set_index = self._get_set_index(key)
        cache_set = self.cache_sets[set_index]
        if key in cache_set.mapping:
            cache_set.key_eviction_strategy.get_key(key)
            return cache_set.mapping[key]
        if len(cache_set.mapping) == self.set_limit:
            eviction_key = cache_set.key_eviction_strategy.evict_key()
            cache_set.mapping.pop(eviction_key)
        cache_set.mapping[key] = value
        cache_set.key_eviction_strategy.put_key(key)

    def __str__(self) -> str:
        return ','.join([str(set_.mapping) for set_ in self.cache_sets])

class FIFOCacheEvictionStrategy(CacheEvictionStrategy, Generic[K]):
    def __init__(self) -> None:
        self.key_queue = deque()
    
    def get_key(self, key: K) -> None:
        pass

    def put_key(self, key: K) -> None:
        self.key_queue.append(key)
    
    def evict_key(self) -> K:
        return self.key_queue.popleft()

lru_nway = NWaySetAssociativeCache(1, 3, lambda: FIFOCacheEvictionStrategy())
print(lru_nway)
lru_nway.put('a', 1)
print(lru_nway)
lru_nway.put('b', 2)
lru_nway.get('b')
print(lru_nway)
lru_nway.put('c', 3)
print(lru_nway)
lru_nway.put('d', 4)
print(lru_nway)
lru_nway.get('a')
print(lru_nway)
lru_nway.put('e', 4)
print(lru_nway)
lru_nway.put('f', 4)
print(lru_nway)
lru_nway.put('g', 4)
print(lru_nway)
lru_nway.get('b')
print(lru_nway)
lru_nway.get('b')
print(lru_nway)
lru_nway.get('b')
print(lru_nway)
Editor is loading...
Leave a Comment