gqa(decode-4cases): Case 4 anchor in dedicated bench (5C.D)

First milestone of the decode 4-cases comparative study per
GQA_full_deck.pptx slides 11-17. Case 4 (Cube-SP × PE-SP, the optimal
case per slide 11) is structurally the existing _gqa_attention_decode_long
at sub_w=4 (Increment 2's lrab-adapted center-root reduce). This commit
wires it into a dedicated bench so the remaining cases land alongside.

Changes:
- New bench: src/kernbench/benches/milestone_gqa_decode_4cases.py
  Houses the 4 case panels under one milestone-gqa-decode-4cases entry
  (gated by GQA_DECODE_4CASES_RUN=1; output to
   1H_milestone_output/gqa_decode_4cases/sweep.json). Cases 1-3 are
  TBD in subsequent sub-increments (5C.A/B/C).
- New panel: single_kv_group_decode_gqa_cube_sp_pe_sp
  C=8, P=8, sub_w=4, T_q=1, S_kv=131_072, d_head=128, h_q=8, h_kv=1.
- src/kernbench/benches/milestone_gqa_headline.py: _run_decode_panel
  extended with keyword-only sub_w/T_q/d_head/h_q/h_kv overrides
  (defaults preserve existing-panel behaviour).
- tests/attention/test_milestone_gqa_decode_4cases.py: 4 new tests
  asserting registration, smoke completion, reduce-to-root at the lrab
  center cube (cube 6), and the predicted 189-ipcq Case-4 traffic
  pattern (168 intra-CUBE + 21 inter-CUBE lrab Phase 1+2).
- tests/attention/test_milestone_gqa_headline.py: rename
  test_sweep_json_has_four_panels -> test_sweep_json_has_expected_panels
  and switch hardcoded 4 to len(PANELS) (the panel set grew to 5
  with Increment 5's single_kv_group_prefill_gqa_c8_p8).

Deviation noted: slide 13 prescribes AllReduce on (m,ℓ,O); our kernel
does reduce-to-root (only the lrab center cube has the answer) per
ADR-0060 §4. Treated as the kernbench Case-4 baseline.

Verification: all 4 new tests pass; 90 regression tests pass; the
previously-failing test_sweep_json_has_four_panels now passes under
its renamed form.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 14:31:44 -07:00
parent 5b4d9cb597
commit c164645aee
4 changed files with 356 additions and 8 deletions
@@ -0,0 +1,152 @@
"""milestone-gqa-decode-4cases: comparative study of 4 decode sharding cases.
Per GQA_full_deck.pptx slides 11-17: 4 KV-cache sharding strategies on
the LLaMA-3.1-70B single-KV-head group (8 cubes × 8 PEs):
Case 1 Cube-SP / PE-TP → KV split by S_kv across cubes; PEs TP on batch
Case 2 Cube-Repl / PE-TP → full KV per cube; PEs TP on batch
Case 3 Cube-Repl / PE-SP → full KV per cube; PEs SP on S_kv (intra-cube AR)
Case 4 Cube-SP / PE-SP → KV split 64-way; 2-phase AR on (m,,O) ★ optimal
Each case is a separate panel. The bench drives all panels in one
invocation and writes per-panel op_log_summary to sweep.json so the
comparative analysis (latency, GEMM/MAC util, comm volume) can be
generated from a single sweep.
Status (initial commit, 5C.D):
- Case 4 panel implemented (uses _gqa_attention_decode_long with
sub_w=4 — ADR-0060 §4.2 lrab-adapted center-root reduce; that is
structurally Case 4 per slide 11 with the reduce-to-root variant
of the AR pattern).
- Cases 1-3 panels: TBD in subsequent sub-increments (5C.A/B/C).
Deviation from slide 13: slide prescribes AllReduce (every rank has
the answer); the kernel does reduce-to-root (only the lrab center
cube has it) per ADR-0060 §4. Treated as the kernbench Case-4 baseline.
Gated by ``GQA_DECODE_4CASES_RUN=1``.
"""
from __future__ import annotations
import json
import os
from pathlib import Path
from kernbench.benches.milestone_gqa_headline import (
_ccl_cfg,
_run_decode_panel,
_summarize_op_log,
)
from kernbench.benches.registry import bench
_OUTPUT_DIR = (
Path(__file__).resolve().parent
/ "1H_milestone_output"
/ "gqa_decode_4cases"
)
_SWEEP_JSON = _OUTPUT_DIR / "sweep.json"
# ── Panel registry ───────────────────────────────────────────────────
_PANELS = (
"single_kv_group_decode_gqa_cube_sp_pe_sp", # Case 4
# Cases 1-3 to be added by 5C.A/B/C
)
# Each entry: (kind, panel-specific params for _run_decode_panel).
# LLaMA-3.1-70B single-KV-head group target:
# 1 KV head, h_q = 8 (G = 8 group), d_head = 128
# 8 cubes (head-parallel group), 8 PEs/cube
# S_kv = 128K (long-context decode), T_q = 1 (one new token per pass)
_PANEL_DISPATCH: dict[str, tuple[str, dict]] = {
"single_kv_group_decode_gqa_cube_sp_pe_sp": ("decode", {
# Case 4: KV split 64-way (Cube-SP × PE-SP), 2-level reduce.
# sub_w=4 ⇒ sub_h=2 ⇒ lrab center-root cube = (1,2) = cube 6.
"C": 8, "P": 8, "sub_w": 4,
"T_q": 1, "S_kv": 131_072,
"d_head": 128, "h_q": 8, "h_kv": 1,
}),
}
# ── Per-panel runner ─────────────────────────────────────────────────
def _make_bench_fn(panel: str):
kind, params = _PANEL_DISPATCH[panel]
def _bench_fn(ctx):
if kind == "decode":
_run_decode_panel(ctx, panel=panel, **params)
else:
raise RuntimeError(
f"milestone-gqa-decode-4cases panel {panel!r} has "
f"unsupported kind={kind!r}; only 'decode' is allowed."
)
return _bench_fn
def _run_panel(panel: str, topology: str) -> dict:
from kernbench.runtime_api.bench_runner import run_bench
from kernbench.runtime_api.types import resolve_device
from kernbench.sim_engine.engine import GraphEngine
from kernbench.topology.builder import resolve_topology
topo = resolve_topology(topology)
result = run_bench(
topology=topo, bench_fn=_make_bench_fn(panel),
device=resolve_device(None),
engine_factory=lambda t, d: GraphEngine(
getattr(t, "topology_obj", t), enable_data=True,
),
)
if not result.completion.ok:
raise RuntimeError(
f"milestone-gqa-decode-4cases panel {panel!r} failed: "
f"{result.completion}"
)
kind, params = _PANEL_DISPATCH[panel]
return {
"panel": panel,
"kind": kind,
**params,
"op_log_summary": _summarize_op_log(result.engine.op_log),
}
# ── Bench entry ──────────────────────────────────────────────────────
@bench(
name="milestone-gqa-decode-4cases",
description=(
"Comparative decode study of 4 KV-cache sharding cases on the "
"LLaMA-3.1-70B single-KV-head group (8 cubes × 8 PEs)."
),
)
def run(torch) -> None:
"""Drive the registered decode case panels; write sweep.json.
Gated by GQA_DECODE_4CASES_RUN=1.
"""
_OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
if not os.environ.get("GQA_DECODE_4CASES_RUN"):
raise RuntimeError(
"milestone-gqa-decode-4cases needs GQA_DECODE_4CASES_RUN=1."
)
topology = os.environ.get(
"GQA_DECODE_4CASES_TOPOLOGY", "topology.yaml",
)
rows = [_run_panel(panel, topology) for panel in _PANELS]
sweep = {
"version": 1,
"panels": list(_PANELS),
"rows": rows,
}
_SWEEP_JSON.write_text(json.dumps(sweep, indent=2))
print(
f" milestone-gqa-decode-4cases: {len(rows)} rows -> {_SWEEP_JSON}"
)