codex/past-exam-of-the-computer-science-course-of-the-university-of-oxford/2015/architecture-2015-prefetch-overlap.py

past-exam-of-the-computer-science-course-of-the-university-of-oxford/2015/architecture-2015-prefetch-overlap.py
"""Synthetic one-node-ahead prefetch schedule; output PNG in the current directory.

Tested with Python 3.14, Matplotlib 3.10.7 and NumPy 2.3.5, as documented
in the repository pyproject.toml. No machine-specific latency claim is made.
"""
from pathlib import Path

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Patch


def main():
    plt.rcParams.update({"font.family": "DejaVu Sans", "font.size": 10})
    fig, axes = plt.subplots(2, 1, figsize=(10, 5.4), sharex=True)
    fig.set_facecolor("white")
    colors = {"compute": "#3274a1", "transfer": "#df8f44", "hint": "#40865b"}

    def block(ax, start, duration, lane, kind, label):
        ax.broken_barh([(start, duration)], (lane - .27, .54),
                      facecolors=colors[kind], edgecolors="white", linewidth=1)
        if label:
            ax.text(start + duration / 2, lane, label, ha="center", va="center",
                    color="white", fontsize=9)

    ax = axes[0]
    block(ax, 0, 120, 1, "compute", "process node i")
    block(ax, 120, 100, 0, "transfer", "demand fetch i+1")
    block(ax, 220, 120, 1, "compute", "process node i+1")
    ax.plot([120, 220], [1, 1], color=colors["transfer"], linestyle=":", linewidth=3)
    ax.text(170, 1.38, "CPU waits", ha="center", fontsize=9, color="#985516")
    ax.axvline(220, color="#555555", linestyle="--", linewidth=.8)
    ax.set_title("No prefetch: processing starts are 220 cycles apart", loc="left", fontsize=11)

    ax = axes[1]
    block(ax, 0, 1, 1, "hint", "")
    block(ax, 1, 120, 1, "compute", "process node i")
    block(ax, 1, 100, 0, "transfer", "prefetch i+1")
    block(ax, 121, 1, 1, "hint", "")
    block(ax, 122, 120, 1, "compute", "process node i+1")
    block(ax, 122, 100, 0, "transfer", "prefetch i+2")
    block(ax, 242, 1, 1, "hint", "")
    block(ax, 243, 110, 1, "compute", "process node i+2")
    ax.annotate("1-cycle hint", xy=(.5, 1.23), xytext=(37, 1.6),
                ha="center", fontsize=9, arrowprops={"arrowstyle": "->", "lw": .8})
    ax.axvline(122, color="#555555", linestyle="--", linewidth=.8)
    ax.set_title("Prefetch: processing starts are 121 cycles apart", loc="left", fontsize=11)
    ax.set_xlabel("Cycles after current node becomes available")

    for ax in axes:
        ax.set_facecolor("white")
        ax.set_yticks([0, 1], ["Transfer", "CPU"])
        ax.set_ylim(-.6, 1.9)
        ax.set_xlim(-5, 355)
        ax.grid(axis="x", color="#dddddd", linewidth=.6)
        ax.set_axisbelow(True)
        ax.spines[["top", "right", "left"]].set_visible(False)
        ax.tick_params(axis="y", length=0)
    fig.suptitle("Overlap the next node's transfer with the current node's work", fontsize=13)
    fig.legend(handles=[Patch(color=colors["compute"], label="120 cycles of independent work"),
                        Patch(color=colors["transfer"], label="100-cycle transfer"),
                        Patch(color=colors["hint"], label="1-cycle hint")],
               loc="lower center", ncol=3, frameon=False, fontsize=9)
    fig.subplots_adjust(left=.10, right=.98, top=.86, bottom=.17, hspace=.56)
    fig.savefig(Path.cwd() / "architecture-2015-prefetch-overlap.png", dpi=110,
                facecolor="white", transparent=False,
                metadata={"Software": "Matplotlib"})
    plt.close(fig)


if __name__ == "__main__":
    main()