Replacement policies (LRU, FIFO, random)
Overview
When a cache is full and a new block needs to be loaded, the cache controller must decide which existing block to evict. This decision is made by the replacement policy. The three fundamental strategies are Least Recently Used (LRU), First-In First-Out (FIFO), and Random.
Why Replacement Policies Matter
The Problem: Cache capacity < Memory size. Temporal locality means recently-used data is likely to be used again. Spatial locality means nearby data is likely to be used soon. A replacement policy exploits these patterns to minimize miss rate.
Performance Impact:
- Miss rate directly affects average memory access time (AMAT):
- A 1% difference in miss rate can mean 10-20% performance difference in memory-bound applications
- Replacement policy affects miss rate but also has hardware cost
LRU (Least Recently Used)
How LRU Works
Mechanism:
- Every cache access updates a timestamp or reorders a recency list
- On eviction, choose the block with the oldest timestamp / at the tail of the list
- On hit or miss (after loading), mark that block as most recently used
Why this approach? Temporal locality: recently-accessed data is likely to be accessed again soon. The block that hasn't been touched longest has the lowest probability of being needed next.
Derivation of LRU Behavior
Consider a 2-way set-associative cache with blocks A and B in set.
Access sequence: A, B, A, C
| Access | Action | Recency Order (MRU → LRU) | Why this step? |
|---|---|---|---|
| A | Load A to way 0 | [A, -] | First access, empty cache |
| B | Load B to way 1 | [B, A] | B becomes MRU, A is now LRU |
| A | Hit on A | [A, B] | A becomes MRU again, B is now LRU |
| C | Evict B, load C to way 1 | [C, A] | B is LRU, so we replace it. C becomes MRU |
Key insight: Each access reorders the recency structure. The block that hasn't been touched longest is always at the "cold" end.
LRU Implementation
Challenge: Tracking recency has hardware cost.
Exact LRU (for small n):
- Counter method: Each block has a timestamp counter. On access, set counter to global time; increment global time. Evict block with smallest counter.
- Cost: n counters of bits each
- Pseudo-LRU (tree-based): Use bits to build a binary tree. Each bit indicates which subtree was accessed more recently. On access, flip bits along path from root to block.
- Cost: Only bits for n-way set
- Approximation: doesn't track exact LRU, but close enough
For 4-way set: Exact LRU needs 4 × 2 = 8 bits (2-bit counters). Pseudo-LRU needs 3 bits.
FIFO (First-In First-Out)
How FIFO Works
Mechanism:
- Maintain a queue pointer or timestamp indicating insertion order
- On eviction, choose the block that entered the cache first
- On hit, do nothing to the order (this is key difference from LRU)
Why FIFO? Simplicity. No need to track accesses, only insertion order.
Derivation of FIFO Behavior
Consider the same 2-way set, access sequence: A, B, A, C
| Access | Action | Queue Order (Head → Tail) | Why this step? |
|---|---|---|---|
| A | Load A | [A, -] | A enters first |
| B | Load B | [A, B] | B enters second (queue now [A, B]) |
| A | Hit on A | [A, B] | Order unchanged — FIFO ignores hits |
| C | Evict A, load C | [B, C] | A entered first, so evict A despite recent hit |
Critical difference from LRU: The hit on A doesn't move A to the back of the queue. FIFO is oblivious to access patterns after insertion.
FIFO Implementation
Hardware: Single -bit counter per set. Incredibly cheap.
For 4-way set: 2-bit counter. For 16-way: 4-bit counter.
Belady's Anomaly
Random Replacement
How Random Works
Mechanism:
- Use a pseudorandom number generator (PRNG) or linear feedback shift register (LFSR)
- On eviction, generate random index
- Evict block at way
Why random? When workload is unpredictable or adversarial, any deterministic policy can be exploited. Random avoids worst-case patterns.
Derivation of Random Behavior
For an n-way set, each block has eviction probability:
Expected number of accesses before evicting a specific block (if it's not accessed again):
Why this formula? Each eviction is an independent trial with success probability . This is a geometric distribution: expected number of trials = .
Random Implementation
Hardware: SmallLFSR (e.g., 4-bit for 16-way set) or use low bits of cycle counter. Extremely cheap: ~10 gates.
Comparison: LRU vs FIFO vs Random
| Policy | Miss Rate | Hardware Cost | Anomalies | Best For | |--------|---------------|-----------|----------| | LRU | Lowest for most workloads | Highest (counters/trees) | None (stack algorithm) | Temporal locality | | FIFO | Medium | Lowest (single counter) | Belady's Anomaly | Streaming / sequential access | | Random | Medium-high | Very low (LFSR) | None (probabilistic) | Adversarial / unpredictable workloads |
Why These Differences?
LRU exploits temporal locality:
- Recent accesses predict future accesses
- Best when working set fits in cache and exhibits reuse
- Cost: Must update state on every access
FIFO ignores access patterns:
- Only tracks insertion order
- Works okay for sequential scans (streaming data)
- Fails when blocks are reused after initial load
- Cost: Update only on evictions
Random avoids worst-case:
- No pathological access patterns can cause100% miss rate
- Performance is average across all patterns
- Cost: Minimal hardware, no state updates
Worked Example: Detailed Trace Comparison
Setup: 4-way set-associative cache, access sequence:
A, B, C, D, E, B, A, B, F, G
Let's trace all three policies:
LRU Trace
| Access | Cache State | Recency Order (MRU→LRU) | Hit/Miss | Why this step? |
|---|---|---|---|---|
| A | [A, -, -, -] | [A] | Miss | Cold start, load A |
| B | [A, B, -, -] | [B, A] | Miss | Load B, A ages by 1 |
| C | [A, B, C, -] | [C, B, A] | Miss | Load C and B age |
| D | [A, B, C, D] | [D, C, B, A] | Miss | Load D, cache now full |
| E | [E, B, C, D] | [E, D, C, B] | Miss | Evict A (LRU), load E |
| B | [E, B, C, D] | [B, E, D, C] | Hit | B moves to MRU |
| A | [E, B, A, D] | [A, B, E, D] | Miss | Evict C (LRU), load A |
| B | [E, B, A, D] | [B, A, E, D] | Hit | B moves to MRU |
| F | [E, B, A, F] | [F, B, A, E] | Miss | Evict D (LRU), load F |
| G | [G, B, A, F] | [G, F, B, A] | Miss | Evict E (LRU), load G |
Total: 8 misses, 2 hits
FIFO Trace
| Access | Cache State | Queue (head→tail) | QueuePtr | Hit/Miss | Why this step? |
|---|---|---|---|---|---|
| A | [A, -, -, -] | [A] | 1 | Miss | Load A to way 0 |
| B | [A, B, -, -] | [A, B] | 2 | Miss | Load B to way 1 |
| C | [A, B, C, -] | [A, B, C] | 3 | Miss | Load C to way 2 |
| D | [A, B, C, D] | [A, B, C, D] | 0 | Miss | Load D to way 3 |
| E | [E, B, C, D] | [B, C, D, E] | 1 | Miss | Evict A (ptr=0), load E |
| B | [E, B, C, D] | [B, C, D, E] | 1 | Hit | No queue change |
| A | [E, A, C, D] | [C, D, E, A] | 2 | Miss | Evict B (ptr=1), load A |
| B | [E, A, B, D] | [D, E, A, B] | 3 | Miss | Evict C (ptr=2), load B |
| F | [E, A, B, F] | [E, A, B, F] | 0 | Miss | Evict D (ptr=3), load F |
| G | [G, A, B, F] | [A, B, F, G] | 1 | Miss | Evict E (ptr=0), load G |
Total: 9 misses, 1 hit
Note: FIFO evicts B at step 7even though B was just accessed (hit). This is why FIFO mises on the second B access.
Random Trace
Assume PRNG outputs: [-, -, -, 1, -, 0, 3, 2, 1]
| Access | Cache State | Random Way | Hit/Miss | Why this step? | |--------|-------------|------------|----------------| | A | [A, -, -, -] | - | Miss | Load A to way 0 | | B | [A, B, -, -] | - | Miss | Load B to way 1 | | C | [A, B, C, -] | - | Miss | Load C to way 2 | | D | [A, B, C, D] | - | Miss | Load D to way 3 | | E | [A, E, C, D] | 1 (B) | Miss | Evict B (random), load E | | B | [A, E, C, D] | - | Miss | Load B to way 1 (just evicted!) | | A | [A, E, C, D] | - | Hit | A still in cache | | B | [A, E, C, B] | 3 (D) | Miss | Evict D (random), load B | | F | [A, E, F, B] | 2 (C) | Miss | Evict C (random), load F | | G | [A, G, F, B] | 1 (E) | Miss | Evict E (random), load G |
Total: 9 misses, 1 hit
Random happened to evict B right before it was needed again (unlucky), but avoided some of LRU's smart evictions by chance.
Summary
Miss rates:
- LRU: 80% (8/10 misses) — best performance
- FIFO: 90% (9/10 misses) — worst here due to evicting recently-hit B
- Random: 90% (9/10 misses) — comparable to FIFO
Key observation: LRU's advantage comes from the two hits on B. By keeping recently-accessed blocks, LRU avoids re-fetching B after the first hit. FIFO and Random don't track recent access, so they evict B and suffer mises.
Recall Explain to a 12-year-old
Imagine you have a tiny backpack that can only hold 4 school books, but you need to access 10 different books throughout the day.
LRU (Least Recently Used): Every time you use a book, you push it to the front of your backpack. The book at the very back is the one you haven't touched in the longest time. When you need a new book and your backpack is full, you remove the book at the back. This works great because you usually need the same books over and over, so keeping the ones you just used makes sense!
FIFO (First-In First-Out): You arrange your books in the order you first put them in. When you need a new book, you always remove the book you put in first, even if you just used it a minute ago! It's like a conveyor belt — first book in is the first book out. This is super easy to keep track of (just remember which book came first), but sometimes you remove books you actually need soon.
Random: You close your eyes and randomly pull out any book when you need space. No strategy at all! Sometimes you get lucky and remove book you won't need. Sometimes you remove a book you're about to use again. On average, it works okay, and you don't need to remember anything.
The best strategy depends on your pattern: if you reuse the same books a lot (like textbooks for homework), LRU wins. If you're just reading books once in order (like a library book list), FIFO is fine. If someone keeps changing which books you need in tricky ways, Random avoids being tricked.
Advanced: Practical Hybrid Policies
Real processors often use approximations:
Pseudo-LRU (PLRU)
- Tree-based bits to approximate LRU ordering
- Intel Core, AMD Ryzen use PLRU for L2/L3 caches
- Cost: bits for n-way, vs. for true LRU
Not Recently Used (NRU)
- Single reference bit per block, cleared periodically
- Evict any block with ref bit = 0
- Used in some TLBs and page tables
Clock Algorithm
- Circular buffer with a hand pointer
- On eviction, scan clockwise, clear ref bits, evict first block with ref=0
- Used in OS page replacement (approximates LRU)
Connections
- 5.4.01-Cache-organizationand-addressing — Replacement policy operates within a set
- 5.4.03-Write-policies-(write-through,-write-back) — Eviction must handle dirty blocks
- 5.4.06-Cache-coherence-protocols — Replacement interacts with coherence state
- 5.5.02-Virtual-memory-and-TLBs — Same replacement concepts apply to TLBs and page tables
- Temporal-and-spatial-locality — LRU exploits temporal locality; FIFO mises it
- Working-set-model — Optimal cache size depends on working set; LRU adapts better
- Cache-conscious-programming — Programmers can optimize for LRU by reusing data inner loops
#flashcards/hardware
What is a cache replacement policy and why is it needed? :: A policy that decides which block to evict when the cache is full and a new block must be loaded. Needed because cache capacity is limited and we must choose which blocks to keep to minimize miss rate.
What does LRU stand for and what does it evict?
Why does LRU generally have the lowest miss rate?
What is the hardware cost trade-off for LRU? :: LRU requires tracking recency for every access (counters or trees), making it the most expensive policy in terms of hardware complexity.
What does FIFO stand for and what does it evict?
Why is FIFO cheaper than LRU?
What is Belady's Anomaly?
Why doesn't LRU suffer from Belady's Anomaly?
What is the Random replacement policy?
When is Random replacement better than deterministic policies?
What is the eviction probability for each block under Random replacement in an n-way cache?
What is Pseudo-LRU (PLRU)?
How does FIFO behave differently from LRU when a block is accessed (hit)?
What is the recency counter update rule for LRU?
What is the formula for AMAT (Average Memory Access Time)?
What is a stack algorithm in the context of cache replacement?
What is the hardware implementation of FIFO?
What is the hardware implementation of Random?
In a 4-way cache with access sequence [A, B, C, D, E], which block does LRU evict on access E?
In the same scenario, which block does FIFO evict?
Why might FIFO have higher miss rate than LRU for workloads with reuse?
Concept Map
Hinglish (regional understanding)
Intuition Hinglish mein samjho
Dekho, jab tumhara cache full ho jata hai aur ek naya block load karna padta hai, tab controller ko decide karna padta hai ki purane blocks me se kaunsa nikaal dein — isko hi eviction bolte hain, aur ye decision jis rule se hota hai use replacement policy kehte hain. Simple example samjho: tumhare study desk pe sirf 4 books ki jagah hai, ab 5th book chahiye toh ek toh hatani padegi na? LRU bolta hai jo book sabse lambe time se nahi chhui use hatao, FIFO bolta hai jo sabse pehle rakhi thi use hatao, aur Random bolta hai bas koi bhi utha ke phenk do. Har policy ke apne trade-offs hain — kitna acha perform karti hai, kitna hardware cost aata hai, aur kitni complex hai.
Ab ye matter kyun karta hai? Kyunki cache ki size limited hoti hai (cost aur power ki wajah se), toh kaunse blocks rakhne hain ye decision seedha tumhare miss rate ko affect karta hai. Aur miss rate ka direct impact hota hai AMAT pe, jiska formula hai: AMAT = Hit Time + Miss Rate × Miss Penalty. Yaad rakho, sirf 1% ka miss rate difference bhi memory-heavy applications me 10-20% performance ka farak la sakta hai — toh ye chhoti si decision actually bahut bada impact rakhti hai.
LRU ka core intuition ye hai ki "agar tumne kuch der se koi cheez use nahi ki, toh probably wo jaldi chahiye bhi nahi hogi" — ye temporal locality ka faayda uthata hai. Har access pe ek recency order update hota hai, aur jo block sabse "cold" end pe hai (sabse purana) usko evict kar dete hain. Practically exact LRU implement karna mehenga hota hai kyunki har block ke liye counters maintain karne padte hain, isliye engineers Pseudo-LRU (tree-based) use karte hain jo thode kam bits me kaam chala leta hai aur "close enough" result deta hai. Bas yehi balance — accuracy vs hardware cost — real-world design me sabse important baat hai.