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>
407 lines
14 KiB
Python
407 lines
14 KiB
Python
import asyncio
|
|
import time
|
|
from dataclasses import dataclass, field
|
|
from typing import Any, Dict, Generic, Optional, Set, TypeVar, Union
|
|
|
|
# Type variables for Generics
|
|
K = TypeVar("K")
|
|
V = TypeVar("V")
|
|
|
|
@dataclass
|
|
class Node(Generic[K, V]):
|
|
"""A node in the doubly linked list representing a cache entry."""
|
|
key: K
|
|
value: V
|
|
freq: int = 1
|
|
expiry: float = float('inf')
|
|
prev: Optional['Node[K, V]'] = None
|
|
next: Optional['Node[K, V]'] = None
|
|
|
|
class DoublyLinkedList(Generic[K, V]):
|
|
"""A standard Doubly Linked List to allow O(1) removal and insertion."""
|
|
def __init__(self):
|
|
self.head: Optional[Node[K, V]] = None
|
|
self.tail: Optional[Node[K, V]] = None
|
|
self.size: int = 0
|
|
|
|
def append(self, node: Node[K, V]):
|
|
"""Adds a node to the end of the list."""
|
|
if not self.head:
|
|
self.head = node
|
|
self.tail = node
|
|
node.prev = None
|
|
node.next = None
|
|
else:
|
|
node.prev = self.tail
|
|
node.next = None
|
|
if self.tail:
|
|
self.tail.next = node
|
|
self.tail = node
|
|
self.size += 1
|
|
|
|
def remove(self, node: Node[K, V]):
|
|
"""Removes a specific node from the list in O(1)."""
|
|
if node.prev:
|
|
node.prev.next = node.next
|
|
else:
|
|
self.head = node.next
|
|
|
|
if node.next:
|
|
node.next.prev = node.prev
|
|
else:
|
|
self.tail = node.prev
|
|
|
|
node.next = None
|
|
node.prev = None
|
|
self.size -= 1
|
|
|
|
def pop_tail(self) -> Optional[Node[K, V]]:
|
|
"""Removes and returns the last node in O(1)."""
|
|
if not self.tail:
|
|
return None
|
|
node = self.tail
|
|
self.remove(node)
|
|
return node
|
|
|
|
class Transaction(Generic[K, V]):
|
|
"""
|
|
Implements ACID-like sub-sessions.
|
|
Provides 'Read Your Own Writes' and isolation from the global cache.
|
|
"""
|
|
def __init__(self, cache: 'LFUCache[K, V]'):
|
|
self._cache = cache
|
|
self._pending_puts: Dict[K, tuple[V, float]] = {}
|
|
self._pending_deletes: Set[K] = set()
|
|
# Track keys read from the main cache to update frequency on commit
|
|
self._read_cache_keys: Set[K] = set()
|
|
self._is_active = True
|
|
|
|
async def get(self, key: K) -> Optional[V]:
|
|
if not self._is_active: raise RuntimeError("Transaction closed")
|
|
|
|
# 1. Check local deletes (Tombstone)
|
|
if key in self._pending_deletes:
|
|
return None
|
|
# 2. Check local writes (Read Your Own Writes)
|
|
if key in self._pending_puts:
|
|
val, _ = self._pending_puts[key]
|
|
return val
|
|
# 3. Check global cache (without affecting LFU frequency until commit)
|
|
async with self._cache._lock:
|
|
node = self._cache.cache_data.get(key)
|
|
if node:
|
|
# Lazy TTL check even during transaction get
|
|
if time.monotonic() >= node.expiry:
|
|
self._cache._remove_node_from_structures(node)
|
|
return None
|
|
self._read_cache_keys.add(key)
|
|
return node.value
|
|
return None
|
|
|
|
async def put(self, key: K, value: V, ttl_seconds: float = float('inf')):
|
|
if not self._is_active: raise RuntimeError("Transaction closed")
|
|
expiry = time.monotonic() + ttl_seconds
|
|
self._pending_puts[key] = (value, expiry)
|
|
if key in self._pending_deletes:
|
|
self._pending_deletes.remove(key)
|
|
|
|
async def delete(self, key: K):
|
|
if not self._is_active: raise RuntimeError("Transaction closed")
|
|
if key in self._pending_puts:
|
|
del self._pending_puts[key]
|
|
self._pending_deletes.add(key)
|
|
|
|
async def commit(self):
|
|
if not self._is_active: raise RuntimeError("Transaction closed")
|
|
async with self._cache._lock:
|
|
# 1. Process Deletes
|
|
for key in self._pending_deletes:
|
|
node = self._cache.cache_data.get(key)
|
|
if node:
|
|
self._cache._remove_node_from_structures(node)
|
|
|
|
# 2. Process Reads (Update frequencies for keys read from global cache)
|
|
for key in self._read_cache_keys:
|
|
node = self._cache.cache_data.get(key)
|
|
if node: # Ensure it wasn't deleted by a pending delete in this TX
|
|
self._cache._increment_frequency(node)
|
|
|
|
# 3. Process Puts
|
|
for key, (val, expiry) in self._pending_puts.items():
|
|
# Internal un-locked put for use within the lock context of commit
|
|
self._cache._internal_put(key, val, expiry)
|
|
|
|
self._is_active = False
|
|
self._pending_puts.clear()
|
|
|
|
async def rollback(self):
|
|
if not self._is_active: raise RuntimeError("Transaction closed")
|
|
self._pending_puts.clear()
|
|
self._pending_deletes.clear()
|
|
self._read_cache_keys.clear()
|
|
self._is_active = False
|
|
|
|
class LFUCache(Generic[K, V]):
|
|
"""
|
|
In-Memory Concurrent LFU Cache.
|
|
Time Complexity: O(1) for get and put.
|
|
"""
|
|
def __init__(self, capacity: int):
|
|
if capacity <= 0: raise ValueError("Capacity must be > 0")
|
|
self.capacity = capacity
|
|
self.cache_data: Dict[K, Node[K, V]] = {}
|
|
self.freq_map: Dict[int, DoublyLinkedList[K, V]] = {}
|
|
self.min_freq: int = 0
|
|
self._lock = asyncio.Lock()
|
|
self._evictor_task: Optional[asyncio.Task] = None
|
|
|
|
async def start_evictor(self, interval: float = 1.0):
|
|
"""Starts the background async eviction loop."""
|
|
if self._evictor_task is None:
|
|
self._evictor_task = asyncio.create_task(self._background_eviction_loop(interval))
|
|
|
|
async def stop_evictor(self):
|
|
"""Stops the background async eviction loop."""
|
|
if self._evictor_task:
|
|
self._evictor_task.cancel()
|
|
try:
|
|
await self._evictor_task
|
|
except asyncio.CancelledError:
|
|
pass
|
|
self._evictor_task = None
|
|
|
|
async def _background_eviction_loop(self, interval: float):
|
|
while True:
|
|
await asyncio.sleep(interval)
|
|
# We do not hold the lock for the entire loop to prevent blocking.
|
|
# Instead, we take a snapshot of keys and process in batches.
|
|
async with self._lock:
|
|
keys_snapshot = list(self.cache_data.keys())
|
|
|
|
# Process in small batches to yield control
|
|
batch_size = 50
|
|
for i in range(0, len(keys_snapshot), batch_size):
|
|
batch = keys_snapshot[i : i + batch_size]
|
|
now = time.monotonic()
|
|
async with self._lock:
|
|
for key in batch:
|
|
node = self.cache_data.get(key)
|
|
if node and now >= node.expiry:
|
|
self._remove_node_from_structures(node)
|
|
|
|
def _remove_node_from_structures(self, node: Node[K, V]):
|
|
"""Internal: Removes node from freq_map and cache_data. O(1)."""
|
|
# Remove from DLL
|
|
dll = self.freq_map[node.freq]
|
|
dll.remove(node)
|
|
if dll.size == 0:
|
|
del self.freq_map[node.freq]
|
|
if self.min_freq == node.freq:
|
|
# This is a simplification; real min_freq update happens in _increment_frequency.
|
|
# If current min freq list is empty, we'll find the new min in _increment_frequency or put.
|
|
pass
|
|
|
|
# Remove from dict
|
|
if node.key in self.cache_data:
|
|
del self.cache_data[node.key]
|
|
|
|
def _increment_frequency(self, node: Node[K, V]):
|
|
"""Internal: Increases frequency of a node. O(1)."""
|
|
old_freq = node.freq
|
|
new_freq = old_freq + 1
|
|
node.freq = new_freq
|
|
|
|
# Remove from old DLL
|
|
old_dll = self.freq_map[old_freq]
|
|
old_dll.remove(node)
|
|
if old_dll.size == 0:
|
|
del self.freq_map[old_freq]
|
|
if self.min_freq == old_freq:
|
|
self.min_freq = new_freq
|
|
|
|
# Add to new DLL
|
|
if new_freq not in self.freq_map:
|
|
self.freq_map[new_freq] = DoublyLinkedList()
|
|
self.freq_map[new_freq].append(node)
|
|
|
|
def _internal_put(self, key: K, value: V, expiry: float):
|
|
"""Internal: The core LFU logic. Must be called within a lock."""
|
|
if key in self.cache_data:
|
|
node = self.cache_data[key]
|
|
node.value = value
|
|
node.expiry = expiry
|
|
self._increment_frequency(node)
|
|
else:
|
|
if len(self.cache_data) >= self.capacity:
|
|
# Evict LFU (min_freq list tail)
|
|
if self.min_freq in self.freq_map and self.freq_map[self.min_freq].size > 0:
|
|
victim = self.freq_map[self.min_freq].pop_tail()
|
|
if victim:
|
|
del self.cache_data[victim.key]
|
|
else:
|
|
# Fallback (should not happen with correct logic)
|
|
k_evict = next(iter(self.cache_data))
|
|
del self.cache_data[k_evict]
|
|
|
|
new_node = Node(key, value, freq=1, expiry=expiry)
|
|
self.cache_data[key] = new_node
|
|
if 1 not in self.freq_map:
|
|
self.freq_map[1] = DoublyLinkedList()
|
|
self.freq_map[1].append(new_node)
|
|
self.min_freq = 1
|
|
|
|
async def get(self, key: K) -> Optional[V]:
|
|
"""O(1) retrieval with lazy TTL eviction."""
|
|
async with self._lock:
|
|
node = self.cache_data.get(key)
|
|
if not node:
|
|
return None
|
|
|
|
# Lazy TTL Eviction
|
|
if time.monotonic() >= node.expiry:
|
|
self._remove_node_from_structures(node)
|
|
return None
|
|
|
|
self._increment_frequency(node)
|
|
return node.value
|
|
|
|
async def put(self, key: K, value: V, ttl_seconds: float = float('inf')):
|
|
"""O(1) insertion with lazy TTL eviction."""
|
|
expiry = time.monotonic() + ttl_seconds
|
|
async with self._lock:
|
|
self._internal_put(key, value, expiry)
|
|
|
|
async def delete(self, key: K):
|
|
"""O(1) deletion."""
|
|
async with self._lock:
|
|
node = self.cache_data.get(key)
|
|
if node:
|
|
self._remove_node_from_structures(node)
|
|
|
|
def begin_transaction(self) -> Transaction[K, V]:
|
|
return Transaction(self)
|
|
|
|
# ==========================================
|
|
# UNIT TESTS
|
|
# ==========================================
|
|
|
|
async def test_lfu_eviction():
|
|
print("Testing O(1) LFU Eviction Logic...")
|
|
cache = LFUCache[str, int](capacity=3)
|
|
await cache.put("a", 1) # freq 1
|
|
await cache.put("b", 2) # freq 1
|
|
await cache.put("c", 3) # freq 1
|
|
|
|
# Increase frequency of a and b
|
|
await cache.get("a") # freq 2
|
|
await cache.get("b") # freq 2
|
|
# c is still freq 1
|
|
|
|
await cache.put("d", 4) # Should evict 'c'
|
|
|
|
assert await cache.get("a") == 1
|
|
assert await cache.get("b") == 2
|
|
assert await cache.get("c") is None
|
|
assert await cache.get("d") == 4
|
|
print("✅ LFU Eviction Passed.")
|
|
|
|
async def test_ttl_eviction():
|
|
print("Testing Dual-Layer TTL Eviction...")
|
|
cache = LFUCache[str, int](capacity=10)
|
|
await cache.start_evictor(interval=0.1)
|
|
|
|
# Test Lazy Eviction
|
|
await cache.put("lazy", 100, ttl_seconds=0.2)
|
|
await asyncio.sleep(0.3)
|
|
assert await cache.get("lazy") is None, "Lazy eviction failed"
|
|
|
|
# Test Background Eviction
|
|
await cache.put("bg", 200, ttl_seconds=0.1)
|
|
assert await cache.get("bg") is not None, "Value should still be there for a millisecond"
|
|
await asyncio.sleep(0.4)
|
|
# Note: Background loop might not have run yet, but we check if it's gone
|
|
# Since background is a separate task, it should have cleared 'bg' by now.
|
|
assert await cache.get("bg") is None, "Background eviction failed"
|
|
|
|
await cache.stop_evictor()
|
|
print("✅ TTL Eviction Passed.")
|
|
|
|
async def test_transactions():
|
|
print("Testing Atomic Transactions...")
|
|
cache = LFUCache[str, int](capacity=5)
|
|
|
|
# Test Rollback
|
|
tx = cache.begin_transaction()
|
|
await tx.put("tx1", 10)
|
|
assert await cache.get("tx1") is None, "Uncommitted write visible!"
|
|
assert await tx.get("tx1") == 10, "Read Your Own Writes failed"
|
|
await tx.rollback()
|
|
assert await cache.get("tx1") is None
|
|
|
|
# Test Commit Visibility
|
|
tx = cache.begin_transaction()
|
|
await tx.put("tx2", 20)
|
|
await tx.commit()
|
|
assert await cache.get("tx2") == 20, "Commit visibility failed"
|
|
|
|
# Test Isolation/Tombstones
|
|
await cache.put("exists", 50)
|
|
tx = cache.begin_transaction()
|
|
await tx.delete("exists")
|
|
assert await tx.get("exists") is None, "Transaction delete failed"
|
|
assert await cache.get("exists") == 50, "Uncommitted delete visible!"
|
|
await tx.commit()
|
|
assert await cache.get("exists") is None, "Commit delete failed"
|
|
|
|
print("✅ Transactions Passed.")
|
|
|
|
async def test_stress():
|
|
print("Running Concurrency Stress Test (50 tasks)...")
|
|
cache = LFUCache[int, int](capacity=20)
|
|
await cache.start_evictor()
|
|
|
|
async def worker(worker_id: int):
|
|
for i in range(100):
|
|
key = (worker_id * 100) + (i % 30) # Overlapping keys to induce contention
|
|
op = i % 3
|
|
if op == 0:
|
|
await cache.put(key, i)
|
|
elif op == 1:
|
|
await cache.get(key)
|
|
else:
|
|
# Transactional stress
|
|
tx = cache.begin_transaction()
|
|
await tx.put(key, i)
|
|
if i % 5 == 0:
|
|
await tx.rollback()
|
|
else:
|
|
await tx.commit()
|
|
if i % 10 == 0:
|
|
await asyncio.sleep(0.01)
|
|
|
|
tasks = [worker(i) for i in range(50)]
|
|
await asyncio.gather(*tasks)
|
|
await cache.stop_evictor()
|
|
print("✅ Stress Test Completed (No crashes).")
|
|
|
|
async def main():
|
|
start_time = time.perf_counter()
|
|
try:
|
|
await test_lfu_eviction()
|
|
print("-" * 30)
|
|
await test_ttl_eviction()
|
|
print("-" * 30)
|
|
await test_transactions()
|
|
print("-" * 30)
|
|
await test_stress()
|
|
print("-" * 30)
|
|
print(f"All tests finished successfully in {time.perf_counter() - start_time:.2f}s")
|
|
except Exception as e:
|
|
print(f"❌ Tests failed with error: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|
|
|