Untitled

mail@pastecode.io avatar
unknown
plain_text
10 days ago
3.0 kB
3
Indexable
Never
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)
Leave a Comment