Exercises — Replacement policies — LRU, LFU, FIFO, Random
Before starting, make sure you're comfortable with the vocabulary from Cache Memory Fundamentals, Temporal and Spatial Locality, and AMAT and Memory Hierarchy. Where the cache is a set rather than the whole cache, that's Set-Associative Mapping — the replacement policy always chooses a victim inside one set.
Level 1 — Recognition
Exercise 1.1
State, in one sentence each, which block LRU, LFU, FIFO, and Random evict when a full cache takes a miss.
Recall Solution
- LRU: the block unused for the longest time (largest gap since its last access).
- LFU: the block with the smallest access-count (least frequently used so far).
- FIFO: the block that entered the cache earliest, no matter how recently it was used.
- Random: a uniformly random block — every resident block equally likely.
Why these four and not one universal rule? Each is a different guess about the unknowable future. There's no free lunch: the only policy that is provably best is Belady's OPT, and it needs future knowledge, so it can't be built.
Exercise 1.2
A cache takes a hit on block . For each policy, does the internal bookkeeping change?
Recall Solution
| Policy | Does a hit change bookkeeping? | What changes |
|---|---|---|
| LRU | Yes | becomes most-recently-used (moves to the MRU end) |
| LFU | Yes | 's counter |
| FIFO | No | insertion order is frozen |
| Random | No | it keeps no metadata at all |
This single table is the seed of every "LRU vs FIFO" trap below: the difference lives on hits, not misses.
Level 2 — Application
Capacity = 3 throughout this level. Access sequence:
Exercise 2.1
Run LRU. Report the hit count.
Recall Solution
Write the cache MRU→LRU (left = most-recently-used, right = least-recently-used, as defined above). A hit moves the block to the left end (MRU); a miss on a full cache evicts the rightmost (LRU) block.
| Step | Access | Cache (MRU→LRU) | Hit/Miss | Evicted |
|---|---|---|---|---|
| 1 | A | A | miss | – |
| 2 | B | B,A | miss | – |
| 3 | C | C,B,A | miss | – |
| 4 | A | A,C,B | hit | – |
| 5 | D | D,A,C | miss | B |
| 6 | B | B,D,A | miss | C |
| 7 | E | E,B,D | miss | A |
| 8 | A | A,E,B | miss | D |
Hits = 1 (only step 4). Misses = 7.
Look at step 5: A was touched at step 4, so it sits ahead (nearer the MRU/left end) of C; B had not been touched since step 2, so B is at the LRU/right end and is the victim. That's the whole rule in action.
Exercise 2.2
Run FIFO on the same sequence. Report the hit count.
Recall Solution
Queue shown oldest→newest (left = oldest by insertion, right = newest). Note this is a different order from LRU's recency order — FIFO ranks by when a block arrived, and hits do not reorder anything.
| Step | Access | Queue (oldest→newest) | Hit/Miss | Evicted |
|---|---|---|---|---|
| 1 | A | A | miss | – |
| 2 | B | A,B | miss | – |
| 3 | C | A,B,C | miss | – |
| 4 | A | A,B,C | hit | – (order unchanged) |
| 5 | D | B,C,D | miss | A |
| 6 | B | B,C,D | hit | – |
| 7 | E | C,D,E | miss | B |
| 8 | A | D,E,A | miss | C |
Hits = 2 (steps 4 and 6). Misses = 6.
Contrast with LRU: at step 5 LRU kept A (just used) and evicted B, but FIFO evicted A because insertion order never noticed the step-4 hit. Yet FIFO got lucky at step 6 (B was still queued), so FIFO ends with more hits here — a reminder that "smarter" LRU is not guaranteed to win on every trace.
Exercise 2.3
Run LFU on the same sequence. On a tie in frequency, evict the block that has been in the cache longest (FIFO tie-break). Report the hit count.
Recall Solution
Track a counter per resident block (counter = number of accesses while resident; a fresh load starts at 1).
| Step | Access | Cache with counts | Hit/Miss | Evicted |
|---|---|---|---|---|
| 1 | A | A:1 | miss | – |
| 2 | B | A:1, B:1 | miss | – |
| 3 | C | A:1, B:1, C:1 | miss | – |
| 4 | A | A:2, B:1, C:1 | hit | – |
| 5 | D | A:2, D:1, C:1 | miss | B (tie B:1,C:1 → B older) |
| 6 | B | A:2, D:1, B:1 | miss | C (tie D:1,C:1 → C older) |
| 7 | E | A:2, E:1, B:1 | miss | D (tie D:1,B:1 → D older) |
| 8 | A | A:3, E:1, B:1 | hit | – |
Hits = 2 (steps 4 and 8). Misses = 6.
Notice A survives everything: its count climbs (2, then 3) so it is never the minimum. That's LFU's whole personality — it protects the popular block.
Level 3 — Analysis
Exercise 3.1 — Belady's anomaly, measured
For the trace under FIFO, count the misses with 3 frames and with 4 frames. Confirm the anomaly.
Recall Solution
3 frames (queue oldest→newest):
| Ref | Queue after | Miss? |
|---|---|---|
| 1 | 1 | ✓ |
| 2 | 1,2 | ✓ |
| 3 | 1,2,3 | ✓ |
| 4 | 2,3,4 | ✓ (evict 1) |
| 1 | 3,4,1 | ✓ (evict 2) |
| 2 | 4,1,2 | ✓ (evict 3) |
| 5 | 1,2,5 | ✓ (evict 4) |
| 1 | 1,2,5 | hit |
| 2 | 1,2,5 | hit |
| 3 | 2,5,3 | ✓ (evict 1) |
| 4 | 5,3,4 | ✓ (evict 2) |
| 5 | 5,3,4 | hit |
3 frames → 9 misses.
4 frames:
| Ref | Queue after | Miss? |
|---|---|---|
| 1 | 1 | ✓ |
| 2 | 1,2 | ✓ |
| 3 | 1,2,3 | ✓ |
| 4 | 1,2,3,4 | ✓ |
| 1 | 1,2,3,4 | hit |
| 2 | 1,2,3,4 | hit |
| 5 | 2,3,4,5 | ✓ (evict 1) |
| 1 | 3,4,5,1 | ✓ (evict 2) |
| 2 | 4,5,1,2 | ✓ (evict 3) |
| 3 | 5,1,2,3 | ✓ (evict 4) |
| 4 | 1,2,3,4 | ✓ (evict 5) |
| 5 | 2,3,4,5 | ✓ (evict 1) |
4 frames → 10 misses. More frames, more misses — that's Belady's anomaly, and only FIFO-like policies can show it.
What the figure below shows: two bars, the left (blue) for a 3-frame FIFO cache at height 9, the right (red) for a 4-frame cache at height 10. The vertical axis is the FIFO miss count for the trace printed in green underneath. The yellow arrow points upward from the 3-frame bar to the taller 4-frame bar, labelled "+1 miss (should DROP!)" — driving home that giving FIFO more room here made things worse, the exact signature of the anomaly.

