Spaces:
Runtime error
Runtime error
Download cache_simulator.py from bowmanhan/gemm-visualizer: direct link, hf CLI and curl.
- Browser
- Download file 6.61 kB
-
https://huggingface.co/spaces/bowmanhan/gemm-visualizer/resolve/main/cache_simulator.py
- Command line
-
hf download hf://spaces/bowmanhan/gemm-visualizer/cache_simulator.py
-
curl -L -o cache_simulator.py https://huggingface.co/spaces/bowmanhan/gemm-visualizer/resolve/main/cache_simulator.py
6.61 kB
| """ | |
| Simple Cache Simulator for Memory Access Pattern Analysis | |
| Simulates a simple set-associative cache to analyze cache behavior | |
| for different GEMM loop orderings. | |
| """ | |
| import numpy as np | |
| from typing import List, Tuple, Dict | |
| from collections import OrderedDict | |
| class CacheSimulator: | |
| """Simulates a simple set-associative cache.""" | |
| def __init__(self, cache_size: int = 32768, line_size: int = 64, | |
| associativity: int = 8, element_size: int = 8): | |
| """ | |
| Initialize cache simulator. | |
| Args: | |
| cache_size: Total cache size in bytes (default: 32KB) | |
| line_size: Cache line size in bytes (default: 64B) | |
| associativity: Set associativity (default: 8-way) | |
| element_size: Size of each matrix element in bytes (default: 8B for double) | |
| """ | |
| self.cache_size = cache_size | |
| self.line_size = line_size | |
| self.associativity = associativity | |
| self.element_size = element_size | |
| # Calculate number of sets | |
| self.num_lines = cache_size // line_size | |
| self.num_sets = self.num_lines // associativity | |
| # Cache storage: dict of sets, each set is an OrderedDict (LRU) | |
| self.cache = {i: OrderedDict() for i in range(self.num_sets)} | |
| # Statistics | |
| self.hits = 0 | |
| self.misses = 0 | |
| self.accesses = 0 | |
| self.hit_rate_history = [] | |
| def reset(self): | |
| """Reset cache and statistics.""" | |
| self.cache = {i: OrderedDict() for i in range(self.num_sets)} | |
| self.hits = 0 | |
| self.misses = 0 | |
| self.accesses = 0 | |
| self.hit_rate_history = [] | |
| def _get_address(self, matrix_base: int, row: int, col: int, n: int) -> int: | |
| """ | |
| Calculate memory address for matrix[row][col]. | |
| Args: | |
| matrix_base: Base address of the matrix | |
| row: Row index | |
| col: Column index | |
| n: Matrix dimension | |
| Returns: | |
| Memory address | |
| """ | |
| # Assume row-major layout | |
| offset = (row * n + col) * self.element_size | |
| return matrix_base + offset | |
| def _parse_address(self, address: int) -> Tuple[int, int, int]: | |
| """ | |
| Parse address into tag, set index, and block offset. | |
| Returns: | |
| (tag, set_index, block_offset) | |
| """ | |
| block_offset = address % self.line_size | |
| set_index = (address // self.line_size) % self.num_sets | |
| tag = address // (self.line_size * self.num_sets) | |
| return tag, set_index, block_offset | |
| def access(self, address: int) -> bool: | |
| """ | |
| Simulate a cache access. | |
| Args: | |
| address: Memory address to access | |
| Returns: | |
| True if hit, False if miss | |
| """ | |
| tag, set_index, _ = self._parse_address(address) | |
| cache_set = self.cache[set_index] | |
| self.accesses += 1 | |
| hit = False | |
| if tag in cache_set: | |
| # Cache hit - move to end (most recently used) | |
| cache_set.move_to_end(tag) | |
| self.hits += 1 | |
| hit = True | |
| else: | |
| # Cache miss | |
| self.misses += 1 | |
| # Add to cache | |
| cache_set[tag] = True | |
| # Evict if set is full (LRU) | |
| if len(cache_set) > self.associativity: | |
| cache_set.popitem(last=False) # Remove least recently used | |
| # Record hit rate periodically | |
| if self.accesses % 100 == 0: | |
| self.hit_rate_history.append(self.get_hit_rate()) | |
| return hit | |
| def simulate_accesses(self, tracks: List[Tuple], matrix_size: int, | |
| matrix_bases: Dict[str, int] = None) -> Dict: | |
| """ | |
| Simulate cache behavior for a sequence of memory accesses. | |
| Args: | |
| tracks: List of access patterns from GEMMSimulator | |
| matrix_size: Size of matrices (n x n) | |
| matrix_bases: Base addresses for matrices A, B, C | |
| Returns: | |
| Dictionary with cache statistics | |
| """ | |
| if matrix_bases is None: | |
| # Default base addresses (like in the original code) | |
| matrix_bases = { | |
| 'A': 0x10000, | |
| 'B': 0x20000, | |
| 'C': 0x30000 | |
| } | |
| self.reset() | |
| for a_pos, b_pos, c_pos in tracks: | |
| # Access A[i][k] | |
| addr_a = self._get_address(matrix_bases['A'], a_pos[0], a_pos[1], matrix_size) | |
| self.access(addr_a) | |
| # Access B[k][j] | |
| addr_b = self._get_address(matrix_bases['B'], b_pos[0], b_pos[1], matrix_size) | |
| self.access(addr_b) | |
| # Access C[i][j] (read-modify-write, count as 2 accesses) | |
| addr_c = self._get_address(matrix_bases['C'], c_pos[0], c_pos[1], matrix_size) | |
| self.access(addr_c) | |
| self.access(addr_c) | |
| return self.get_statistics() | |
| def get_hit_rate(self) -> float: | |
| """Calculate current cache hit rate.""" | |
| if self.accesses == 0: | |
| return 0.0 | |
| return (self.hits / self.accesses) * 100 | |
| def get_statistics(self) -> Dict: | |
| """ | |
| Get cache statistics. | |
| Returns: | |
| Dictionary containing cache performance metrics | |
| """ | |
| return { | |
| 'total_accesses': self.accesses, | |
| 'hits': self.hits, | |
| 'misses': self.misses, | |
| 'hit_rate': self.get_hit_rate(), | |
| 'miss_rate': 100 - self.get_hit_rate(), | |
| 'hit_rate_history': self.hit_rate_history.copy(), | |
| 'cache_config': { | |
| 'cache_size': self.cache_size, | |
| 'line_size': self.line_size, | |
| 'associativity': self.associativity, | |
| 'num_sets': self.num_sets | |
| } | |
| } | |
| if __name__ == '__main__': | |
| # Test the cache simulator | |
| from gemm_simulator import GEMMSimulator | |
| print("Testing Cache Simulator...") | |
| # Create GEMM simulator | |
| gemm = GEMMSimulator(n=16, block_size=4) | |
| # Test different loop orders | |
| cache = CacheSimulator(cache_size=32768, line_size=64, associativity=8) | |
| for loop_order in ['ijk', 'kji']: | |
| tracks = gemm.simulate(loop_order, blocked=True) | |
| stats = cache.simulate_accesses(tracks, matrix_size=16) | |
| print(f"\n{loop_order.upper()} Loop Order:") | |
| print(f" Total accesses: {stats['total_accesses']}") | |
| print(f" Hits: {stats['hits']}") | |
| print(f" Misses: {stats['misses']}") | |
| print(f" Hit rate: {stats['hit_rate']:.2f}%") | |
| print("\n✓ Cache Simulator module working correctly!") | |