codexcache-line-traversal-misses.py
cache-line-traversal-misses.py
"""Draw the 16 reads for row/column traversal with an initially empty one-line cache.
Tested with Python 3.14.4, NumPy 2.3.5, Matplotlib 3.10.7.
Run from _media; outputs this script's PNG basename to the current directory.
Read-only, fully associative LRU model; no prefetching or writeback costs.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import numpy as np
def main():
plt.rcParams.update({"font.family": "DejaVu Sans", "font.size": 11})
row_colours = ["#dceaf8", "#fce4cc", "#ddedda", "#ebe0f4"]
fig, axes = plt.subplots(2, 1, figsize=(10.4, 6.0), dpi=120)
fig.patch.set_facecolor("white")
fig.suptitle("One resident line: use it before replacing it", fontsize=18, fontweight="bold", y=0.96)
fig.text(0.5, 0.89, "16 array reads • empty initial cache • capacity 1 line • fully associative LRU • no prefetch / writes",
ha="center", fontsize=10.5)
orders = [[(i, j) for i in range(4) for j in range(4)],
[(i, j) for j in range(4) for i in range(4)]]
for ax, order, name in zip(axes, orders, ["By rows", "By columns"]):
lines = np.array([(4 * (4 * i + j)) // 16 for i, j in order])
resident = None
hits = []
for step, ((i, j), line) in enumerate(zip(order, lines), start=1):
hit = resident == line
hits.append(hit)
resident = line
ax.add_patch(Rectangle((step - 0.46, 1.5), 0.92, 0.82,
facecolor=row_colours[line], edgecolor="white"))
ax.text(step, 1.91, str(line), ha="center", va="center", fontweight="bold")
ax.text(step, 2.65, f"{i},{j}", ha="center", va="center", fontsize=9)
ax.add_patch(Rectangle((step - 0.46, 0.48), 0.92, 0.74,
facecolor="#d6eee6" if hit else "#f9dfd9",
edgecolor="#13765b" if hit else "#b43c25", linewidth=1.2))
ax.text(step, 0.85, "H" if hit else "M", ha="center", va="center", fontweight="bold",
color="#0c5943" if hit else "#8e2818")
count = sum(hits)
assert count == (12 if name == "By rows" else 0)
ax.set_title(f"{name}: {16 - count} misses, {count} hits", loc="left", fontsize=13, fontweight="bold", pad=10)
ax.set(xlim=(0.4, 16.6), ylim=(0.1, 3), xticks=np.arange(1, 17),
yticks=[0.85, 1.91, 2.65], yticklabels=["Outcome", "Line", "Item i,j"])
ax.set_xlabel("Memory reference number", labelpad=7)
ax.tick_params(length=0)
for spine in ax.spines.values():
spine.set_visible(False)
fig.text(0.5, 0.048, "H = hit; M = miss. After each read, the requested line is the sole resident line.\n"
"With capacity 4 lines, BOTH orders instead have 4 compulsory misses and 12 hits.",
ha="center", fontsize=11, linespacing=1.6)
fig.subplots_adjust(left=0.10, right=0.985, top=0.79, bottom=0.18, hspace=0.80)
fig.savefig(Path(__file__).stem + ".png", facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()