"""Comparative figure for the compute-bound prefill composite study. Reads sweep_prefill_compute_bound.json (emitted by milestone-1h-gqa, sweep ``prefill_cb``) and writes one two-panel PNG: gqa_prefill_compute_bound.png Left — end-to-end prefill latency (µs) vs context length. Right — MAC utilization (achieved / 8 TFLOP·s⁻¹ per-PE peak) vs context. Unlike memory-bound decode (where command form is latency-neutral), in compute-bound prefill the composite command keeps the MAC array fed by streaming DMA↔compute per HW tile, so it wins on both latency and utilization — and the margin grows with context (deeper P·V reduction = more tiles to pipeline). Run (after the bench): GQA_1H_RUN=1 GQA_1H_SWEEPS=prefill_cb python -m kernbench.cli.main run \\ --bench milestone-1h-gqa --topology topology.yaml python scripts/paper/paper_plot_gqa_prefill_compute_bound.py """ from __future__ import annotations import json from pathlib import Path import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt # noqa: E402 _REPO_ROOT = Path(__file__).resolve().parents[2] _FIG_DIR = ( _REPO_ROOT / "src" / "kernbench" / "benches" / "1H_milestone_output" / "gqa" / "long_ctx" ) _SWEEP_JSON = _FIG_DIR / "sweep_prefill_compute_bound.json" _PAPER_FIG_DIR = ( _REPO_ROOT / "docs" / "report" / "1H-codesign-paper" / "figures" ) _VARIANT_STYLE = { "primitive": ("primitive (tl.dot, hand-tiled)", "#c0504d", "o"), "composite": ("composite GEMM", "#3b6ea5", "s"), "composite_extended": ("composite + softmax_merge", "#4f8a4f", "^"), } _ORDER = ("primitive", "composite", "composite_extended") def _ctx_label(c: int) -> str: return f"{c // 1024}K" if c >= 1024 else str(c) def _series(rows, variant, key): pts = sorted(((r["ctx_len"], r[key]) for r in rows if r["variant"] == variant), key=lambda t: t[0]) return [p[0] for p in pts], [p[1] for p in pts] def main() -> None: sweep = json.loads(_SWEEP_JSON.read_text()) rows = sweep["rows"] ctxs = sweep["ctx_points"] fig, (ax_lat, ax_util) = plt.subplots(1, 2, figsize=(13.0, 4.8)) for v in _ORDER: label, color, marker = _VARIANT_STYLE[v] xs, lat = _series(rows, v, "latency_ns") ax_lat.plot(xs, [y / 1e3 for y in lat], marker=marker, color=color, label=label, lw=2) xs, util = _series(rows, v, "mac_util") ax_util.plot(xs, [u * 100 for u in util], marker=marker, color=color, label=label, lw=2) for ax in (ax_lat, ax_util): ax.set_xscale("log", base=2) ax.set_xticks(ctxs) ax.set_xticklabels([_ctx_label(c) for c in ctxs]) ax.set_xlabel(r"context length (= $T_q$ = $S_{kv}$)") ax.grid(True, ls=":", alpha=0.5) ax.legend(fontsize=9) ax_lat.set_ylabel("end-to-end prefill latency (µs)") ax_lat.set_title("Compute-bound prefill latency per command form") ax_util.set_ylabel("MAC utilization (% of 8 TFLOP·s⁻¹ peak)") ax_util.set_title( "MAC utilization — composite keeps the array fed; primitive starves" ) ax_util.axhline(100, color="#888", ls="--", lw=1, alpha=0.6) fig.suptitle( "Compute-bound prefill attention — use of composite commands\n" "single-rank, GQA single-KV-head group ($h_q{=}8$, $d_{\\text{head}}" "{=}128$); $M{=}8T_q$ tile-filling", fontsize=11, ) fig.tight_layout(rect=(0, 0, 1, 0.92)) out = _FIG_DIR / "gqa_prefill_compute_bound.png" fig.savefig(out, dpi=150) plt.close(fig) print(f"wrote {out}") if _PAPER_FIG_DIR.is_dir(): dst = _PAPER_FIG_DIR / out.name dst.write_bytes(out.read_bytes()) print(f"copied {dst}") if __name__ == "__main__": main()