Skip to content

Commit e335358

Browse files
committed
docs(§26): F0 precompute as tensor-core T.gemm MEASURED on gb10 — 24.57ms→1.13ms (REAL sm_121 HMMA, parity PASS, Tri-Dao parity)
The §22/§23 24.57ms F0 lever is CLOSED. chunk_precompute_fwd_cuda_prim (committed dbf79fd) re-expresses cb + summary_states as T.gemm; measured at prod cfg (b1 S4096 c64 g8 H112 P64 N64): new tensor-core F0 = 1.129 ms (median, min 1.113) serial F0 (same box) = 16.36 ms -> 14.5x faster §23-headline serial = 24.57 ms -> 21.8x faster Tri-Dao cb+state = 1.061 ms -> new F0 is 1.06x (cb+state+cumsum 0.996x): PARITY REAL sm_121 MMA verified at BOTH levels: codegen emits tl::mma_sync<...16,8,16> x4 + ptx_ldmatrix x4, zero leftover serial-N dots; SASS (nvcc -arch=sm_121a + cuobjdump) emits 64x HMMA.16816.F32 + 32x LDSM. NOT relabeled serial. Parity PASS all elements (fp16 gate 5e-4): cb 1.22e-4, summary_states 3.74e-6, dA_cumsum 4.89e-4 vs serial F0 reference; cb 2.44e-4 / ss 3.74e-6 vs serial prim. Chain F0+F1+F2 = 5.24 ms (was ~30.0 ms) -> 1.69x cppmega's 3.11ms whole fwd. HONEST step-level: §17 step is backward-bound (447.8ms bwd vs 7.2ms fwd), so the 3.9ms forward saving is ~0.86% of the step -> headline tok/s UNCHANGED ≈907/≈298 (EXTRAPOLATION). GO kernel-level (F0 fixed, Tri-Dao parity); NO-GO step-level (does not move tok/s; next lever is the backward). Adds scratch/probe_f0_tc_gb10.py. Co-Authored-By: David Gornshtein <davidgornshtein@gmail.com>
1 parent dbf79fd commit e335358

2 files changed

Lines changed: 355 additions & 0 deletions

File tree

