From 0e346b939df6b89406b07843471d0011a2444c23 Mon Sep 17 00:00:00 2001 From: Mukesh Garg Date: Thu, 21 May 2026 09:58:08 -0700 Subject: [PATCH] gemm: test-generated GEMM plots under tests/gemm/ + docs/diagrams/gemm_plots/ Mirror the sccl pattern for GEMM figures: a tests/gemm/ package renders the GEMM bar charts as PNGs from the committed docs/diagrams/gemm_sweep.json, so the figures are fast test artifacts (run by default) while the heavy sim sweep stays a manual script (scripts/gemm_sweep.py, kept) wrapped by a slow regenerator test. tests/gemm/: - _gemm_plot_helpers.py: matplotlib renderers (series logic mirrors the GEMM _render_* functions in scripts/build_overview_slides.py). - test_plot_gemm_stage_breakdown.py: gemm_stage_breakdown.png (load_ref). - test_plot_gemm_mac_utilization.py: gemm_mac_utilization_measured.png + gemm_mac_utilization_theoretical_vs_measured.png (load_ref). - test_gemm_sweep.py: @pytest.mark.slow regenerator (runs scripts/gemm_sweep.py). Chart set trimmed to three (stage breakdown, MAC util, theoretical-vs-measured); "formula" relabeled to "theoretical" throughout the comparison chart. Known follow-ups (not blocking): - gemm_mac_utilization_measured.png currently plots the theoretical ideal- pipeline model, not simulator-measured data; the name is a misnomer pending a decision to repoint its content or retitle. - The theoretical-model constants (HBM 256 GB/s, T_stage 16 ns, 3 stages) are inherited verbatim from build_overview_slides.py and not yet verified against ADR-0033 / ADR-0014 / topology. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../gemm_mac_utilization_measured.png | Bin 0 -> 40614 bytes ...ac_utilization_theoretical_vs_measured.png | Bin 0 -> 46649 bytes .../gemm_plots/gemm_stage_breakdown.png | Bin 0 -> 42848 bytes tests/gemm/_gemm_plot_helpers.py | 283 ++++++++++++++++++ tests/gemm/test_gemm_sweep.py | 36 +++ tests/gemm/test_plot_gemm_mac_utilization.py | 35 +++ tests/gemm/test_plot_gemm_stage_breakdown.py | 24 ++ 7 files changed, 378 insertions(+) create mode 100644 docs/diagrams/gemm_plots/gemm_mac_utilization_measured.png create mode 100644 docs/diagrams/gemm_plots/gemm_mac_utilization_theoretical_vs_measured.png create mode 100644 docs/diagrams/gemm_plots/gemm_stage_breakdown.png create mode 100644 tests/gemm/_gemm_plot_helpers.py create mode 100644 tests/gemm/test_gemm_sweep.py create mode 100644 tests/gemm/test_plot_gemm_mac_utilization.py create mode 100644 tests/gemm/test_plot_gemm_stage_breakdown.py diff --git a/docs/diagrams/gemm_plots/gemm_mac_utilization_measured.png b/docs/diagrams/gemm_plots/gemm_mac_utilization_measured.png new file mode 100644 index 0000000000000000000000000000000000000000..a896aaac0964aafcd88ad8dbf1bd2ea76442a3a1 GIT binary patch literal 40614 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

