gqa(decode-4cases): Case 3 — Cube-Repl × PE-SP (intra-CUBE AR only; 8× memory) (5C.C)
Adds the third 4-cases panel: KV replicated per cube (8× memory waste), PEs SP on S_kv within each cube, intra-CUBE 8-way reduce on (m, ℓ, O), and no inter-CUBE comm (every cube ends with full answer; designated writer = cube 0). Reuses the row-chain + col-bridge intra-CUBE pattern that anchors Case 4 (21 ipcq_copy per cube × 8 cubes = 168 total). 12 tests pass (4 Case 4 + 4 Case 2 + 4 Case 3). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,7 @@ from kernbench.topology.builder import resolve_topology
|
||||
TOPOLOGY_DEFAULT = Path(__file__).resolve().parents[2] / "topology.yaml"
|
||||
|
||||
_CASE4_PANEL = "single_kv_group_decode_gqa_cube_sp_pe_sp"
|
||||
_CASE3_PANEL = "single_kv_group_decode_gqa_cube_repl_pe_sp"
|
||||
_CASE2_PANEL = "single_kv_group_decode_gqa_cube_repl_pe_tp"
|
||||
_CUBE_RE = re.compile(r"\bcube(\d+)\b")
|
||||
|
||||
@@ -265,6 +266,124 @@ def test_case2_single_dma_write_at_cube_0():
|
||||
)
|
||||
|
||||
|
||||
# ── Case 3 (Cube-Repl × PE-SP) ──────────────────────────────────────
|
||||
|
||||
|
||||
def _run_case3_smoke(*, S_kv: int):
|
||||
"""Drive the Case 3 decode panel via the case-specific runner.
|
||||
|
||||
Case 3 = Cube-Repl × PE-SP. K, V replicated per cube; S_kv split
|
||||
8-way across PEs within each cube. Intra-CUBE 8-way reduce on
|
||||
(m, ℓ, O); no inter-CUBE comm (every cube ends with full answer).
|
||||
"""
|
||||
from kernbench.benches.milestone_gqa_decode_4cases import (
|
||||
_run_decode_panel_cube_repl_pe_sp,
|
||||
)
|
||||
topo = resolve_topology(str(TOPOLOGY_DEFAULT))
|
||||
|
||||
def _bench_fn(ctx):
|
||||
_run_decode_panel_cube_repl_pe_sp(
|
||||
ctx, panel=_CASE3_PANEL,
|
||||
C=8, P=8,
|
||||
T_q=1, S_kv=S_kv,
|
||||
d_head=128, h_q=8, h_kv=1,
|
||||
)
|
||||
|
||||
return run_bench(
|
||||
topology=topo, bench_fn=_bench_fn,
|
||||
device=resolve_device(None),
|
||||
engine_factory=_engine_factory,
|
||||
)
|
||||
|
||||
|
||||
# ── Case 3 — T1: panel registered ───────────────────────────────────
|
||||
|
||||
|
||||
def test_case3_panel_registered():
|
||||
"""The Case 3 panel must be in the bench's ``_PANELS`` +
|
||||
``_PANEL_DISPATCH`` with the expected single-KV-group dims.
|
||||
|
||||
Case 3: Cube-Repl × PE-SP. K, V replicated per cube; PEs SP on
|
||||
S_kv. Intra-CUBE 8-way AR; no inter-CUBE comm.
|
||||
"""
|
||||
from kernbench.benches.milestone_gqa_decode_4cases import (
|
||||
_PANEL_DISPATCH,
|
||||
_PANELS,
|
||||
)
|
||||
assert _CASE3_PANEL in _PANELS, (
|
||||
f"{_CASE3_PANEL!r} not in _PANELS; got {_PANELS}"
|
||||
)
|
||||
assert _CASE3_PANEL in _PANEL_DISPATCH
|
||||
kind, params = _PANEL_DISPATCH[_CASE3_PANEL]
|
||||
assert kind == "decode_cube_repl_pe_sp"
|
||||
assert params.get("C") == 8
|
||||
assert params.get("P") == 8
|
||||
assert params.get("T_q") == 1
|
||||
assert params.get("S_kv") == 131_072
|
||||
assert params.get("d_head") == 128
|
||||
assert params.get("h_q") == 8
|
||||
assert params.get("h_kv") == 1
|
||||
|
||||
|
||||
# ── Case 3 — T2: smoke runner completes ─────────────────────────────
|
||||
|
||||
|
||||
def test_case3_runner_smoke():
|
||||
"""Case 3 runner drives the new kernel to completion at smoke S_kv."""
|
||||
result = _run_case3_smoke(S_kv=8192)
|
||||
assert result.completion.ok, (
|
||||
f"Case 3 decode smoke at C=8 P=8 must complete; "
|
||||
f"got {result.completion}"
|
||||
)
|
||||
|
||||
|
||||
# ── Case 3 — T3: intra-CUBE-only AR (168 ipcq, no inter-CUBE) ───────
|
||||
|
||||
|
||||
def test_case3_intra_cube_ar_only_ipcq():
|
||||
"""Case 3 reduce pattern: per-CUBE 8-way PE-SP AR (same structural
|
||||
cost as Case 4's intra-CUBE phase = 21 ipcq_copy per cube), and
|
||||
NO inter-CUBE traffic (each cube has a full copy of KV).
|
||||
|
||||
per-CUBE intra (2×4 PE grid):
|
||||
row chain along intra_W: cols 1,2,3 each row × 2 rows ×
|
||||
3 tensors (m, ℓ, O) = 18
|
||||
col bridge along intra_N: pe4 only × 3 tensors = 3
|
||||
per-CUBE intra total = 21
|
||||
× 8 CUBEs = 168
|
||||
|
||||
inter-CUBE: 0 (replicated KV ⇒ no AllReduce needed).
|
||||
|
||||
Grand total: 168.
|
||||
"""
|
||||
result = _run_case3_smoke(S_kv=8192)
|
||||
assert result.completion.ok
|
||||
n_copy = _count(result.engine.op_log, "ipcq_copy")
|
||||
assert n_copy == 168, (
|
||||
f"Case 3 expected 168 ipcq_copy (intra-CUBE only, no inter-CUBE); "
|
||||
f"got {n_copy}"
|
||||
)
|
||||
|
||||
|
||||
# ── Case 3 — T4: single dma_write from cube 0 (designated writer) ───
|
||||
|
||||
|
||||
def test_case3_single_dma_write_at_cube_0():
|
||||
"""Every cube ends with the full answer after intra-CUBE AR; only
|
||||
the designated writer (cube 0, PE 0) stores O to avoid 8 redundant
|
||||
DMAs.
|
||||
"""
|
||||
result = _run_case3_smoke(S_kv=8192)
|
||||
assert result.completion.ok
|
||||
cubes = _dma_write_cubes(result.engine.op_log)
|
||||
assert cubes, "expected at least one dma_write for the final O store"
|
||||
distinct = set(cubes)
|
||||
assert distinct == {0}, (
|
||||
f"Case 3 designated writer must be cube 0; "
|
||||
f"got cubes={sorted(distinct)}"
|
||||
)
|
||||
|
||||
|
||||
# ── Case 4 — T4 (existing, kept) ────────────────────────────────────
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user