docs/RELAX-GRAPH-VS-MEGATRON.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1968,3 +1968,106 @@ backward crossings so the deep graph materializes incrementally instead of accum
19681968
- **NO-GO (unchanged): fp8 e2e as a tok/s WIN at floor0.** 16× slower (bridge-crossing-bound, not
19691969
GEMM-bound). The realized e2e tok/s win still awaits floor-tuning + crossing-batching; §17's
19701970
≈907/≈298 stays the canonical bf16 step number.
1971+
1972+
1973+
## §26. F0 PRECOMPUTE RE-EXPRESSED AS TENSOR-CORE T.gemm — the §22/§23 MEASURED 24.57 ms lever is GONE: new F0 = 1.13 ms (REAL sm_121 HMMA, parity PASS all elements), at Tri-Dao chunk_state+bmm parity. F0 is no longer the forward bottleneck; but the §17 STEP is backward-bound so the headline tok/s is essentially unchanged (MEASURED, gb10 sm_121, 2026-06-04)
1974+
1975+
**What changed.** §22/§23 named the path_c → Megatron 3399 gap root cause: OUR F0 precompute was
1976+
pathologically slow because `chunk_precompute_fwd_metal_prim` computed both its GEMM-shaped
1977+
reductions as SERIAL scalar loops — `cb[L,L] = C@B^T` as a per-`(li,si)` dot over dstate, and
1978+
`summary_states[P,N]` as a `P*N=4096`-cell serial-`l` accumulate — emitting NO tensor-core MMA. This
1979+
round adds `chunk_precompute_fwd_cuda_prim` (the sm_121/gb10 twin), re-expressing those two
1980+
head-independent loops as `T.gemm` and keeping `dA_cumsum` as the single-lane serial scan (it is a
1981+
scan, not a GEMM). `build_chunk_precompute_metal` selects the CUDA prim when the resolved target is
1982+
CUDA (mirrors F2's per-target branch); the serial Metal prim is **byte-identical** and F1 is
1983+
**untouched**.
1984+
1985+
- **(1) cb** = `T.gemm(C_shared[L,N], B_shared[L,N], cb_frag[L,L], transpose_B=True)` per
1986+
`(chunk, group)`, written once/group via the `head_idx % heads_per_group == 0` guard
1987+
(Tri-Dao `_bmm_chunk_fwd`).
1988+
- **(2) summary_states** = `T.gemm(x_dec_shared[L,P], B_shared[L,N], states_frag[P,N],
1989+
transpose_A=True)` per `(chunk, head)` — the dominant `P*N` term. The per-row
1990+
`decay*dt = exp2((dacs[L-1]-dacs[l])*p)*dt[l]` is folded into the **x operand** (Tri-Dao
1991+
`_chunk_state_fwd` order; B kept native fp16; no `minimum(.,0)` clamp, matching the serial sign
1992+
convention). Output stays fp32 (= accum_dtype; the §23 N=64 dtype contract).
1993+
1994+
DTYPE contract unchanged (§23): fp16 operands, fp32 register-fragment C accumulate, `cb` stored fp16,
1995+
`summary_states` stored fp32. Idioms mirror the proven F2 `chunk_scan_fwd_cuda_prim`: plain
1996+
`scope="shared"` operands (NO `make_swizzled_layout`), `disable_tma=True` on every copy, and
1997+
`pass_configs={tl.disable_tma_lower, tl.disable_warp_specialized}` on the CUDA branch. A smem-budget
1998+
gate (48.5 KB at prod L=P=N=64 « 99 KB) and an m16n8k16 divisibility gate **RAISE** on violation —
1999+
RULE #1, no serial fallback.
2000+
2001+
### MEASURED on gb10 sm_121 (prod cfg b=1 S=4096 c=64 g=8 H=112 P=64 N=64)
2002+
2003+
| F0 variant (same gb10 box, same session) | per-call ms (median / min) |
2004+
|---|---:|
2005+
| **NEW tensor-core CUDA F0** | **1.129 / 1.113 ms** |
2006+
| serial F0 prim re-built on CUDA (the §22/§23 kernel, this box) | 16.36 / 16.35 ms |
2007+
| §23-headline serial F0 (slower box state, same kernel) | 24.57 ms |
2008+
| Tri-Dao `_bmm_chunk_fwd` + `_chunk_state_fwd` (cb+state) | 1.061 ms |
2009+
| Tri-Dao cb + state + `_chunk_cumsum_fwd` (≈ all F0 does) | 1.133 ms |
2010+
2011+
- **vs serial F0:** **14.5×** faster than the same-box serial F0 (16.36 ms), **21.8×** faster than the
2012+
§23-headline 24.57 ms. The §22/§23 "F0 is the 24.57 ms lever" finding is now CLOSED.
2013+
- **vs cppmega / Tri-Dao:** new F0 (1.129 ms, which ALSO does the `dA_cumsum` scan) is **1.06×**
2014+
Tri-Dao's cb+state (1.061 ms) and **0.996×** their cb+state+cumsum (1.133 ms) — i.e. at Tri-Dao
2015+
tensor-core parity for the same work.
2016+
2017+
### REAL sm_121 tensor-core MMA — verified at codegen AND SASS (not relabeled serial)
2018+
2019+
- **Codegen** (`/tmp/f0_cuda_src.cu`, dumped via `JITKernel.get_kernel_source()`): the cb and
2020+
summary_states regions emit `tl::mma_sync<kFloat16,kFloat16,kFloat32,16,8,16,...>` **×4** (2 per
2021+
GEMM) fed by `ptx_ldmatrix_x4` / `ptx_ldmatrix_x4_trans` **×4**; **zero** leftover
2022+
`for (int n = 0; n < 64 …)` serial-N reduction dots. `tl::mma_sync` routes to CuTe
2023+
`SM80_16x8x16_F32F16F16F32_TN`, whose inline asm is
2024+
`mma.sync.aligned.m16n8k16.row.col.f32.f16.f16.f32`.
2025+
- **SASS** (`nvcc -arch=sm_121a -cubin` of the dumped `.cu`, `cuobjdump -sass`): **64×
2026+
`HMMA.16816.F32`** (the machine tensor-core op) + **32× `LDSM`** (ldmatrix). A relabeled serial
2027+
loop cannot produce HMMA in the binary. CONFIRMED real.
2028+
2029+
### Parity — ALL elements, fp16 gate 5e-4 (PASS)
2030+
2031+
| output | new F0 vs serial F0 reference (fp32 einsum) | new F0 vs serial Metal prim (rebuilt on CUDA) |
2032+
|---|---:|---:|
2033+
| `cb` | 1.22e-4 | 2.44e-4 |
2034+
| `summary_states` | 3.74e-6 | 3.74e-6 |
2035+
| `dA_cumsum` (fp16-stored) | 4.89e-4 | 0.0 (verbatim scan) |
2036+
2037+
NaN=False. Both GEMM outputs pass the 5e-4 fp16 gate over the FULL prod tensors (no subset).
2038+
2039+
### F0 → F1 → F2 chain with the new F0 (MEASURED)
2040+
2041+
| stage | ms (median) |
2042+
|---|---:|
2043+
| **F0 (new tensor-core)** | **1.129** |
2044+
| F1 inter-chunk recur | 1.230 |
2045+
| F2 scan+combine | 2.885 |
2046+
| **chain SUM** | **5.244** |
2047+
2048+
The un-fused forward chain drops from **≈30.0 ms** (§23, serial F0) to **5.24 ms** — now **1.69×**
2049+
cppmega's whole fused mamba fwd (3.11 ms), down from ≈9.6×. F0 is no longer the forward bottleneck;
2050+
F2 (2.88 ms) is now the largest forward term.
2051+
2052+
### tok/s effect — EXTRAPOLATION (clearly labelled; NOT a measured step win)
2053+
2054+
The §17 canonical step is **backward-bound**: the gridded backward chain is **447.8 ms** while the
2055+
WHOLE forward chain is **7.231 ms** (§15), of which the §17/§15 step already ran F0 as the **5.025 ms**
2056+
chunked precompute (NOT the 24.57 ms serial — that figure is the §23 *fused-probe* re-measure of the
2057+
same kernel). Dropping F0 to **1.13 ms** saves **≈3.9 ms** off the forward chain, i.e. **≈0.86 % of the
2058+
≈455 ms step**. So the §17 headline **tok/s is essentially UNCHANGED**: **≈907 @8L / ≈298 @28L**, gap
2059+
vs Megatron 3399 **unchanged at ≈3.75×/≈11.4×**. (Step-level tok/s was not re-measured this round; the
2060+
above is the honest step-model EXTRAPOLATION.) The win is **forward-chain-level, not step-level**: F0
2061+
is removed as a hot kernel and the forward chain now sits at near-cppmega (1.69×), closing the
2062+
specific §22/§23 lever. The next step-level lever remains the **backward** (B2 at 334.6 ms, §17 honest
2063+
attribution #3), not F0.
2064+
2065+
### GO / NO-GO
2066+
2067+
- **GO (kernel-level): F0 is now a REAL tensor-core kernel at Tri-Dao parity.** 1.13 ms (14.5–21.8×
2068+
over serial), HMMA-verified at SASS, parity PASS all elements, RULE #1 raises (no serial fallback).
2069+
The §22/§23 "F0 is the 24.57 ms gap lever" finding is CLOSED — the forward chain is now 5.24 ms
2070+
(1.69× cppmega's 3.11 ms whole fwd).
2071+
- **NO-GO (honest, step-level): this does NOT move the headline tok/s.** The §17 step is
2072+
backward-bound (447.8 ms bwd vs 7.2 ms fwd), so a 3.9 ms forward saving is ≈0.86 % of the step.
2073+
§17's ≈907/≈298 stays the canonical number; the next step-level lever is the backward (B2), not F0.

scratch/probe_f0_tc_gb10.py

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
"""GB10/sm_121 F0 tensor-core precompute probe (§26).
2+
3+
Builds the NEW chunk_precompute_fwd_cuda_prim (cb + summary_states T.gemm)
4+
at prod cfg and:
5+
1. dumps generated CUDA, greps mma.sync (REAL sm_120 MMA, not relabeled serial).
6+
2. times new F0 per-call (warm median + min).
7+
3. parity of cb + summary_states + dA_cumsum vs:
8+
(a) the SERIAL F0 reference (eager precompute math),
9+
(b) the SERIAL Metal F0 prim built on CUDA target == NO, the serial prim is
10+
Metal-only; instead we compare against the eager-precompute fp32 reference
11+
AND re-build the *serial* prim source for byte check separately.
12+
over ALL elements (max|abs|), gate fp16 < 5e-4.
13+
4. F0->F1->F2 chain timing with the NEW F0.
14+
15+
RULE #1: every kernel goes through its ONE builder; failures SURFACED.
16+
"""
17+
import sys, time, traceback, re
18+
try:
19+
from cppmega_mlx._gb10_nvrtc_env import ensure_nvrtc_builtins_path
20+
ensure_nvrtc_builtins_path()
21+
except Exception as _e:
22+
print(f"[nvrtc-env] unavailable: {_e!r}")
23+
24+
import torch
25+
from einops import rearrange, repeat
26+
DEV = "cuda"
27+
28+
29+
def eager_precompute_f0(C, Bmat, x, A, dt, chunk_size):
30+
"""SERIAL F0 reference (fp32): cb, dA_cumsum, summary_states.
31+
Mirrors chunk_precompute math exactly (the contract anchor)."""
32+
batch, seqlen, ngroups, dstate = C.shape
33+
_, _, nheads, headdim = x.shape
34+
nchunks = seqlen // chunk_size
35+
h = nheads // ngroups
36+
Cf, Bf, xf, Af, dtf = C.float(), Bmat.float(), x.float(), A.float(), dt.float()
37+
a = Af.view(1, 1, nheads) * dtf
38+
a_c = rearrange(a, "b (c l) hh -> b hh c l", c=nchunks)
39+
dA_cumsum = torch.cumsum(a_c, dim=-1) # (b,h,c,l)
40+
Cc = rearrange(Cf, "b (c l) g n -> b c l g n", c=nchunks)
41+
Bc = rearrange(Bf, "b (c s) g n -> b c s g n", c=nchunks)
42+
cb = torch.einsum("bclgn,bcsgn->bcgls", Cc, Bc) # (b,c,g,l,s)
43+
Bexp = Bc.repeat_interleave(h, dim=3) # (b,c,s,hh,n)
44+
xexp = rearrange(xf, "b (c s) hh p -> b c s hh p", c=nchunks)
45+
dtc = rearrange(dtf, "b (c s) hh -> b hh c s", c=nchunks)
46+
decay_states = torch.exp(dA_cumsum[:, :, :, -1:] - dA_cumsum) # (b,h,c,s)
47+
# summary_states[b,c,hh,p,n] = sum_s decay*dt * x * B
48+
states = torch.einsum("bhcs,bhcs,bcshp,bcshn->bchpn",
49+
decay_states, dtc, xexp, Bexp)
50+
return (cb.float().contiguous(),
51+
dA_cumsum.float().contiguous(),
52+
states.float().contiguous())
53+
54+
55+
def _time(fn, n=30):
56+
for _ in range(5):
57+
fn()
58+
torch.cuda.synchronize()
59+
ts = []
60+
for _ in range(n):
61+
torch.cuda.synchronize()
62+
t0 = time.perf_counter()
63+
fn()
64+
torch.cuda.synchronize()
65+
ts.append((time.perf_counter() - t0) * 1e3)
66+
ts.sort()
67+
return ts[len(ts)//2], ts[0]
68+
69+
70+
def run_cfg(batch, seqlen, chunk, ngroups, nheads, headdim, dstate):
71+
from cppmega_mlx.nn._tilelang.mamba3_chunked_precompute_core import (
72+
build_chunk_precompute_metal, build_inter_chunk_recur_metal,
73+
chunk_precompute_fwd_cuda_prim, chunk_precompute_fwd_metal_prim,
74+
)
75+
from cppmega_mlx.nn._tilelang.mamba3_chunked_scan_core import (
76+
build_chunk_scan_combine_metal,
77+
)
78+
nchunks = seqlen // chunk
79+
print(f"\n=== cfg b={batch} S={seqlen} c={chunk} g={ngroups} H={nheads} "
80+
f"P={headdim} N={dstate} nchunks={nchunks} ===")
81+
82+
torch.manual_seed(0)
83+
C = (torch.randn(batch, seqlen, ngroups, dstate, device=DEV) * 0.1).half()
84+
Bmat = (torch.randn(batch, seqlen, ngroups, dstate, device=DEV) * 0.1).half()
85+
x = (torch.randn(batch, seqlen, nheads, headdim, device=DEV) * 0.1).half()
86+
A = -torch.rand(nheads, device=DEV).half()
87+
dt = (torch.rand(batch, seqlen, nheads, device=DEV) * 0.05).half()
88+
D = torch.randn(nheads, device=DEV).half()
89+
h0 = (torch.randn(batch, nheads, headdim, dstate, device=DEV) * 0.1).float()
90+
91+
# ---- SERIAL F0 reference (fp32 einsum on the SAME fp16 inputs) ----
92+
cb_ref, dac_ref, ss_ref = eager_precompute_f0(
93+
C, Bmat, x, A, dt, chunk)
94+
95+
# =================== NEW CUDA F0 (tensor-core) ===================
96+
try:
97+
k0 = build_chunk_precompute_metal(
98+
batch, seqlen, chunk, ngroups, nheads, headdim, dstate, target="cuda")
99+
except Exception:
100+
print("[F0-CUDA] COMPILE FAILED:")
101+
traceback.print_exc()
102+
return False
103+
print("[F0-CUDA] COMPILE ok")
104+
105+
# ---- dump CUDA source, grep mma.sync ----
106+
try:
107+
src = k0.get_kernel_source()
108+
# tl::mma_sync is the codegen wrapper for the real PTX
109+
# mma.sync.aligned.m16n8k16 (CuTe SM80_16x8x16_F32F16F16F32_TN).
110+
n_mma = src.count("tl::mma_sync") + src.count("mma.sync")
111+
n_ldm = src.count("ptx_ldmatrix")
112+
n_serial_dot = len(re.findall(r"for \(int n = 0; n < %d" % dstate, src))
113+
print(f"[F0-CUDA] codegen: tl::mma_sync x{n_mma} ldmatrix x{n_ldm} "
114+
f"leftover_serial_N_dot x{n_serial_dot} len={len(src)}B")
115+
with open("/tmp/f0_cuda_src.cu", "w") as f:
116+
f.write(src)
117+
print("[F0-CUDA] source -> /tmp/f0_cuda_src.cu")
118+
except Exception:
119+
print("[F0-CUDA] source dump FAILED:")
120+
traceback.print_exc()
121+
n_mma = -1
122+
123+
cb = torch.zeros(batch, nchunks, ngroups, chunk, chunk, device=DEV, dtype=torch.float16)
124+
dA_cumsum = torch.zeros(batch, nheads, nchunks, chunk, device=DEV, dtype=torch.float16)
125+
summary_states = torch.zeros(batch, nchunks, nheads, headdim, dstate, device=DEV, dtype=torch.float32)
126+
127+
def _run_f0():
128+
k0(x.contiguous(), Bmat.contiguous(), C.contiguous(), A.contiguous(),
129+
dt.contiguous(), cb, dA_cumsum, summary_states)
130+
try:
131+
_run_f0(); torch.cuda.synchronize()
132+
except Exception:
133+
print("[F0-CUDA] RUN FAILED:")
134+
traceback.print_exc()
135+
return False
136+
print("[F0-CUDA] RUN ok (LAUNCHES at N=%d)" % dstate)
137+
138+
# ---- PARITY (all elements) of new F0 vs serial F0 reference ----
139+
nan0 = bool(torch.isnan(cb).any()) or bool(torch.isnan(summary_states).any()) \
140+
or bool(torch.isnan(dA_cumsum).any())
141+
cb_err = float((cb.float().cpu() - cb_ref.cpu()).abs().max())
142+
dac_err = float((dA_cumsum.float().cpu() - dac_ref.cpu()).abs().max())
143+
ss_err = float((summary_states.float().cpu() - ss_ref.cpu()).abs().max())
144+
print(f"[F0-CUDA vs SERIAL-ref] cb max|abs|={cb_err:.3e} "
145+
f"dA_cumsum={dac_err:.3e} summary_states={ss_err:.3e} NaN={nan0}")
146+
147+
f0_med, f0_min = _time(_run_f0)
148+
print(f"[F0-CUDA] TIMING median={f0_med:.4f} ms/call min={f0_min:.4f} ms")
149+
150+
# =================== SERIAL METAL F0 prim built on CUDA (the 24.57ms baseline) ===================
151+
# Build the *serial* metal prim explicitly on the CUDA target so we get the
152+
# apples-to-apples 24.57ms serial F0 on the SAME gb10 device.
153+
f0_serial_med = None
154+
cb_s_err = ss_s_err = None
155+
try:
156+
import tilelang
157+
from cppmega_mlx.nn._tilelang.mamba3_chunked_scan_core import _resolve_chunked_compile_target
158+
prim_s = chunk_precompute_fwd_metal_prim(
159+
batch, seqlen, chunk, ngroups, nheads, headdim, dstate)
160+
k0s = tilelang.compile(prim_s, out_idx=[5, 6, 7],
161+
target=_resolve_chunked_compile_target("cuda"))
162+
cb_s = torch.zeros_like(cb); dac_s = torch.zeros_like(dA_cumsum)
163+
ss_s = torch.zeros_like(summary_states)
164+
def _run_f0s():
165+
k0s(x.contiguous(), Bmat.contiguous(), C.contiguous(), A.contiguous(),
166+
dt.contiguous(), cb_s, dac_s, ss_s)
167+
_run_f0s(); torch.cuda.synchronize()
168+
print("[F0-SERIAL on CUDA] RUN ok")
169+
f0_serial_med, f0_serial_min = _time(_run_f0s)
170+
print(f"[F0-SERIAL on CUDA] TIMING median={f0_serial_med:.4f} ms min={f0_serial_min:.4f} ms")
171+
# parity new-CUDA vs serial-prim (the tightest gate: same algorithm, fp16)
172+
cb_s_err = float((cb.float() - cb_s.float()).abs().max())
173+
ss_s_err = float((summary_states.float() - ss_s.float()).abs().max())
174+
dac_s_err = float((dA_cumsum.float() - dac_s.float()).abs().max())
175+
print(f"[F0-CUDA vs F0-SERIAL prim] cb={cb_s_err:.3e} "
176+
f"summary_states={ss_s_err:.3e} dA_cumsum={dac_s_err:.3e}")
177+
except Exception:
178+
print("[F0-SERIAL on CUDA] build/run FAILED (serial prim may be Metal-only):")
179+
traceback.print_exc()
180+
181+
# =================== F0->F1->F2 chain timing with NEW F0 ===================
182+
try:
183+
k1 = build_inter_chunk_recur_metal(
184+
batch, seqlen, chunk, ngroups, nheads, headdim, dstate, target="cuda")
185+
k2 = build_chunk_scan_combine_metal(
186+
batch, seqlen, chunk, ngroups, nheads, headdim, dstate, target="cuda")
187+
except Exception:
188+
print("[CHAIN] F1/F2 COMPILE FAILED:")
189+
traceback.print_exc()
190+
return False
191+
prev_states = torch.zeros(batch, nchunks, nheads, headdim, dstate, device=DEV, dtype=torch.float32)
192+
final_state = torch.zeros(batch, nheads, headdim, dstate, device=DEV, dtype=torch.float32)
193+
out_u = torch.zeros(batch, seqlen, nheads, headdim, device=DEV, dtype=torch.float16)
194+
dt_k = rearrange(dt, "b (c s) hh -> b hh c s", c=nchunks).contiguous()
195+
196+
def _run_f1():
197+
k1(summary_states.contiguous(), dA_cumsum.contiguous(), h0.contiguous(),
198+
prev_states, final_state)
199+
def _run_f2():
200+
k2(cb.contiguous(), x.contiguous(), dt_k.contiguous(), dA_cumsum.contiguous(),
201+
C.contiguous(), prev_states.contiguous(), D.contiguous(), out_u)
202+
try:
203+
_run_f1(); torch.cuda.synchronize()
204+
_run_f2(); torch.cuda.synchronize()
205+
except Exception:
206+
print("[CHAIN] F1/F2 RUN FAILED:")
207+
traceback.print_exc()
208+
return False
209+
f1_med, f1_min = _time(_run_f1)
210+
f2_med, f2_min = _time(_run_f2)
211+
chain = f0_med + f1_med + f2_med
212+
print(f"[CHAIN] F0={f0_med:.4f} F1={f1_med:.4f} F2={f2_med:.4f} "
213+
f"SUM={chain:.4f} ms (medians)")
214+
215+
gate = 5e-4
216+
parity_ok = (cb_err < gate and ss_err < gate and dac_err < 1e-2 and not nan0)
217+
# NOTE: dA_cumsum is fp16-stored so its gate is looser (cumsum magnitude),
218+
# report it but key the gate on cb/summary_states (the GEMM outputs).
219+
print(f"\n[SUMMARY] new_F0={f0_med:.4f} ms "
220+
f"serial_F0={'%.4f'%f0_serial_med if f0_serial_med else 'NA'} ms "
221+
f"baseline_24.57ms chain={chain:.4f} ms")
222+
print(f"[SUMMARY] parity cb={cb_err:.2e} ss={ss_err:.2e} -> "
223+
f"{'PASS' if parity_ok else 'FAIL'} (gate {gate})")
224+
print(f"[CSV] new_f0={f0_med:.4f} new_f0_min={f0_min:.4f} "
225+
f"serial_f0={f0_serial_med if f0_serial_med else -1:.4f} "
226+
f"f1={f1_med:.4f} f2={f2_med:.4f} chain={chain:.4f} "
227+
f"cb_err={cb_err:.3e} ss_err={ss_err:.3e} dac_err={dac_err:.3e} "
228+
f"cb_vs_serialprim={cb_s_err if cb_s_err else -1:.3e} "
229+
f"ss_vs_serialprim={ss_s_err if ss_s_err else -1:.3e} "
230+
f"mma_sync={n_mma} parity={'PASS' if parity_ok else 'FAIL'}")
231+
return parity_ok
232+
233+
234+
def main():
235+
print("=== F0 tensor-core precompute probe (gb10 sm_121) ===")
236+
print("torch", torch.__version__, "cuda", torch.cuda.is_available(),
237+
torch.cuda.get_device_name(0) if torch.cuda.is_available() else "NONE")
238+
if not torch.cuda.is_available():
239+
print("NO CUDA"); sys.exit(2)
240+
cfg = dict(batch=1, seqlen=4096, chunk=64, ngroups=8, nheads=112,
241+
headdim=64, dstate=64)
242+
ok = False
243+
try:
244+
ok = run_cfg(**cfg)
245+
except Exception:
246+
print("CFG CRASHED:"); traceback.print_exc()
247+
print("\nF0 PARITY PASS" if ok else "\nF0 PARITY/RUN FAILURE")
248+
sys.exit(0 if ok else 1)
249+
250+
251+
if __name__ == "__main__":
252+
main()

0 commit comments

Comments
 (0)