COMPUTER ORGANIZATION & ARCHITECTURE / 8. CACHE MEMORY
Cache Memory — Bridging the Speed Gap
L1/L2/L3 hierarchy, locality, hit rates, and why cache design matters for your code
EXPLANATION
The memory speed gap: CPUs operate at ~3 GHz (one operation every ~0.3ns). DRAM (main RAM) has ~100ns latency — 300× slower than the CPU. Without cache, the CPU would spend 99% of its time waiting for memory. Cache is the solution. Cache = small, fast SRAM (Static RAM) between the CPU and DRAM. The CPU checks cache first. If data is there (cache hit) → fast. If not (cache miss) → fetch from next level → slow. Principle of Locality — why cache works: - Temporal locality: recently accessed data will likely be accessed again soon. (Loop variables, hot code paths) - Spatial locality: if you access address X, you'll likely access X+1, X+2 soon. (Arrays, sequential code execution) Cache exploits BOTH: it stores recently used data (temporal) and fetches an entire cache line (64 bytes) per access, not just one byte (spatial). The Memory Hierarchy: - Registers: 1 cycle, ~1 KB total - L1 cache: 4 cycles, 32-64 KB per core, per-core private - L2 cache: 12 cycles, 256 KB - 1 MB per core, per-core private - L3 cache: 40 cycles, 8-32 MB, shared across all cores - DRAM (RAM): ~200 cycles, 16-64 GB - NVMe SSD: ~100,000 cycles Cache organization — Direct-Mapped Cache: Each memory block maps to exactly one cache line. Address is split into: [Tag | Index | Block Offset] - Block Offset: which byte within the 64-byte cache line - Index: which cache set (which line to look in) - Tag: the upper bits — checked to verify this is the right block Set-Associative Cache: - N-way set associative: each index maps to N possible lines - 4-way: any block can go in 1 of 4 lines in its set - Higher associativity → fewer conflicts → better hit rate - 8-way or 16-way is common for L2/L3 - Fully associative: any block anywhere (best hit rate, hardware-expensive) Write policies: - Write-through: write to cache AND memory simultaneously. Simple, always consistent. Memory bus becomes bottleneck. - Write-back: write only to cache, mark line as "dirty". Write to memory only when the line is evicted. Better performance, more complex. Replacement policies (when cache is full, which line to evict): - LRU (Least Recently Used): evict the line not used for longest time. Best hit rate, expensive to track exactly. - Pseudo-LRU: approximate LRU with a bit tree. Most real CPUs use this. - FIFO, Random: simpler, used in some L3 designs. Cache-friendly code: the difference between cache-friendly and cache-unfriendly code can be 10-100× performance difference. Row-major vs column-major access of a 2D array is the classic example — covered in the code section.
DIAGRAM
MEMORY HIERARCHY:
┌──────────────────────────────────────────────────────────┐
│ CPU Core │
│ Registers (1 cycle, 1KB) │
│ L1-I Cache L1-D Cache (4 cycles, 32KB each) │
│ └──────────┘ │
│ L2 Cache (12 cycles, 512KB) │
└──────────────────┬──────────────────────────────────────┘
│
L3 Cache (40 cycles, 32MB, shared)
│
DDR5 RAM (200 cycles, 32GB)
│
NVMe SSD (100,000 cycles)
DIRECT-MAPPED CACHE (4 lines, 4-byte blocks):
Address: [Tag 28b | Index 2b | Offset 2b]
Address 0x00 → index=0, offset=0
Address 0x08 → index=2, offset=0
Address 0x10 → index=0, offset=0 ← CONFLICT with 0x00!
4-WAY SET ASSOCIATIVE (4 sets, 4 ways each):
Same addresses → different ways in set 0. No conflict!
Set 0: [0x00][0x10][0x20][0x30] ← all fit!CODE