Page-replacement benchmarks

Measuring eviction-policy trade-offs in a demand-paged Pintos kernel — part of unix-kernel-subsystem.

The kernel pages on demand and chooses its eviction policy at boot (-evict=clock|fifo|random). These numbers are the kernel's own KSTAT counters over 10 seeds per configuration, collected by scripts/bench.sh and summarised by analyze.py. Design rationale is in ADR-003.

2.9×fewer evictions under clock than FIFO on page-parallel (4 MB)
3pages swapped back in under clock vs 320 under FIFO — clock keeps the hot set
∞FIFO thrashes page-merge-par past the time budget; clock finishes in ~3 s

Watch it run

A replay of the kernel's real output: a fork bomb contained, a CPU hog killed at its budget, a well-behaved process still finishing on time beside both, and demand-paged VM swapping a working set larger than RAM.

Recorded kernel output, replayed. Click to pause/resume.

Policy comparison at a fixed memory budget (4 MB)

At 4 MB every workload pages heavily, so the policy alone decides how much I/O the system does for the same program. Fewer swap-ins is better — a swap-in is a page the policy evicted and the program immediately needed back.

evictions and swap-ins by policy
Eviction work by policy. Clock (blue, highlighted) uses the accessed bit to keep pages still in use; FIFO and random pay far more swap traffic.
workloadpolicyokevictionsswap-insswap-outspage-faultsticks
page-merge-parclock10/10334179331732283±8
page-merge-parfifo0/5—————
page-merge-parrandom5/5301±15147±15299±15699±15286±13
page-parallelclock10/10164±13159±11050118±5
page-parallelfifo5/54803204791365179±9
page-parallelrandom5/5470±100284±93462±981333±95175±23

Paging curve: clock vs physical RAM

Holding the policy at clock and growing RAM shows the working set starting to fit — eviction collapses to zero once it is resident.

evictions vs physical RAM
Evictions fall to zero by 6 MB as the working set becomes resident.
workloadRAM (MB)okevictionsswap-inspage-faults
page-merge-par410/10334179732
page-merge-par55/5303556
page-merge-par65/500550
page-parallel410/10164±131050
page-parallel55/535±131050
page-parallel65/5001044
Running this curve originally surfaced two concurrency bugs in the paging code — a free-before-read race in swap_in and a load-during-eviction window — that only the benchmark's parallel stress triggered. Both are fixed; every memory size now passes. Write-up: BUGS-FOUND.md.

Theory: simulation against Belady's optimal

A trace-driven simulator (simulate.py) runs the same reference strings under FIFO, LRU, CLOCK and OPT (Belady's optimal, the unbeatable lower bound). It reproduces Belady's anomaly — FIFO faulting more with more frames — which LRU and OPT, as stack algorithms, never do.

page faults vs frames per policy
OPT is the floor; the gap above it is the cost of not knowing the future. Full tables in SIMULATION.md.