codexpage-replacement-three-frame-trace.py
page-replacement-three-frame-trace.py
"""Generate page-replacement-three-frame-trace.png in the working directory.
Tested with Python 3.14.4, matplotlib 3.10.7 and numpy 2.3.5.
Run from _media with MPLBACKEND=Agg. This is a synthetic demand-paging model.
"""
from pathlib import Path
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
TRACE = (1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5)
POLICIES = ("FIFO", "LRU", "OPT", "CLOCK")
def simulate(policy, trace=TRACE, capacity=3, writes=()):
"""Fixed slots; FIFO arrival order, exact LRU, farthest-future OPT, or CLOCK.
CLOCK starts at slot zero; each miss scans from the hand, clears set bits,
installs with bit one and advances the hand. A hit sets its bit only.
OPT ties select the lowest slot. Writes are one-based reference indices.
Every fill is clean; writes mark dirty; no background or terminal writeback.
"""
if policy not in POLICIES or capacity < 1:
raise ValueError("Unknown policy or nonpositive capacity")
slots = [None] * capacity
bits = [0] * capacity
dirty = [False] * capacity
arrival, recency = [], []
hand = misses = writebacks = 0
rows = []
for i, page in enumerate(trace):
hit = page in slots
victim = None
if hit:
slot = slots.index(page)
else:
misses += 1
if policy == "CLOCK":
while slots[hand] is not None and bits[hand]:
bits[hand] = 0
hand = (hand + 1) % capacity
slot = hand
hand = (hand + 1) % capacity
elif None in slots:
slot = slots.index(None)
elif policy == "FIFO":
slot = slots.index(arrival[0])
elif policy == "LRU":
slot = slots.index(recency[0])
else:
def next_use(j):
return next((k for k in range(i + 1, len(trace))
if trace[k] == slots[j]), float("inf"))
slot = max(range(capacity), key=next_use)
victim = slots[slot]
writebacks += int(dirty[slot])
if victim is not None:
arrival.remove(victim)
recency.remove(victim)
slots[slot] = page
dirty[slot] = False
arrival.append(page)
if page in recency:
recency.remove(page)
recency.append(page)
bits[slot] = 1
if i + 1 in writes:
dirty[slot] = True
rows.append(dict(page=page, hit=hit, victim=victim, slots=tuple(slots),
bits=tuple(bits), hand=hand, misses=misses,
writebacks=writebacks, dirty=tuple(dirty),
arrival=tuple(arrival), recency=tuple(recency)))
return rows
def main():
plt.rcParams.update({"font.family": "DejaVu Sans", "font.size": 11})
fig, axes = plt.subplots(4, 1, figsize=(12, 11.2), dpi=100)
fig.patch.set_facecolor("white")
fig.suptitle("One page-reference trace, three initially empty frames", fontsize=17, y=.982)
fig.text(.5, .942, "Fixed frame positions; states AFTER each access. M = miss, H = hit.",
ha="center", fontsize=11)
for ax, policy in zip(axes, POLICIES):
rows = simulate(policy)
ax.set_xlim(-1.4, 12)
ax.set_ylim(6.6, -.75)
ax.axis("off")
ax.text(-1.3, -.45, policy, weight="bold", fontsize=13)
ax.text(12, -.45, f"{rows[-1]['misses']} misses", ha="right", weight="bold")
labels = ["Access index", "Reference", "Frame 1", "Frame 2", "Frame 3", "Outcome", "Evicted"]
for y, label in enumerate(labels):
ax.text(-.12, y, label, ha="right", va="center", fontsize=10)
for i, row in enumerate(rows):
values = [str(i + 1), str(row["page"])]
for slot, page in enumerate(row["slots"]):
value = "–" if page is None else str(page)
if policy == "CLOCK" and page is not None:
value += f"/{row['bits'][slot]}"
if policy == "CLOCK" and slot == row["hand"]:
value = "→" + value
values.append(value)
values += ["H" if row["hit"] else "M",
"–" if row["victim"] is None else str(row["victim"])]
for y, value in enumerate(values):
fill = "#eaf1f8" if y in (0, 1) else "white"
if y == 5:
fill = "#d9eee5" if row["hit"] else "#f8dfd7"
ax.add_patch(Rectangle((i, y - .44), .99, .88,
facecolor=fill, edgecolor="#ccd2d8", linewidth=.6))
ax.text(i + .5, y, value, ha="center", va="center", fontsize=11)
fig.text(.5, .032, "CLOCK: cell = page / reference bit; → marks the NEXT hand position.",
ha="center", fontsize=11)
fig.text(.5, .012, "OPT: ties choose the lowest frame number. No prefetch, writes or external eviction.",
ha="center", fontsize=10)
fig.subplots_adjust(left=.12, right=.98, top=.912, bottom=.068, hspace=.28)
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()