New graded (11 total now):
gemma4-26b-a4b-8bit-mlx 82 Minor Flaws (tied top local; delta-based tx freq)
qwen3.6-27b-8bit-mlx 78 Minor Flaws (clean; anom. slow generation flagged)
qwen3-coder-30b-6bit-mlx 50 Critical (asyncio.Lock used with sync with -> crash)
Dashboard redesign:
- Bar chart is now the full-width hero row (was cramped half-width)
- 4 stat tiles squished 2x2 beside the radar up top
- Quant + Format are dedicated columns in the leaderboard (MLX/GGUF/CLOUD chips)
- New 'Format & Quant Showdown' panel: groups same-family variants so
GGUF-vs-MLX and quant-depth comparisons are side by side
- Bar-chart axis labels now include the quant so duplicate model names
are distinguishable, with rotation for readability
Co-Authored-By: Claude <noreply@anthropic.com>
336 lines
12 KiB
Python
336 lines
12 KiB
Python
import asyncio
|
|
import time
|
|
import weakref
|
|
from dataclasses import dataclass, field
|
|
from typing import Optional, Dict, Any, List, Set, Tuple
|
|
from collections import OrderedDict
|
|
import threading
|
|
|
|
# --- Data Structures ---
|
|
|
|
@dataclass
|
|
class CacheNode:
|
|
"""Doubly-linked node for LFU frequency buckets."""
|
|
key: str
|
|
value: Any
|
|
freq: int = 0
|
|
ttl: Optional[float] = None # Unix timestamp of expiry
|
|
prev: Optional['CacheNode'] = None
|
|
next: Optional['CacheNode'] = None
|
|
|
|
@dataclass
|
|
class FrequencyBucket:
|
|
"""A bucket of nodes with the same frequency."""
|
|
freq: int
|
|
head: CacheNode = field(default_factory=lambda: CacheNode("", None, 0)) # Dummy head
|
|
tail: CacheNode = field(default_factory=lambda: CacheNode("", None, 0)) # Dummy tail
|
|
nodes: Dict[str, CacheNode] = field(default_factory=dict)
|
|
|
|
def __post_init__(self):
|
|
self.head.next = self.tail
|
|
self.tail.prev = self.head
|
|
|
|
def add_node(self, node: CacheNode):
|
|
"""Add a node to the front of the bucket."""
|
|
node.next = self.head.next
|
|
node.prev = self.head
|
|
self.head.next.prev = node
|
|
self.head.next = node
|
|
self.nodes[node.key] = node
|
|
|
|
def remove_node(self, node: CacheNode):
|
|
"""Remove a node from the bucket."""
|
|
node.prev.next = node.next
|
|
node.next.prev = node.prev
|
|
del self.nodes[node.key]
|
|
|
|
def is_empty(self) -> bool:
|
|
return len(self.nodes) == 0
|
|
|
|
# --- Transaction Support ---
|
|
|
|
class Transaction:
|
|
def __init__(self, cache: 'InMemoryLFUCache'):
|
|
self.cache = cache
|
|
self._writes: Dict[str, Tuple[Any, Optional[float]]] = {} # key -> (value, ttl)
|
|
self._deletions: Set[str] = set()
|
|
self._committed = False
|
|
self._rolled_back = False
|
|
|
|
def get(self, key: str) -> Any:
|
|
if self._rolled_back or self._committed:
|
|
raise RuntimeError("Transaction is no longer active")
|
|
if key in self._deletions:
|
|
return None
|
|
if key in self._writes:
|
|
return self._writes[key][0]
|
|
return self.cache.get(key)
|
|
|
|
def put(self, key: str, value: Any, ttl_seconds: Optional[float] = None):
|
|
if self._rolled_back or self._committed:
|
|
raise RuntimeError("Transaction is no longer active")
|
|
self._writes[key] = (value, ttl_seconds)
|
|
|
|
def delete(self, key: str):
|
|
if self._rolled_back or self._committed:
|
|
raise RuntimeError("Transaction is no longer active")
|
|
self._deletions.add(key)
|
|
|
|
async def commit(self):
|
|
if self._rolled_back or self._committed:
|
|
raise RuntimeError("Transaction is no longer active")
|
|
await self.cache._commit_transaction(self)
|
|
self._committed = True
|
|
|
|
async def rollback(self):
|
|
if self._rolled_back or self._committed:
|
|
raise RuntimeError("Transaction is no longer active")
|
|
self._rolled_back = True
|
|
|
|
# --- Cache Implementation ---
|
|
|
|
class InMemoryLFUCache:
|
|
def __init__(self, capacity: int = 100):
|
|
self.capacity = capacity
|
|
self._data: Dict[str, CacheNode] = {} # Global cache data
|
|
self._freq_buckets: Dict[int, FrequencyBucket] = {}
|
|
self._min_freq = 0
|
|
self._lock = asyncio.Lock()
|
|
self._evictor_task: Optional[asyncio.Task] = None
|
|
self._evictor_running = False
|
|
self._transaction_lock = asyncio.Lock()
|
|
self._transactions: Dict[int, Transaction] = {}
|
|
self._transaction_counter = 0
|
|
|
|
def _create_bucket(self, freq: int) -> FrequencyBucket:
|
|
bucket = FrequencyBucket(freq)
|
|
self._freq_buckets[freq] = bucket
|
|
return bucket
|
|
|
|
def _get_bucket(self, freq: int) -> FrequencyBucket:
|
|
if freq not in self._freq_buckets:
|
|
return self._create_bucket(freq)
|
|
return self._freq_buckets[freq]
|
|
|
|
def _evict_lfu(self):
|
|
"""Evict the least frequently used item."""
|
|
if self._min_freq not in self._freq_buckets:
|
|
return None
|
|
bucket = self._freq_buckets[self._min_freq]
|
|
if bucket.is_empty():
|
|
return None
|
|
node_to_evict = bucket.tail.prev # Tail's previous is the last item
|
|
self._remove_node(node_to_evict)
|
|
return node_to_evict.key
|
|
|
|
def _remove_node(self, node: CacheNode):
|
|
"""Remove a node from its bucket and global map."""
|
|
bucket = self._get_bucket(node.freq)
|
|
bucket.remove_node(node)
|
|
if bucket.is_empty():
|
|
del self._freq_buckets[node.freq]
|
|
del self._data[node.key]
|
|
|
|
def _update_node_frequency(self, node: CacheNode):
|
|
"""Move node to the next frequency bucket and update min_freq if needed."""
|
|
old_freq = node.freq
|
|
node.freq += 1
|
|
new_freq = node.freq
|
|
bucket = self._get_bucket(old_freq)
|
|
bucket.remove_node(node)
|
|
if bucket.is_empty():
|
|
del self._freq_buckets[old_freq]
|
|
if old_freq == self._min_freq:
|
|
self._min_freq = new_freq
|
|
new_bucket = self._get_bucket(new_freq)
|
|
new_bucket.add_node(node)
|
|
|
|
def _is_expired(self, node: CacheNode) -> bool:
|
|
if node.ttl is None:
|
|
return False
|
|
return time.time() > node.ttl
|
|
|
|
def _check_and_remove_expired(self, node: CacheNode) -> bool:
|
|
"""Returns True if node was expired and removed."""
|
|
if self._is_expired(node):
|
|
self._remove_node(node)
|
|
return True
|
|
return False
|
|
|
|
async def get(self, key: str) -> Any:
|
|
async with self._lock:
|
|
if key not in self._data:
|
|
return None
|
|
node = self._data[key]
|
|
# Lazy TTL check
|
|
if self._check_and_remove_expired(node):
|
|
return None
|
|
self._update_node_frequency(node)
|
|
return node.value
|
|
|
|
async def put(self, key: str, value: Any, ttl_seconds: Optional[float] = None):
|
|
async with self._lock:
|
|
ttl = time.time() + ttl_seconds if ttl_seconds is not None else None
|
|
if key in self._data:
|
|
node = self._data[key]
|
|
# Lazy TTL check
|
|
if self._check_and_remove_expired(node):
|
|
node = None # Re-add it below
|
|
if node:
|
|
node.value = value
|
|
node.ttl = ttl
|
|
self._update_node_frequency(node)
|
|
return
|
|
# Add new node or replace existing expired one
|
|
node = CacheNode(key, value, freq=1, ttl=ttl)
|
|
self._data[key] = node
|
|
bucket = self._get_bucket(1)
|
|
bucket.add_node(node)
|
|
self._min_freq = 1
|
|
|
|
# Check capacity and evict if needed
|
|
if len(self._data) > self.capacity:
|
|
evicted_key = self._evict_lfu()
|
|
if evicted_key is not None:
|
|
# Ensure that the bucket is cleaned up if empty
|
|
pass
|
|
|
|
def begin_transaction(self) -> Transaction:
|
|
with self._transaction_lock:
|
|
self._transaction_counter += 1
|
|
tx = Transaction(self)
|
|
self._transactions[self._transaction_counter] = tx
|
|
return tx
|
|
|
|
async def _commit_transaction(self, tx: Transaction):
|
|
async with self._lock:
|
|
# Apply writes
|
|
for key, (value, ttl) in tx._writes.items():
|
|
await self.put(key, value, ttl)
|
|
# Apply deletions
|
|
for key in tx._deletions:
|
|
if key in self._data:
|
|
node = self._data[key]
|
|
self._remove_node(node)
|
|
|
|
async def start_evictor(self, interval_seconds: float = 5.0):
|
|
"""Start the background evictor task."""
|
|
async def _evict_loop():
|
|
while self._evictor_running:
|
|
try:
|
|
await asyncio.sleep(interval_seconds)
|
|
await self._evict_expired_batch()
|
|
except Exception:
|
|
pass # Silently ignore errors in background task
|
|
|
|
self._evictor_running = True
|
|
self._evictor_task = asyncio.create_task(_evict_loop())
|
|
|
|
async def stop_evictor(self):
|
|
"""Stop the background evictor task."""
|
|
self._evictor_running = False
|
|
if self._evictor_task:
|
|
self._evictor_task.cancel()
|
|
try:
|
|
await self._evictor_task
|
|
except asyncio.CancelledError:
|
|
pass
|
|
|
|
async def _evict_expired_batch(self, batch_size: int = 10):
|
|
"""Evict a batch of expired keys without holding the global lock for too long."""
|
|
to_remove = []
|
|
with self._lock:
|
|
# Collect expired nodes
|
|
for node in self._data.values():
|
|
if self._is_expired(node):
|
|
to_remove.append(node.key)
|
|
if len(to_remove) >= batch_size:
|
|
break
|
|
# Remove outside lock to avoid blocking readers
|
|
for key in to_remove:
|
|
async with self._lock:
|
|
if key in self._data:
|
|
node = self._data[key]
|
|
if self._is_expired(node):
|
|
self._remove_node(node)
|
|
|
|
# --- Unit Tests ---
|
|
|
|
async def main():
|
|
print("Running InMemoryLFUCache tests...")
|
|
|
|
# Test 1: O(1) LFU eviction
|
|
print("Test 1: LFU eviction")
|
|
cache = InMemoryLFUCache(capacity=3)
|
|
await cache.put("a", 1)
|
|
await cache.put("b", 2)
|
|
await cache.put("c", 3)
|
|
# Access a twice to make it more frequent
|
|
await cache.get("a")
|
|
await cache.get("a")
|
|
# Add d, should evict the least frequent (b)
|
|
await cache.put("d", 4)
|
|
assert await cache.get("b") is None
|
|
assert await cache.get("a") == 1
|
|
assert await cache.get("c") == 3
|
|
assert await cache.get("d") == 4
|
|
print("✓ LFU eviction works")
|
|
|
|
# Test 2: TTL eviction
|
|
print("Test 2: TTL eviction")
|
|
cache = InMemoryLFUCache(capacity=10)
|
|
await cache.put("a", 1, ttl_seconds=0.1) # Expire quickly
|
|
await asyncio.sleep(0.2)
|
|
assert await cache.get("a") is None
|
|
print("✓ TTL eviction works")
|
|
|
|
# Test 3: Transaction commit visibility
|
|
print("Test 3: Transaction visibility")
|
|
cache = InMemoryLFUCache(capacity=10)
|
|
tx = cache.begin_transaction()
|
|
tx.put("x", 100)
|
|
assert await cache.get("x") is None # Not committed yet
|
|
await tx.commit()
|
|
assert await cache.get("x") == 100 # Now visible
|
|
print("✓ Transaction commit works")
|
|
|
|
# Test 4: Transaction rollback
|
|
print("Test 4: Transaction rollback")
|
|
cache = InMemoryLFUCache(capacity=10)
|
|
await cache.put("y", 200)
|
|
tx = cache.begin_transaction()
|
|
tx.put("y", 300)
|
|
tx.delete("z") # z doesn't exist yet
|
|
await tx.rollback()
|
|
assert await cache.get("y") == 200 # Original value restored
|
|
print("✓ Transaction rollback works")
|
|
|
|
# Test 5: Concurrent access
|
|
print("Test 5: Concurrent access")
|
|
cache = InMemoryLFUCache(capacity=10)
|
|
async def worker(worker_id: int):
|
|
for i in range(5):
|
|
key = f"{worker_id}_{i}"
|
|
await cache.put(key, i)
|
|
val = await cache.get(key)
|
|
assert val == i
|
|
tasks = [asyncio.create_task(worker(i)) for i in range(50)]
|
|
await asyncio.gather(*tasks)
|
|
print("✓ Concurrent access works")
|
|
|
|
# Test 6: Background eviction
|
|
print("Test 6: Background eviction")
|
|
cache = InMemoryLFUCache(capacity=10)
|
|
await cache.put("expiring", 1, ttl_seconds=0.1)
|
|
await cache.start_evictor(interval_seconds=0.05)
|
|
await asyncio.sleep(0.2)
|
|
assert await cache.get("expiring") is None
|
|
await cache.stop_evictor()
|
|
print("✓ Background eviction works")
|
|
|
|
print("\nAll tests passed! ✅")
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|
|
|