Skip to content

Commit c0d4343

Browse files
committed
docs(triton): iteration-4 CopyNode->TMA measured report (UTMALDG mechanism proven, §P1 NO-GO honest)
MEASURED from tilelang HEAD 1378c173 on gb10 sm_121a: - §P1 default path: .cu byte-identical OFF==ON (md5 99c23241), UTMALDG=0, LDG=38, spill=306, HMMA=32, EXEC median 1101.41ms (no drop from 1102), parity MAXDIFF 4.88e-04 PASS; small multi-K-trip 3.05e-05 PASS. - Mechanism proof (ground-truth contiguous C-tile, from HEAD): SASS UTMALDG=6, UTMACCTL=1, LDG 38->32, CUtensorMap=2, mbarrier=22 -- CopyNode->LowerBulk->TMA is real on sm_121. - Honest TMA-ineligibility: dout innermost = seq axis stride 7168 (non-contiguous); C innermost contiguous but opaque-symbolic arg19 (unprovable). Both correctly fall through, no num_stages mis-annotation. RULE #1 clean. - other-paths: route_all7 5/7 mma=2 intact, 2 fails pre-existing.
1 parent d0def7b commit c0d4343

1 file changed

Lines changed: 138 additions & 0 deletions

File tree

docs/TRITON-COPYNODE-TMA.md

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Triton-frontend iteration 4 — real 2D-strided `T.copy` CopyNode for the K-loop producer (TMA / UTMALDG)
2+
3+
Status: **HONEST PARTIAL — mechanism PROVEN, default §P1 path NO-GO on UTMALDG.**
4+
The iteration-4 CopyNode conversion is committed on the routed-triton async path.
5+
The CopyNode IS recognized by `PipelinePlanning` as a proven copy and, when the
6+
innermost global stride is provably contiguous, the C-tile lowers to genuine
7+
**UTMALDG** (TMA bulk async load) on sm_121 — proven by SASS from HEAD. BUT on the
8+
real §P1 de-monomorphized kernel **neither tile is statically TMA-eligible**
9+
(dout's innermost axis is the K-reduction/seq axis, stride 7168, genuinely
10+
non-contiguous; C's innermost is runtime-contiguous but passed as an opaque
11+
symbolic arg so `is_one()` cannot prove it). Both therefore correctly fall through
12+
to the existing predicated-`For` path — the generated `.cu` is **byte-identical**
13+
to iteration-3, UTMALDG stays 0, and there is **no ms drop** from the 1102 ms
14+
iteration-2/-3 baseline.
15+
16+
All numbers below are MEASURED-FROM-COMMITTED-HEAD on gb10 (sm_121a), regenerating
17+
the `_chunk_scan_bwd_dstates` artifacts from HEAD, interleaved OFF/OPT, parity
18+
re-verified under the GPU mutex.
19+
20+
- tilelang committed HEAD: `1378c1737b0dda677a24b3808801ed24745b610a`
21+
(`merge/upstream-codegen-reorg`). gb10 working tree byte-identical to HEAD for
22+
all 4 routed-frontend files (md5-verified: `memory.py 5d6f289d…`,
23+
`control.py 5c7f9cb9…`, `__init__.py 31ebe2f4…`, `op_mapping.py 3648f32f…`).
24+
25+
## What landed (committed, iteration 4)
26+
27+
`poc/triton_frontend/op_emitters/memory.py`:
28+
29+
- `_ptrstate_block_base_and_strides` — splits the collapsed PtrState flat address
30+
into a per-block `elem_offset` (loop-independent offsets + carry advance) and
31+
per-axis strides, preserving the 2D axis-contiguity that the 1D flat-index
32+
lowering had discarded.
33+
- `_emit_ptrstate_tile_load_copynode` — declares the global source (dout/C) as a
34+
2D `tir.Buffer` aliasing the flat arg `data` Var with `strides=[stride0,stride1]`
35+
+ `elem_offset=base`, emits a REAL `T.copy(src2d -> shared_tile)` CopyNode, and
36+
SPLITS the bounds mask OFF into a separate masked epilogue (not inside the
37+
`if_then_else` producer).
38+
- A **TMA-eligibility gate (RULE #1)**: the CUDA bulk-copy lowering hard-requires
39+
a statically-provable contiguous innermost global stride
40+
(`ICHECK(is_one(desc.global_stride[0]))` in `src/backend/cuda/op/copy.cc`). If
41+
the innermost stride is provably 1 (or `routed_contiguous_innermost` supplies
42+
ground-truth contiguity) the CopyNode is emitted; otherwise the function
43+
RETURNS FALSE and the caller keeps the load on the predicated-`For` path
44+
WITHOUT stamping `num_stages` — no predicated-For dressed up as a coalesced copy.
45+
46+
Wired into `_emit_ptrstate_tile_load_tir`, gated on
47+
`routed_triton_async_loads and rank>=2`. The iteration-3 infra
48+
(`async_loads` gate, `_gmem_shared_copies` counter, `map_scf_for._maybe_pipeline`
49+
`num_stages` stamp) is the committed prerequisite.
50+
51+
## Measured — default §P1 path (gb10, `_chunk_scan_bwd_dstates`)
52+
53+
§P1 config: b=1 nh=112 hd=64 ds=64 nc=64 cs=64, BM/BN/BK = 64/64/32.
54+
55+
| metric | async OFF | async ON (HEAD default) | iter-3 baseline | Triton ref |
56+
|---|---|---|---|---|
57+
| generated `.cu` md5 | `99c232418b10c2793928ed63776b76c2` | `99c232418b10c2793928ed63776b76c2` | `99c23241…` ||
58+
| `.cu` byte-identical OFF==ON || **True** | True ||
59+
| SASS UTMALDG | 0 | **0** | 0 ||
60+
| SASS LDGSTS | 0 | 0 | 0 | (Ampere-only) |
61+
| SASS LDG | 38 | 38 | 38 ||
62+
| SASS spill (STL+LDL) | 306 (168+138) | 306 (168+138) | 306 | 0 |
63+
| SASS HMMA | 32 | 32 | 32 | 256 |
64+
| §P1 parity MAXDIFF || **4.882812e-04** | 4.88e-04 ||
65+
| ALLCLOSE 1e-3 || **PASS** | PASS ||
66+
| EXEC ms/kernel (CUDA events, N=50, ×4 interleaved) | median **1101.79** | median **1101.41** | ~1102 | ~1.12 |
67+
| Δ ms (OFF−OPT) || **0.38** (noise) | 0 ||
68+
69+
OFF per-rep: 1101.59 / 1101.99 / 1102.38 / 1101.25.
70+
OPT per-rep: 1101.19 / 1101.35 / 1102.03 / 1101.47.
71+
The `.cu` is byte-identical OFF==ON → the 0.38 ms is run-to-run noise. **No real
72+
drop from 1102.** Small real-strided multi-K-trip parity (parity_all7 `small`:
73+
b1 nh8 hd64 ds64 nc4 cs64 ng2 s256, 2 K-trips, real strides): MAXDIFF=**3.051758e-05**,
74+
ALLCLOSE 1e-3 PASS — no regression from baseline.
75+
76+
## Mechanism PROOF — CopyNode → UTMALDG fires when the innermost is contiguous
77+
78+
Reproducible from HEAD by supplying ground-truth contiguity
79+
(`ctx.routed_contiguous_innermost=True`, the flag the route would set once it has
80+
verified the real tensor's innermost stride == 1) and dropping `disable_tma`. The
81+
C-tile (`[k, ds]`, innermost = ds) then pins a literal-1 innermost and the CopyNode
82+
takes the TMA bulk path:
83+
84+
| metric | default §P1 | ground-truth-contiguous C-tile |
85+
|---|---|---|
86+
| `.cu` `CUtensorMap` | 0 | **2** |
87+
| `.cu` `mbarrier` | 0 | **22** |
88+
| `.cu` `swizzle` | 0 | **1** |
89+
| SASS UTMALDG | 0 | **6** |
90+
| SASS UTMACCTL | 0 | **1** |
91+
| SASS LDG | 38 | **32** (the 6 C-tile loads moved to TMA) |
92+
| SASS HMMA | 32 | 32 (intact) |
93+
94+
This proves the CopyNode→`cuda::Copy::LowerBulk`→UTMALDG path is real and live on
95+
sm_121 — the missing piece was a statically-provable contiguous innermost, not the
96+
pipeline plumbing.
97+
98+
## RULE #1 — honest TMA-eligibility of the §P1 tiles
99+
100+
- **dout tile** is `[hd, k]` → innermost = the **K-reduction (seq) axis**, runtime
101+
stride `nheads*headdim = 112*64 = 7168`. **Genuinely non-contiguous → TMA-INELIGIBLE.**
102+
TMA needs the innermost axis contiguous; dout does not qualify. Correct
103+
fall-through to predicated-`For` (NOT counted as an async copy).
104+
- **C tile** is `[k, ds]` → innermost = ds, runtime stride 1 (contiguous), BUT the
105+
de-monomorphized kernel passes it as opaque `arg19`, so `is_one()` cannot prove
106+
`== 1` at compile time → TMA-INELIGIBLE on the default path. The mechanism proof
107+
above shows it DOES take UTMALDG once contiguity is ground-truthed.
108+
109+
No silent fallback was introduced: when a tile is not provably TMA-eligible the
110+
CopyNode emitter RETURNS FALSE and the load stays on the correct predicated-`For`
111+
path, and the K-loop is NOT mis-annotated with `num_stages`. Parity holds at §P1
112+
(MAXDIFF 4.88e-04, unchanged) and small multi-K-trip (3.05e-05). The GEMM
113+
cooperative path is byte-intact.
114+
115+
## Other paths (no-regression)
116+
117+
`route_all7`: 5/7 route with mma=2 intact (dstates / dc / state_db / state_dx /
118+
ddAcs). The 2 fails (`dcb`: PtrState unresolved SSA `%1044`; `dx`: undefined
119+
`arg4_numel_147`) are PRE-EXISTING and IDENTICAL to baseline — not a regression.
120+
GEMM HMMA=32 intact in the routed kernels.
121+
122+
## GO / NO-GO
123+
124+
- **GO** on the CopyNode mechanism: the real 2D-strided `T.copy` is recognized as a
125+
proven copy and lowers to genuine UTMALDG (SASS UTMALDG=6, LDG 38→32) when the
126+
innermost is contiguous — proven from HEAD.
127+
- **NO-GO** on a §P1 ms drop this iteration: both §P1 tiles are statically
128+
TMA-ineligible (dout genuinely non-contiguous innermost; C contiguous but
129+
opaque-symbolic), so the default path correctly falls through, `.cu` is
130+
byte-identical, UTMALDG=0, EXEC stays 1101 ms.
131+
132+
Remaining gap to native ~1.12 ms is ~983x, now precisely localized to TWO
133+
prerequisites the frontend cannot satisfy alone: (1) the de-monomorphized launch
134+
must surface a literal-1 innermost stride for C (monomorphization / a
135+
route-supplied contiguity contract), and (2) dout needs a transposed tile so its
136+
innermost becomes the contiguous headdim axis rather than the seq axis. With both,
137+
C+dout take UTMALDG; HMMA (32 vs Triton 256) and spill (306) remain the next
138+
fronts after the loads coalesce.

0 commit comments

Comments
 (0)