-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 literal 0 HcmV?d00001 diff --git a/docs/diagrams/gemm_plots/gemm_mac_utilization_theoretical_vs_measured.png b/docs/diagrams/gemm_plots/gemm_mac_utilization_theoretical_vs_measured.png new file mode 100644 index 0000000000000000000000000000000000000000..927913428f43b3462d0f5897df6ba3653ef13c7d GIT binary patch literal 46649 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 aEw9zks29qs_mszgd7_!sp)!-Rk^cdanF*2r literal 0 HcmV?d00001 diff --git a/docs/diagrams/gemm_plots/gemm_stage_breakdown.png b/docs/diagrams/gemm_plots/gemm_stage_breakdown.png new file mode 100644 index 0000000000000000000000000000000000000000..1130b3c4cb24d338a1b038babfbfc54d69850628 GIT binary patch literal 42848 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 $p^PGdC6HB(UPcB`BhwfA5M8~iw1SHY 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 literal 0 HcmV?d00001 diff --git a/tests/gemm/_gemm_plot_helpers.py b/tests/gemm/_gemm_plot_helpers.py new file mode 100644 index 0000000..b7a97ce --- /dev/null +++ b/tests/gemm/_gemm_plot_helpers.py @@ -0,0 +1,283 @@ +"""Shared plotting plumbing for the GEMM figure tests. + +Not a test module (no ``test_`` prefix -> pytest does not collect it). + +Reads the committed ``docs/diagrams/gemm_sweep.json`` (produced by the heavy +``scripts/gemm_sweep.py`` sim sweep) and renders matplotlib PNGs into +``docs/diagrams/gemm_plots/``. No simulation here -> the figure tests are fast +and run by default; regenerating the underlying data stays a manual script. + +Chart set (mirrors the GEMM MAC slides in scripts/build_overview_slides.py): + - stage breakdown (load_ref operand staging) + - MAC utilization — measured (load_ref) + - MAC utilization — theoretical vs measured (load_ref) +""" +from __future__ import annotations + +import json +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent.parent +GEMM_SWEEP_JSON = ROOT / "docs" / "diagrams" / "gemm_sweep.json" +GEMM_PLOTS_DIR = ROOT / "docs" / "diagrams" / "gemm_plots" + +# Shapes excluded from the figures (mirrors build_overview_slides). +EXCLUDED_SHAPES = {(512, 512, 512)} + +# Stage bars shown (raw op_log stage_type keys) + display names + colors. +STAGE_KEYS = ["DMA_READ", "FETCH", "GEMM", "DMA_WRITE"] +STAGE_DISPLAY = { + "DMA_READ": "DMA in", + "FETCH": "Fetch", + "GEMM": "GEMM", + "DMA_WRITE": "DMA out", +} +STAGE_COLORS = { + "DMA_READ": "#3B82F6", + "FETCH": "#10B981", + "GEMM": "#F59E0B", + "DMA_WRITE": "#A855F7", +} + +# MAC-utilization model constants (mirror build_overview_slides). +_HBM_GBS = 256.0 +_BPE = 2 +_T_STAGE = 16.0 +_D_STAGES = 3 + +_PLOT_VARIANT = "load_ref" + + +def _load_sweep_data() -> dict: + if not GEMM_SWEEP_JSON.exists(): + return {"rows": []} + data = json.loads(GEMM_SWEEP_JSON.read_text()) + data["rows"] = [ + r for r in data.get("rows", []) + if (r["M"], r["K"], r["N"]) not in EXCLUDED_SHAPES + ] + return data + + +def _shape_label(r: dict) -> str: + if r["M"] == r["K"] == r["N"]: + return f"M=K=N={r['M']}" + return f"M={r['M']} K={r['K']} N={r['N']}" + + +def _under_tile(M, K, N, tile_M, tile_K, tile_N) -> bool: + return M < tile_M or K < tile_K or N < tile_N + + +def _xtick_labels(shape_labels, tile_counts, flagged) -> list[str]: + out = [] + for lbl, tc, fl in zip(shape_labels, tile_counts, flagged): + s = f"{lbl}\n({tc} tiles)" + if fl: + s += " *" + out.append(s) + return out + + +def _grouped_bar_png( + out_name: str, *, title: str, subtitle: str | None, + shape_labels, tile_counts, flagged, series: dict, colors: dict, + y_label: str, threshold: float | None = None, footnote: str | None = None, +) -> str: + """Render one grouped-bar chart to GEMM_PLOTS_DIR/out_name; return the path.""" + import matplotlib.pyplot as plt + import numpy as np + + n_groups = len(shape_labels) + n_series = max(1, len(series)) + x = np.arange(n_groups) + width = 0.8 / n_series + + fig, ax = plt.subplots(figsize=(11, 6)) + for i, (name, vals) in enumerate(series.items()): + offset = (i - (n_series - 1) / 2) * width + ax.bar(x + offset, vals, width, label=name, color=colors.get(name)) + + ax.set_xticks(x) + ax.set_xticklabels( + _xtick_labels(shape_labels, tile_counts, flagged), fontsize=8, + ) + ax.set_ylabel(y_label) + ax.set_title(title, fontsize=13, fontweight="bold") + if subtitle: + ax.text(0.5, 1.01, subtitle, transform=ax.transAxes, ha="center", + va="bottom", fontsize=8, color="#475569") + if threshold is not None: + ax.axhline(threshold, ls="--", color="gray", lw=1.0) + ax.legend(fontsize=8, loc="upper right") + ax.grid(True, axis="y", alpha=0.3) + + caption = "* = under-tile shape (M str | None: + """Per-stage engine wall-clock per shape (load_ref operand staging).""" + data = _load_sweep_data() + rows = [r for r in data["rows"] if r.get("variant") == _PLOT_VARIANT] + if not rows: + return None + tile = data["tile_sizes"] + shape_labels = [_shape_label(r) for r in rows] + flagged = [_under_tile(r["M"], r["K"], r["N"], tile["M"], tile["K"], tile["N"]) + for r in rows] + tile_counts = [r["tile_count_expected"] for r in rows] + series = { + STAGE_DISPLAY[s]: [r.get("stages", {}).get(s, {}).get("wall_ns", 0.0) + for r in rows] + for s in STAGE_KEYS + } + colors = {STAGE_DISPLAY[s]: STAGE_COLORS[s] for s in STAGE_KEYS} + return _grouped_bar_png( + "gemm_stage_breakdown.png", + title="GEMM stage breakdown", + subtitle=(f"Per-stage engine wall-clock (DMA in / Fetch / GEMM / " + f"DMA out), {_PLOT_VARIANT} staging. " + f"Tile {tile['M']}x{tile['K']}x{tile['N']}."), + shape_labels=shape_labels, tile_counts=tile_counts, flagged=flagged, + series=series, colors=colors, y_label="ns", + footnote="Bars = engine wall-clock interval (merged overlaps).", + ) + + +def emit_mac_utilization_measured() -> str | None: + """GEMM util % and useful pipeline-eff % (analytical model, load_ref).""" + data = _load_sweep_data() + rows = data["rows"] + if not rows: + return None + tile = data["tile_sizes"] + TILE_M, TILE_K, TILE_N = tile["M"], tile["K"], tile["N"] + tile_flops = 2 * TILE_M * TILE_K * TILE_N + dma_w_per_pair = (TILE_M * TILE_N * _BPE) / _HBM_GBS + head_ns = (_D_STAGES - 1) * _T_STAGE + + by_shape = {(r["M"], r["K"], r["N"]): r + for r in rows if r["variant"] == _PLOT_VARIANT} + shapes = list(by_shape) + if not shapes: + return None + shape_labels = [_shape_label(by_shape[k]) for k in shapes] + flagged = [_under_tile(*k, TILE_M, TILE_K, TILE_N) for k in shapes] + tile_counts = [by_shape[k]["tile_count_expected"] for k in shapes] + + gemm_util, useful_eff = [], [] + for k in shapes: + r = by_shape[k] + M, K, N = r["M"], r["K"], r["N"] + useful = 2 * M * K * N + tiles = r["tile_count_expected"] + gu = useful / (tile_flops * tiles) * 100 + gemm_util.append(gu) + m_tiles = (M + TILE_M - 1) // TILE_M + n_tiles = (N + TILE_N - 1) // TILE_N + n_mn = m_tiles * n_tiles + compute_total = tiles * _T_STAGE + wall = head_ns + tiles * _T_STAGE + max(0, n_mn - 1) * dma_w_per_pair + ueff = (compute_total * (gu / 100.0) / wall) * 100 if wall > 0 else 0.0 + useful_eff.append(ueff) + + series = {"GEMM util %": gemm_util, "Useful eff %": useful_eff} + colors = {"GEMM util %": "#10B981", "Useful eff %": "#F59E0B"} + return _grouped_bar_png( + "gemm_mac_utilization_measured.png", + title="GEMM MAC utilization — load_ref", + subtitle=("GEMM util = useful FLOPs / (tile FLOPs x tiles); " + "Useful eff = GEMM util x ideal pipeline efficiency."), + shape_labels=shape_labels, tile_counts=tile_counts, flagged=flagged, + series=series, colors=colors, y_label="%", threshold=100.0, + footnote="Theoretical ideal-pipeline model (not simulator data).", + ) + + +def emit_mac_utilization_theoretical_vs_measured() -> str | None: + """Theoretical vs simulator-measured GEMM util / useful eff (load_ref).""" + data = _load_sweep_data() + rows = data["rows"] + if not rows: + return None + tile = data["tile_sizes"] + TILE_M, TILE_K, TILE_N = tile["M"], tile["K"], tile["N"] + tile_flops = 2 * TILE_M * TILE_K * TILE_N + dma_w_per_pair = (TILE_M * TILE_N * _BPE) / _HBM_GBS + head_ns = (_D_STAGES - 1) * _T_STAGE + peak_per_ns = tile_flops / _T_STAGE + + by_shape = {(r["M"], r["K"], r["N"]): r + for r in rows if r["variant"] == _PLOT_VARIANT} + shapes = list(by_shape) + if not shapes: + return None + shape_labels = [_shape_label(by_shape[k]) for k in shapes] + flagged = [_under_tile(*k, TILE_M, TILE_K, TILE_N) for k in shapes] + tile_counts = [by_shape[k]["tile_count_expected"] for k in shapes] + + gu_t, gu_m, eff_t, eff_m = [], [], [], [] + for k in shapes: + r = by_shape[k] + M, K, N = r["M"], r["K"], r["N"] + useful = 2 * M * K * N + tiles = r["tile_count_expected"] + gut = useful / (tile_flops * tiles) + gu_t.append(gut * 100) + rec = r.get("stages", {}).get("GEMM", {}).get("record_count", 0) or tiles + gu_m.append((useful / (tile_flops * rec) * 100) if rec else 0.0) + m_tiles = (M + TILE_M - 1) // TILE_M + n_tiles = (N + TILE_N - 1) // TILE_N + n_mn = m_tiles * n_tiles + compute_total = tiles * _T_STAGE + wall_t = head_ns + compute_total + max(0, n_mn - 1) * dma_w_per_pair + eff_t.append((compute_total * gut / wall_t * 100) if wall_t > 0 else 0.0) + cw = r.get("composite_window_ns", 0.0) or 0.0 + eff_m.append((useful / cw / peak_per_ns * 100) if cw > 0 else 0.0) + + series = { + "GEMM util % (theoretical)": gu_t, + "GEMM util % (measured)": gu_m, + "Theoretical eff %": eff_t, + "Measured eff %": eff_m, + } + colors = { + "GEMM util % (theoretical)": "#10B981", + "GEMM util % (measured)": "#6EE7B7", + "Theoretical eff %": "#F59E0B", + "Measured eff %": "#3B82F6", + } + return _grouped_bar_png( + "gemm_mac_utilization_theoretical_vs_measured.png", + title="GEMM MAC utilization — theoretical vs measured (load_ref)", + subtitle=("theoretical model vs simulator op_log; agreement " + "validates the analytical pipeline model."), + shape_labels=shape_labels, tile_counts=tile_counts, flagged=flagged, + series=series, colors=colors, y_label="%", threshold=100.0, + ) + + +def emit_all_gemm_plots() -> list[str]: + """Render every GEMM figure that has data; return the list of paths written.""" + paths = [] + for fn in (emit_stage_breakdown, + emit_mac_utilization_measured, + emit_mac_utilization_theoretical_vs_measured): + p = fn() + if p: + paths.append(p) + return paths diff --git a/tests/gemm/test_gemm_sweep.py b/tests/gemm/test_gemm_sweep.py new file mode 100644 index 0000000..39fa646 --- /dev/null +++ b/tests/gemm/test_gemm_sweep.py @@ -0,0 +1,36 @@ +"""Regenerate docs/diagrams/gemm_sweep.json by running the GEMM sweep. + +Heavy: drives matmul-composite across all shapes x variants through the +simulator (24 runs; the 512 shape alone is 2048 tiles). Marked ``slow`` so it +is excluded from the default ``pytest`` run (addopts: -m "not slow") and runs +on demand: + + pytest -m slow tests/gemm/test_gemm_sweep.py + +Delegates to scripts/gemm_sweep.py (the single source of the sweep logic) via +subprocess so there is no duplicated sim-driving code. +""" +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path + +import pytest + +from tests.gemm._gemm_plot_helpers import GEMM_SWEEP_JSON, ROOT + + +@pytest.mark.slow +def test_gemm_sweep_regenerates_json(): + script = ROOT / "scripts" / "gemm_sweep.py" + assert script.exists(), f"missing {script}" + proc = subprocess.run( + [sys.executable, str(script)], + cwd=str(ROOT), capture_output=True, text=True, + ) + assert proc.returncode == 0, ( + f"gemm_sweep.py failed (rc={proc.returncode})\n" + f"stdout:\n{proc.stdout[-2000:]}\nstderr:\n{proc.stderr[-2000:]}" + ) + assert Path(GEMM_SWEEP_JSON).exists() diff --git a/tests/gemm/test_plot_gemm_mac_utilization.py b/tests/gemm/test_plot_gemm_mac_utilization.py new file mode 100644 index 0000000..8a06cb3 --- /dev/null +++ b/tests/gemm/test_plot_gemm_mac_utilization.py @@ -0,0 +1,35 @@ +"""Emit the GEMM MAC-utilization bar charts. + +A measured chart (load_ref) plus the theoretical-vs-measured overlay (load_ref). +Reads docs/diagrams/gemm_sweep.json and writes gemm_mac_utilization*.png into +docs/diagrams/gemm_plots/. +""" +from __future__ import annotations + +from pathlib import Path + +import pytest + +from tests.gemm._gemm_plot_helpers import ( + GEMM_SWEEP_JSON, + emit_mac_utilization_measured, + emit_mac_utilization_theoretical_vs_measured, +) + + +@pytest.mark.skipif( + not GEMM_SWEEP_JSON.exists(), + reason="gemm_sweep.json absent; run scripts/gemm_sweep.py first", +) +def test_plot_gemm_mac_utilization_measured(): + out = emit_mac_utilization_measured() + assert out is not None and Path(out).exists() + + +@pytest.mark.skipif( + not GEMM_SWEEP_JSON.exists(), + reason="gemm_sweep.json absent; run scripts/gemm_sweep.py first", +) +def test_plot_gemm_mac_utilization_theoretical_vs_measured(): + out = emit_mac_utilization_theoretical_vs_measured() + assert out is not None and Path(out).exists() diff --git a/tests/gemm/test_plot_gemm_stage_breakdown.py b/tests/gemm/test_plot_gemm_stage_breakdown.py new file mode 100644 index 0000000..99664af --- /dev/null +++ b/tests/gemm/test_plot_gemm_stage_breakdown.py @@ -0,0 +1,24 @@ +"""Emit the GEMM per-stage engine wall-clock bar chart (load_ref). + +Reads docs/diagrams/gemm_sweep.json (run scripts/gemm_sweep.py to refresh it) +and writes gemm_stage_breakdown.png into docs/diagrams/gemm_plots/. +""" +from __future__ import annotations + +from pathlib import Path + +import pytest + +from tests.gemm._gemm_plot_helpers import ( + GEMM_SWEEP_JSON, + emit_stage_breakdown, +) + + +@pytest.mark.skipif( + not GEMM_SWEEP_JSON.exists(), + reason="gemm_sweep.json absent; run scripts/gemm_sweep.py first", +) +def test_plot_gemm_stage_breakdown(): + out = emit_stage_breakdown() + assert out is not None and Path(out).exists()