gqa(decode-4cases): Case 2 — Cube-Repl × PE-TP (no comm; 8× memory) (5C.B)
Second case in the GQA decode 4-cases comparative study per
GQA_full_deck.pptx slide 11. Case 2 replicates K, V across all 8
cubes × 8 PEs (the slide-11 8 KB/tok/PE memory waste) and has zero
inter-rank comm. For B=1 (single-user decode default), only PE 0
of CUBE 0 has work — the inherent PE-TP waste slide 11 calls out.
Changes:
- New kernel: src/kernbench/benches/_gqa_attention_decode_cube_repl_pe_tp.py
Simplest of the 4 cases. Active rank loads full Q/K/V from HBM,
computes attention via S_kv tile sweep with online-softmax merge,
writes O. All non-(0,0) ranks early-return. No tl.send/recv.
- src/kernbench/benches/milestone_gqa_decode_4cases.py:
- Add panel single_kv_group_decode_gqa_cube_repl_pe_tp (Case 2)
to _PANELS and _PANEL_DISPATCH.
- Add _run_decode_panel_cube_repl_pe_tp helper: DPPolicy K/V/Q/O
= cube=replicate, pe=replicate (models 8× memory waste).
- Extend _make_bench_fn to dispatch kind="decode_cube_repl_pe_tp"
to the new runner.
- tests/attention/test_milestone_gqa_decode_4cases.py:
4 new tests assert Case 2 contract: panel registered, smoke
completion, zero ipcq_copy (no comm), single dma_write from cube 0.
Verification: 8/8 tests pass (4 Case 4 anchor + 4 new Case 2).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -32,12 +32,17 @@ import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from kernbench.benches._gqa_attention_decode_cube_repl_pe_tp import (
|
||||
gqa_attention_decode_cube_repl_pe_tp_kernel,
|
||||
)
|
||||
from kernbench.benches.milestone_gqa_headline import (
|
||||
_ccl_cfg,
|
||||
_run_decode_panel,
|
||||
_summarize_op_log,
|
||||
)
|
||||
from kernbench.benches.registry import bench
|
||||
from kernbench.ccl.sfr_config import configure_sfr_intercube_multisip
|
||||
from kernbench.policy.placement.dp import DPPolicy
|
||||
|
||||
_OUTPUT_DIR = (
|
||||
Path(__file__).resolve().parent
|
||||
@@ -51,11 +56,12 @@ _SWEEP_JSON = _OUTPUT_DIR / "sweep.json"
|
||||
|
||||
|
||||
_PANELS = (
|
||||
"single_kv_group_decode_gqa_cube_sp_pe_sp", # Case 4
|
||||
# Cases 1-3 to be added by 5C.A/B/C
|
||||
"single_kv_group_decode_gqa_cube_sp_pe_sp", # Case 4 ★ optimal
|
||||
"single_kv_group_decode_gqa_cube_repl_pe_tp", # Case 2 (no comm; 8× memory)
|
||||
# Cases 1, 3 to be added by 5C.A/C
|
||||
)
|
||||
|
||||
# Each entry: (kind, panel-specific params for _run_decode_panel).
|
||||
# Each entry: (kind, panel-specific params).
|
||||
# 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
|
||||
@@ -68,22 +74,63 @@ _PANEL_DISPATCH: dict[str, tuple[str, dict]] = {
|
||||
"T_q": 1, "S_kv": 131_072,
|
||||
"d_head": 128, "h_q": 8, "h_kv": 1,
|
||||
}),
|
||||
"single_kv_group_decode_gqa_cube_repl_pe_tp": ("decode_cube_repl_pe_tp", {
|
||||
# Case 2: K, V replicated everywhere (8× memory waste); PEs TP
|
||||
# on batch. For B=1 only one rank works (slide-11 PE-TP waste).
|
||||
# No inter-rank communication.
|
||||
"C": 8, "P": 8,
|
||||
"T_q": 1, "S_kv": 131_072,
|
||||
"d_head": 128, "h_q": 8, "h_kv": 1,
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
# ── Per-panel runner ─────────────────────────────────────────────────
|
||||
|
||||
|
||||
def _run_decode_panel_cube_repl_pe_tp(
|
||||
ctx, *, panel: str, C: int, P: int,
|
||||
T_q: int, S_kv: int,
|
||||
d_head: int, h_q: int, h_kv: int,
|
||||
) -> None:
|
||||
"""Case 2 runner: K, V replicated everywhere; B=1 single-rank work.
|
||||
|
||||
DPPolicy models the cluster-wide memory waste — every rank holds
|
||||
full K, V in its HBM region. Only PE 0 of CUBE 0 computes (the
|
||||
kernel early-returns on every other rank), so only one rank reads
|
||||
from its HBM copy and writes the output.
|
||||
"""
|
||||
configure_sfr_intercube_multisip(ctx.engine, ctx.spec, _ccl_cfg())
|
||||
dp_repl = DPPolicy(cube="replicate", pe="replicate",
|
||||
num_cubes=C, num_pes=P)
|
||||
q = ctx.zeros((T_q, h_q * d_head),
|
||||
dtype="f16", dp=dp_repl, name=f"{panel}_q")
|
||||
k = ctx.zeros((S_kv, h_kv * d_head),
|
||||
dtype="f16", dp=dp_repl, name=f"{panel}_k")
|
||||
v = ctx.zeros((S_kv, h_kv * d_head),
|
||||
dtype="f16", dp=dp_repl, name=f"{panel}_v")
|
||||
o = ctx.empty((T_q, h_q * d_head),
|
||||
dtype="f16", dp=dp_repl, name=f"{panel}_o")
|
||||
ctx.launch(
|
||||
panel, gqa_attention_decode_cube_repl_pe_tp_kernel,
|
||||
q, k, v, o,
|
||||
T_q, S_kv, h_q, h_kv, d_head, C, P,
|
||||
_auto_dim_remap=False,
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
elif kind == "decode_cube_repl_pe_tp":
|
||||
_run_decode_panel_cube_repl_pe_tp(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."
|
||||
f"unsupported kind={kind!r}."
|
||||
)
|
||||
return _bench_fn
|
||||
|
||||
|
||||
Reference in New Issue
Block a user