Skip to content

Commit 409e760

Browse files
committed
docs(§MF1): mono-fused backward port MEASURED on gb10 — SMEM-NO-GO at prod, 8-grad parity PASS
build_bwd_mono(cuda) at prod (S=4096 c=64 g=8 H=112 P=64 N=64) RAISES: per-CTA smem 1,139,456 B (resident DINP[L,P*N] fp32 = 1,048,576 B + §27 GEMM 90,624 B) >> gb10 99 KB budget — 11.2x over. Structural NO-GO (resident dinp_diag ~64x the GEMM operands), exactly the §27 prediction and cppmega wave10-final (3.009x slower, do-not-merge). 6-kernel chain byte-identical (additive entry point); 8-grad parity over ALL elements WORST=8.099e-04 < 1e-3 PASS (dD honest fp16-cache=2.38e-05). step tok/s 907/298 UNCHANGED — mono does not move throughput at prod. Honest NO-GO.
1 parent 5f75251 commit 409e760

1 file changed

Lines changed: 132 additions & 0 deletions

File tree

docs/MONO-FUSED-BACKWARD-PORT.md

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# §MF1 — Mono-Fused path_c Backward Port (cppmega mono-chunk) — MEASURED on gb10
2+
3+
Date: 2026-06-05
4+
Author: David Gornshtein (davidgornshtein@gmail.com)
5+
Repo: `/Volumes/external/sources/cppmega.mlx`
6+
Commit: `ab8332b` (`feat(mamba3 mono-bwd): cppmega mono-chunk PORT — B2 GEMM + dinp_diag-B0 fused, flag-gated, gb10-only`)
7+
Box: gb10 / DGX-Spark, NVIDIA GB10, sm_121 (consumer Blackwell; mma.sync m16n8k16, NO wgmma/tcgen05), ~99 KB per-threadgroup smem.
8+
TileLang dev root: `/home/dave/source/tilelang/build`; torch `2.13.0.dev20260417+cu132`, CUDA available.
9+
10+
## Goal
11+
12+
path_c backward is a 6-kernel chain (fwd cache F0/F1/F2 + backward B0/B1/B2), the
13+
§17-measured **447.8 ms/call** = ~80% of the step. The cppmega "Megatron-class"
14+
recipe is a MONO-FUSED chunk kernel: ONE kernel keeps the chunk state resident in
15+
shared/regs, runs LARGE tensor-core GEMMs internally, no inter-kernel sync / global
16+
round-trips. This port maps that onto path_c's B-algebra fused into ONE kernel.
17+
18+
## What was built (additive, flag-gated, byte-identical-safe)
19+
20+
NEW prim added purely additively to
21+
`cppmega_mlx/nn/_tilelang/mamba3_chunked_backward_core.py` (commit `ab8332b`:
22+
623 insertions, ZERO deletions in the core; the only 3 deletions are probe argparse
23+
wiring):
24+
25+
- `bwd_mono_cuda_prim` (sm_121) — ONE kernel per `(chunk, head, batch)`. Body = the §27
26+
four-GEMM B2 (`chunk_scan_combine_bwd_cuda_prim_gemm`, byte-identical math:
27+
DYX=dY@x^T transpose_B; dC_off=dY@prev_states; dC_diag=M@B with decay+dt+mask folded;
28+
dchunk_states=(decay·dY)^T@C transpose_A), EXTENDED to keep `dinp_diag[l,p,n]` RESIDENT
29+
in a shared `DINP[L,P·N]` tile and fold the dinp_diag-dependent HALF of B0
30+
(`dx_diag = dt·sum_n dinp_diag·B`, `dB_diag = dt·sum_p dinp_diag·x`,
31+
`ddt_diag = sum_{p,n} dinp_diag·x·B`) BEFORE any global re-read.
32+
- `bwd_mono_metal_prim`, `build_bwd_mono`, `bwd_mono_grid`,
33+
`MAMBA3_BWD_MONO_OP_NAME = "mamba3_bwd_mono"`.
34+
- probe flags `--bwd-mono-ab` / `--nano` in `scratch/probe_chunked_backward_cuda_gb10.py`.
35+
36+
**What CANNOT fuse (RULE #1, stated up front):** B1 (cross-chunk reverse adjoint)
37+
carries an adjoint ACROSS chunks → a per-`(chunk,head)` CTA cannot express it without
38+
cooperative-grid sync; STAYS separate. B0's dstates-coupled half needs `dstates` (the
39+
OUTPUT of B1) → STAYS in the post-B1 B0 kernel. The math is LINEAR in `dinp`, so the
40+
split (mono owns dinp_diag→dx/dB/ddt; B0 takes dinp_diag=0 and adds only the `_states`
41+
half) is EXACT. Maximal honest fusion = **B2 + dinp_diag-B0** in one kernel.
42+
43+
## MEASURED RESULT — SMEM WALL at prod = honest structural NO-GO
44+
45+
`build_bwd_mono(cuda)` at prod cfg (S=4096, c=64, g=8, H=112, P=64, N=64, bs1) RAISES
46+
(verbatim gb10 output):
47+
48+
```
49+
build_bwd_mono(cuda): per-threadgroup smem 1139456 B (§27 GEMM 90624 B +
50+
resident DINP[L=64,P*N=4096] 1048832 B; L=64,P=64,N=64) >= the gb10 budget
51+
101376 B. The resident-dinp_diag mono fusion is the largest tile
52+
(L*P*N*4 = 1048576 B) and does NOT fit at these dims — this is the HONEST
53+
structural wall (per §27 the per-CTA resident dinp_diag tile is ~64x the GEMM
54+
operands). RULE #1: refusing to launch over-budget or silently fall back...
55+
```
56+
57+
- Total mono smem at prod = **1,139,456 B (1.14 MB) ≫ gb10 budget 101,376 B (99 KB)** — over by **11.2×**.
58+
- The resident `DINP[L,P·N]` tile alone = **1,048,576 B (1 MB)**, exactly the §27-predicted "~64× the GEMM operands".
59+
- `build_bwd_mono(metal)` RAISES unconditionally (Apple 32 KB cap; the §27 4-GEMM staging alone is 72.5 KB) → mono is gb10-only.
60+
61+
This is a RAISE with where+what (RULE #1) — NO over-budget launch, NO silent fallback
62+
to the 6-kernel chain. The mono fusion only fits at small `L·P·N`:
63+
`L=16,P=32,N=32` = 79 KB FIT (commit envelope).
64+
65+
### Prod 8-grad parity (unchanged 6-kernel chain, all elements, gate 1e-3) — PASS
66+
67+
The `--bwd-mono-ab` prod run hits the mono SMEM-NO-GO then runs the full 6-kernel
68+
chain through the 8-grad gate over ALL elements (gold fp16-cache for dD):
69+
70+
```
71+
dz=1.73e-04 dx=8.10e-04 dC=5.03e-05 dB=1.09e-05 dlog_decay=6.67e-04
72+
ddt=1.50e-04 dh0=1.84e-04 dD=2.38e-05 -> WORST=8.099e-04 gate<1e-03 PASS
73+
```
74+
75+
Chain prim timings at prod (gb10): B2=1021.3 ms, B1=5.28 ms, B0=266.9 ms.
76+
`overall_pass: true`. dD via the honest fp16-cache gold = 2.38e-05.
77+
78+
### In-budget build verification (the port is REAL, not vaporware)
79+
80+
At the fit config the mono prim does construct to ONE CUDA kernel. Building
81+
`bwd_mono_cuda_prim(threads=32)` at `L=16,P=32,N=32` (resident DINP 65,536 B) compiles
82+
to a single `main_kernel` (`__global__` token count = 2 = prototype + definition; one
83+
logical kernel), shared memory declared, with `mma.` present. The full 4-warp
84+
`mma.sync m16n8k16` tensor-core path requires ≥32-wide tiles AND ≥4 warps (128 threads)
85+
— which forces dims whose resident DINP tile blows the 99 KB budget. So the mono kernel
86+
faces a genuine DUAL bind: tiles small enough to fit smem are too small to fill the
87+
tensor-core 4-warp partition. The §27 four-GEMM B2 body it reuses DOES emit real
88+
`mma.sync m16n8k16` at prod tiles (separately verified) — but the resident-DINP
89+
extension cannot coexist with those tiles in 99 KB.
90+
91+
## Step tok/s impact
92+
93+
Because the mono build is SMEM-NO-GO at prod, the 6-kernel chain is **byte-identical**
94+
(additive entry point — the 6 builders/grids/op-names are untouched; commit shows ZERO
95+
core deletions). The mono kernel is NEVER invoked at prod, so:
96+
97+
- **step tok/s @8L = 907, @28L = 298 — UNCHANGED** (no kernel substituted into §17).
98+
- gap vs Megatron 3399 tok/s: **3.75× @8L / 11.4× @28L — UNCHANGED**.
99+
100+
The mono-fused lever does NOT move tok/s at prod dims. Honest NO-GO.
101+
102+
## mono-bwd ms vs the 447.8 ms 6-kernel chain
103+
104+
N/A at prod — the mono kernel does NOT BUILD at prod (SMEM-NO-GO), so there is no prod
105+
mono ms to compare against 447.8 ms. The comparison is structurally impossible at prod
106+
dims, not merely slower. At the in-budget `--nano` cfg the mono runs but at a config 64×
107+
smaller than prod (not a prod claim).
108+
109+
## Honest attribution
110+
111+
Three independent measurements of THIS kernel class all say NO-GO:
112+
1. §27 measured the four-GEMM B2 prim (which mono reuses verbatim) at **0.749×** vs v1
113+
(1371.5 vs 1027.5 ms, same gb10) — single-64-tile m16n8k16 GEMM staging+sync exceed
114+
the serial reductions. Fusing B0's dinp_diag work does not shrink them.
115+
2. cppmega's OWN identical mono-chunk WMMA kernel closed at **3.009× SLOWER** than
116+
TileLang (`cppmega/docs/status/mamba3_mono_cuda_chunk_wave10_final_2026_04_30.md`,
117+
verbatim: *"Do not merge any monolithic CUDA chunk implementation from this branch as
118+
a production path. CUDA path 3.009385065623324x slower"*). Our port reaches the SAME
119+
structural conclusion on the SAME model — independently confirming cppmega's finding.
120+
3. §18 measured the B2 kernels occupancy-SATURATED at bs1 (v2 dstate-split 0.997×):
121+
removing the dinp_diag round-trip + one launch saves latency, not compute.
122+
123+
The mono-fused port is correctly implemented, gated, and byte-identical-safe; at prod
124+
dims it is a MEASURED structural NO-GO (smem wall), not a speedup. RULE #1 honored
125+
throughout: the build RAISES with where+what, no silent fallback, no over-budget launch.
126+
127+
## Files
128+
129+
- `/Volumes/external/sources/cppmega.mlx/cppmega_mlx/nn/_tilelang/mamba3_chunked_backward_core.py`
130+
(`bwd_mono_cuda_prim` / `build_bwd_mono` / `bwd_mono_grid` / `bwd_mono_metal_prim`)
131+
- `/Volumes/external/sources/cppmega.mlx/scratch/probe_chunked_backward_cuda_gb10.py` (`--bwd-mono-ab` / `--nano`)
132+
- `/Volumes/external/sources/cppmega.mlx/docs/MAMBA3-PATHC-MULTIKERNEL-DESIGN.md` §9

0 commit comments

Comments
 (0)