Read it as a warning label: for FIFO, the miss-vs-size curve is not guaranteed to slope downward.
Exercise 3.2 — Why LRU is anomaly-proof
Explain, using the stack property, why LRU can never show this anomaly.
Recall Solution
The stack property says: for the same trace, the contents of a size- LRU cache are always a subset of the size- cache, written .
Why LRU has it: LRU ranks blocks by a single recency order that does not depend on cache size. A size- cache keeps the top- most-recent blocks; a size- cache keeps the top-. The top- set is a subset of the top- set by construction.
The consequence: if a block is a hit in the smaller cache, it is present in the larger cache too (subset ⇒ present), so it is also a hit there. Hence misses can only stay the same or drop as size grows — never rise. FIFO fails this because its "which block leaves" depends on a queue whose evictions differ with size, breaking the subset relation. This is also why pseudo-LRU is worth approximating: it inherits most of LRU's good behaviour cheaply.
Level 4 — Synthesis
Exercise 4.1 — From miss rate to AMAT
Policy P1 gives a miss rate ; policy P2 gives . With ns and ns, compute AMAT for each and the percent improvement of P2 over P1.
Recall Solution
Use the boxed formula:
- P1: ns.
- P2: ns.
- Improvement: , i.e. about faster average access.
Why the small miss-rate change yields a big time change: the penalty ( ns) dwarfs the hit time ( ns), so the term dominates AMAT. Cutting from to is leverage.
Exercise 4.2 — Where does the time go?
For P1 above, what fraction of total AMAT is spent on miss penalty alone?
Recall Solution
Penalty contribution ns out of ns. Over four-fifths of average access time is "waiting for memory." That is why replacement quality matters at all — it's the only knob that touches the dominant term.
Level 5 — Mastery
Exercise 5.1 — Design a trap trace for FIFO vs LRU
Construct the shortest access sequence (capacity 3) on which LRU has strictly more hits than FIFO, and prove it by running both.
Recall Solution
Sequence: . Capacity 3.
LRU (MRU→LRU):
| Step | Access | Cache | Hit/Miss |
|---|---|---|---|
| 1 | A | A | miss |
| 2 | B | B,A | miss |
| 3 | C | C,B,A | miss |
| 4 | A | A,C,B | hit |
| 5 | D | D,A,C | miss (evict B) |
| 6 | A | A,D,C | hit |
LRU hits = 2.
FIFO (oldest→newest):
| Step | Access | Queue | Hit/Miss |
|---|---|---|---|
| 1 | A | A | miss |
| 2 | B | A,B | miss |
| 3 | C | A,B,C | miss |
| 4 | A | A,B,C | hit (no reorder) |
| 5 | D | B,C,D | miss (evict A) |
| 6 | A | C,D,A | miss (A was evicted!) |
FIFO hits = 1.
The mechanism: at step 4 both hit on A, but only LRU promoted A. When D arrives at step 5, FIFO evicts A (oldest in queue) while LRU keeps A (freshly used). So step 6's re-access to A is an LRU hit but a FIFO miss. LRU 2 hits, FIFO 1 hit — strict win. This is the minimal witness of "the difference lives on hits."
Exercise 5.2 — LFU pollution, quantified
Capacity 3. First run the "warm-up" five times, then the loop . Under LFU (ties broken by longest-resident), how many misses occur in the loop, and why does never leave?
Recall Solution
After warm-up: cache plus two empty slots.
| Step | Access | Cache (counts) | Hit/Miss | Evicted |
|---|---|---|---|---|
| – | X×5 | X:5 | – | – |
| 1 | Y | X:5, Y:1 | miss | – |
| 2 | Z | X:5, Y:1, Z:1 | miss | – |
| 3 | W | X:5, W:1, Z:1 | miss | Y (min tie Y:1,Z:1 → Y older) |
| 4 | Y | X:5, W:1, Y:1 | miss | Z (min tie Z:1,W:1 → Z older) |
| 5 | Z | X:5, Z:1, Y:1 | miss | W |
| 6 | W | X:5, W:1, Y:1 | miss | Z |
Loop misses = 6 out of 6 — a 0% hit rate on the working set!
Why is immortal: its count is always strictly above the working-set blocks' count , so is never the minimum and is never evicted. It squats one slot forever even though it's dead. This is cache pollution / stale popularity. Fix in real LFU: periodically age (decay) the counters so old fame fades. Compare with LRU, which would happily evict once it goes untouched.
Recall One-line self-check before you leave
LRU-vs-FIFO diverge on ::: a hit (LRU promotes, FIFO doesn't). Belady's anomaly can appear under ::: FIFO (not LRU/LFU, which are stack algorithms). AMAT keeps on a miss because ::: the cache is probed first, then memory. LFU's failure mode is ::: stale popularity / cache pollution, fixed by aging.