Skip to content

Commit 784a3c3

Browse files
committed
docs(triton-simt-coalesce): iter-7 EXECUTED de-risk -- SIMT route runs to completion, coalescing does NOT drop ms (loads stayed scalar under swizzle)
MEASURED from tilelang HEAD c2796a70 on gb10 sm_121a: compute-sanitizer memcheck: 0 errors (LAUNCHES + RUNS TO COMPLETION, no TMA fault) PARITY OFF/OPT MAXDIFF=4.882812e-04 PASS (bit-identical to HEAD) OPT_MS=1101.97 (N=60x4 interleaved) vs OFF=3145.47 (2.85x) vs native=1.146 OPT cubin: LDG.E.128=0, LDG.E.CONSTANT=38 (loads stayed SCALAR), UTMALDG=0, HMMA=32 VERDICT (RULE#1): coalescing does NOT measurably drop ms because the swizzled shared destination breaks float4 vectorizability of the global load -> scalar LDG.E. TMA sidestepped, kernel runs, but no coalescing speedup. ~961x to native. NO-GO as a speedup lever; next lever = remove/align the shared-dest swizzle.
1 parent c5c5891 commit 784a3c3

1 file changed

Lines changed: 133 additions & 0 deletions

File tree

docs/TRITON-SIMT-COALESCE.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# TRITON-SIMT-COALESCE — iteration 7: SIMT-coalesced de-risk, the EXECUTED ms answer
2+
3+
**Date:** 2026-06-08
4+
**Branch/HEAD:** `merge/upstream-codegen-reorg` @ `c2796a70`
5+
**Target:** NVIDIA GB10, sm_121a (aarch64-linux), CUDA 13.x
6+
**Kernel under test:** routed `_chunk_scan_bwd_dstates` at §P1
7+
(`b1 nh112 hd64 ds64 nc64 cs64`, s=4096, grid `(1,64,112)`)
8+
9+
## The question (4 iterations could not answer)
10+
11+
Does routing the dstates K-loop `dout`/`C` global→shared `T.copy` as a **coalesced
12+
load** measurably **drop §P1 ms**? Iterations 3–6 kept hitting TMA: iter-6 got the C
13+
tile to lower to a real `UTMALDG` and the kernel LAUNCHED, but the **sm_90 TMA template
14+
(`cp.async.bulk.tensor.2d`) FAULTS at runtime on sm_121** (compute-sanitizer: illegal
15+
instruction at `tl::tma_load … copy_sm90.h:96`). So no executed ms ever came out.
16+
17+
## What iteration 7 did — SIDESTEP TMA, route SIMT
18+
19+
Set `disable_tma=True` on the routed C/dout CopyNodes so `Copy::Lower`
20+
(`src/backend/cuda/op/copy.cc`, gated by `SelectCopyInstForLowering` on `!disable_tma`)
21+
takes the **SIMT branch** (plain `LDG`), never `LowerBulk`/TMA. No tensorMap, no
22+
`cp.async.bulk.tensor`, no sm_90 template — the sm_121 fault is gone.
23+
24+
### The lever (no hack): restore the per-Call annotations channel
25+
26+
In this fork `tirx.call_intrin` did `del annotations, kwargs` and the FFI `tirx::Call`
27+
factory took only 4 args, so `T.copy(..., disable_tma=True)` **silently dropped** the
28+
annotation and the copy was force-classified TMA-bulk. Fixed in `3rdparty/tvm`
29+
(committed source, byte-identical on gb10):
30+
31+
- `python/tvm/tirx/op.py`: `del kwargs` only; `return Call(dtype, func_name, args, span, annotations=annotations)`
32+
- `src/tirx/ir/expr.cc`: 5-arg `tirx::Call` ctor threads `annotations``CallNode::annotations`; absent ⇒ unchanged 4-arg path
33+
- `python/tvm/tirx/expr.py`, `src/target/llvm/codegen_cpu.cc`: supporting
34+
35+
This is a *restore* of a real channel (the old `del annotations` was itself a silent
36+
fallback), not a special-case. `disable_tma` now reaches `CopyNode`.
37+
38+
Route lever: `poc/triton_frontend/op_emitters/memory.py`
39+
`_emit_ptrstate_tile_load_copynode` now emits `T.copy(src2d, tile_buf, disable_tma=True)`
40+
unconditionally for the routed C/dout tiles.
41+
42+
## MEASURED FROM HEAD (c2796a70) on gb10 — all EXECUTED, runs-to-completion
43+
44+
Harness: committed `poc/triton_frontend/_test_harness/tridao_parity/measure_dstates_simt_coalesced.py`
45+
(sha256 `4a63d05…`), N=60/rep × 4 reps, interleaved OFF/OPT, CUDA-event median-of-medians.
46+
47+
| metric | value |
48+
|---|---|
49+
| **compute-sanitizer memcheck** | **0 errors** — LAUNCHES + RUNS TO COMPLETION, no TMA fault |
50+
| PARITY OFF | MAXDIFF **4.882812e-04** ALLCLOSE PASS |
51+
| PARITY OPT | MAXDIFF **4.882812e-04** ALLCLOSE PASS (bit-identical to HEAD) |
52+
| OPT cubin TMA | UTMALDG=0, no `tma_load`/`tensorMap`/`cp.async.bulk.tensor`/LDGSTS |
53+
| **OPT_MS** (SIMT route) | **1101.97 ms** |
54+
| OFF_MS (un-routed serial) | 3145.47 ms |
55+
| speedup vs OFF | 2.85x |
56+
| HMMA (GEMM cooperative) | **32** (intact) |
57+
| native triton §P1 | **1.146 ms** (median N=100; min 1.139) |
58+
59+
### EXECUTED SASS of the sm_121a cubin (fresh cuobjdump -sass, this run)
60+
61+
```
62+
LDG.E.CONSTANT 38 # scalar 32-bit global loads (the routed C/dout loads)
63+
LDG.E.128 0 # NO vectorized coalesced global loads
64+
LDGSTS / UTMALDG 0 # no cp.async, no TMA
65+
STS.128 / LDS.128 324/108 # wide ops are SHARED-side only
66+
STL.128 / LDL.128 168/136 # wide ops are LOCAL-side only
67+
HMMA 32 # GEMM tensor-core path intact
68+
STG.E 4
69+
```
70+
71+
## THE COALESCING VERDICT (RULE #1 honest)
72+
73+
**Coalescing does NOT measurably drop §P1 ms — because the loads did not actually
74+
become coalesced 128-bit loads.** The executed cubin shows **LDG.E.128 = 0 /
75+
LDG.E.CONSTANT = 38**: the routed C/dout global loads stayed **scalar**. OPT_MS =
76+
1101.97 ms == the prior *routed* best (~1102 ms); the SIMT route ran to completion and
77+
TMA is fully sidestepped, but it delivered no coalescing speedup.
78+
79+
**Root cause:** the **swizzled shared-memory destination layout** breaks float4
80+
vectorizability of the *global* load. The wide 128-bit traffic is all shared/local
81+
(`STS.128`/`LDS.128`/`STL.128`/`LDL.128`) — the SIMT loop vectorizer cannot prove a
82+
contiguous 128-bit window on the global side under the swizzle, so it emits scalar
83+
`LDG.E.CONSTANT`. Contiguity of the ds/hd axis (stride 1) is necessary but not
84+
sufficient: the swizzle on the destination defeats the source-side vectorizer.
85+
86+
The 2.85x vs OFF is the **addressing-fold / route restructuring** win (un-routed serial
87+
→ routed), NOT a coalescing win. It is identical to the prior routed best with TMA
88+
removed.
89+
90+
## Remaining gap to native (honest)
91+
92+
OPT **1101.97 ms** vs native **1.146 ms****~961x slower than native**. The 2.85x is
93+
only vs the un-routed serial OFF baseline. Coalescing was the lever to start closing the
94+
961x gap; it did not fire because the loads stayed scalar. The dominant cost is
95+
elsewhere (per-element scalar global traffic + the routed serial structure), not the
96+
load width alone.
97+
98+
## Separate blocker (not the coalescing question)
99+
100+
`async_loads=True` (pipelining the masked routed copy into cp.async) hits a distinct
101+
`PipelinePlanning` overlapping-write conflict (CopyNode + mask epilogue both write the
102+
tile). Out of scope here; the coalescing property is a load-width property independent of
103+
pipelining and is answered above.
104+
105+
## GO / NO-GO
106+
107+
**NO-GO on "SIMT-coalesced loads drop §P1 ms" as a speedup lever — on the merits, with
108+
the executed number that 4 iterations could not produce.** The kernel finally runs to
109+
completion (sanitizer-clean, parity bit-correct), TMA is sidestepped, but the loads do
110+
not vectorize to LDG.E.128 under the swizzled shared destination, so ms does not drop.
111+
To actually realize coalescing the **shared-destination swizzle must be removed/aligned**
112+
so the global load can be proven 128-bit-contiguous — that is the next lever, not more
113+
TMA work.
114+
115+
## Reproduce from HEAD (c2796a70)
116+
117+
gb10 working tree byte-identical to committed sources (sha256-verified):
118+
`memory.py` `a6c7218…`; tvm `expr.py/op.py/expr.cc/codegen_cpu.cc` all match `4418b2fe`.
119+
`libtvm_compiler.so` newer than the touched `.cc` (fix compiled in).
120+
121+
```
122+
ssh gb10 'cd /home/dave/source/tilelang && \
123+
/home/dave/cppmega-venv/bin/python \
124+
poc/triton_frontend/_test_harness/tridao_parity/measure_dstates_simt_coalesced.py'
125+
# compute-sanitizer:
126+
ssh gb10 'cd /home/dave/source/tilelang && /usr/local/cuda/bin/compute-sanitizer \
127+
--tool memcheck /home/dave/cppmega-venv/bin/python /tmp/ttir7/sanitizer_dstates.py'
128+
# SASS:
129+
ssh gb10 '/usr/local/cuda/bin/cuobjdump -sass \
130+
/tmp/tvm-debug-mode-tempdirs/<OPT-tempdir>/00000/tvm_kernels.cubin'
131+
```
132+
133+
(`/tmp/ttir7/_chunk_scan_bwd_dstates.ttir` is the routed TTIR input.)

0 commit comments

Comments
 (0)