Skip to content

Commit 77cb4a6

Browse files
committed
perf(path_c): gridded CUDA SSD chunked-scan replaces MR single-block serial mamba recurrence (6.56s -> ~7.2ms forward, sm_121)
The Path-C MR mamba sub-region's forward was a single-block T.Kernel(1, threads=1024) O(S)=4096-step SERIAL SSD recurrence (external_state_recurrence) = 6.56 s/call (95% of the train step @8l). This ports the already-Metal-validated chunked SSD grid kernels (F0/F1/F2) to target=cuda/sm_121 and drives the MR mamba region with them. WHAT CHANGED (all in cppmega_mlx; no tilelang/codegen changes needed): - _msl_transform: add _as_cuda_target (CUDA sibling of _as_metal_target). - mamba3_chunked_*_core: thread `target=` through all 7 build_*_metal builders (default Metal => existing Metal callers byte-identical; "cuda" => CUDA backend). Single _resolve_chunked_compile_target() maps intent -> tvm.target.Target. - mamba3_chunked_scan_core: NEW chunk_scan_fwd_cuda_prim — CUDA twin of the Metal F2. IDENTICAL SSD math; only the backend-shaped codegen differs: * acc_o is a REGISTER FRAGMENT (CUDA T.gemm asserts fragment C accumulator; Metal staged C in shared for the 8x8 simdgroup route). * x_shared plain "shared" (drop Metal "shared.dyn"); no make_swizzled_layout. * disable_tma=True on every global<->shared copy + pass_configs {tl.disable_tma_lower, tl.disable_warp_specialized}: on sm_121 the GEMM/copy TMA tensormap descriptor is 32/16-byte aligned where TMA needs 64-byte ("Invalid TMA descriptor arguments"); non-TMA cp.async path is correct. compile_chunk_scan_fwd_metal selects the CUDA prim + pass_configs when target=cuda. - path_c_fusion_schedules: _mamba3_chunked_grid_delegation_prim now passes target=cuda on a CUDA host and block_Dstate=dstate to F2 (the default 128 would over-slice a production dstate=64 state axis). MEASURED on gb10 tvm.cuda(0) / sm_121 (scratch/probe_chunked_scan_cuda_gb10.py), production local_gb10_quarter mamba shape S=4096 chunk=64 heads=112 head_dim=64 state_dim=64 groups=8, grid (112,4,64)=28672 threadgroups vs the serial forward's 1: F2 scan+combine : 0.984 ms/call (parity vs serial fwd 4.75e-4 fp16, PASS<5e-4) F0 precompute : 5.025 ms/call (cb exact, dA_cumsum 9.8e-4) F1 inter-chunk : 1.209 ms/call (prev_states 1.31e-4) TOTAL forward : ~7.2 ms/call vs the serial scan's 6559.7 ms/call (~900x). The scan recurrence itself (F2, what literally replaced the serial carry) is 0.984 ms vs 6.56 s (~6670x). RULE #1 numeric equivalence: same SSD math, fp16 parity verified vs the explicit per-timestep serial reference at all probed shapes. NO REGRESSION on Metal: 21 chunked Metal tests pass (F0/F1/F2 + chained + region-flip); disable_tma annotations are inert on Metal (TMA does not exist there), CUDA prim only selected for target=cuda. REMAINING (not in this commit): the BACKWARD chunked cores (B0/B1/B2) are now target-threaded too but their CUDA run + the full flag-ON train_step (fwd+bwd) e2e drive is a follow-up — B0/B1/B2 use NO T.gemm (plain TileLang, like F0/F1) so they are expected to port with the same target threading; to be validated separately.
1 parent cc236fd commit 77cb4a6

6 files changed

Lines changed: 714 additions & 41 deletions

File tree

cppmega_mlx/nn/_tilelang/_msl_transform.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,6 +1469,42 @@ def _as_metal_target_uncached(target: Any) -> Any:
14691469
return spec
14701470

14711471

1472+
def _as_cuda_target(target: Any = "cuda") -> Any:
1473+
"""Coerce a CUDA target spec into a ``tvm.target.Target`` for TileLang.
1474+
1475+
The mamba3 chunked-SSD grid kernels (F0/F1/F2 + B0/B1/B2) were authored
1476+
against the Metal codegen path via :func:`_as_metal_target`. On a CUDA host
1477+
(gb10 / sm_121) the SAME ``@T.prim_func`` bodies lower to CUDA unchanged, but
1478+
the builders must select a CUDA ``tvm.target.Target`` instead of a Metal one.
1479+
This helper is the CUDA sibling of :func:`_as_metal_target`: it returns a
1480+
``tvm.target.Target`` for the bare ``"cuda"`` kind (TileLang's
1481+
``determine_target`` allowlist accepts the Target object) or coerces a
1482+
passed-through spec/Target.
1483+
1484+
The arch (sm_121) is inferred by TVM from the live device at compile time;
1485+
we do NOT pin ``-arch`` here so the same call works on any CUDA device the
1486+
runtime actually owns. Returns the input unchanged when TVM is unavailable so
1487+
a non-TVM host sees a clear downstream error (RULE #1: no silent degrade).
1488+
"""
1489+
try:
1490+
from tilelang import tvm # type: ignore
1491+
except Exception:
1492+
try:
1493+
import tvm # type: ignore
1494+
except Exception:
1495+
return target
1496+
1497+
if not isinstance(target, str):
1498+
try:
1499+
return tvm.target.Target(target)
1500+
except Exception:
1501+
return target
1502+
try:
1503+
return tvm.target.Target(target)
1504+
except Exception:
1505+
return target
1506+
1507+
14721508
# Wave 5 grok finding #1: optional self-validation of the full-signature
14731509
# corpus at import time. Off by default to keep import fast; enable in CI or
14741510
# during local development by setting ``CPPMEGA_VALIDATE_PARSE_TESTS=1``.
@@ -1496,6 +1532,7 @@ def _as_metal_target_uncached(target: Any) -> Any:
14961532
"MSLDispatchUnsupported",
14971533
"MetalKernel",
14981534
"TileLangMSLLowering",
1535+
"_as_cuda_target",
14991536
"_as_metal_target",
15001537
"_assert_path_c_metal_fp8_intrinsics_registered",
15011538
"can_run_metal",

cppmega_mlx/nn/_tilelang/mamba3_chunked_backward_core.py

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -399,25 +399,30 @@ def build_chunk_scan_combine_bwd_metal(
399399
nheads: int,
400400
headdim: int,
401401
dstate: int,
402+
*,
403+
target: Any = None,
402404
**kwargs: Any,
403405
) -> Any:
404-
"""Compile the B2 scan+combine backward kernel to a Metal ``JITKernel``.
406+
"""Compile the B2 scan+combine backward kernel to a ``JITKernel``.
405407
406-
Outputs (dC, dx, dz, dchunk_states, dinp, dA_cumsum_y, dD) are the trailing 7
407-
params; pass PRE-ZEROED contiguous buffers positionally. RULE #1: compile
408-
failures propagate (no serial fallback).
408+
``target`` selects Metal (default) or CUDA (``"cuda"`` / sm_121). Outputs
409+
(dC, dx, dz, dchunk_states, dinp, dA_cumsum_y, dD) are the trailing 7 params;
410+
pass PRE-ZEROED contiguous buffers positionally. RULE #1: compile failures
411+
propagate (no serial fallback).
409412
"""
410413
import tilelang
411414

412-
from cppmega_mlx.nn._tilelang import _msl_transform
415+
from cppmega_mlx.nn._tilelang.mamba3_chunked_scan_core import (
416+
_resolve_chunked_compile_target,
417+
)
413418

414419
prim = chunk_scan_combine_bwd_metal_prim(
415420
batch, seqlen, chunk_size, ngroups, nheads, headdim, dstate, **kwargs
416421
)
417422
return tilelang.compile(
418423
prim,
419424
out_idx=[11, 12, 13, 14, 15, 16, 17],
420-
target=_msl_transform._as_metal_target("metal -thread_warp_size=32"),
425+
target=_resolve_chunked_compile_target(target),
421426
)
422427

423428

@@ -571,25 +576,30 @@ def build_inter_chunk_recur_bwd_metal(
571576
nheads: int,
572577
headdim: int,
573578
dstate: int,
579+
*,
580+
target: Any = None,
574581
**kwargs: Any,
575582
) -> Any:
576-
"""Compile the B1 reverse inter-chunk recurrence kernel to a Metal JITKernel.
583+
"""Compile the B1 reverse inter-chunk recurrence kernel to a ``JITKernel``.
577584
578-
Outputs (dstates, dh0, dA_cumsum_tail) are the trailing 3 params; pass
579-
PRE-ZEROED contiguous fp32 buffers positionally. RULE #1: compile failures
580-
propagate (no serial fallback).
585+
``target`` selects Metal (default) or CUDA (``"cuda"`` / sm_121). Outputs
586+
(dstates, dh0, dA_cumsum_tail) are the trailing 3 params; pass PRE-ZEROED
587+
contiguous fp32 buffers positionally. RULE #1: compile failures propagate
588+
(no serial fallback).
581589
"""
582590
import tilelang
583591

584-
from cppmega_mlx.nn._tilelang import _msl_transform
592+
from cppmega_mlx.nn._tilelang.mamba3_chunked_scan_core import (
593+
_resolve_chunked_compile_target,
594+
)
585595

586596
prim = inter_chunk_recur_bwd_metal_prim(
587597
batch, seqlen, chunk_size, ngroups, nheads, headdim, dstate, **kwargs
588598
)
589599
return tilelang.compile(
590600
prim,
591601
out_idx=[4, 5, 6],
592-
target=_msl_transform._as_metal_target("metal -thread_warp_size=32"),
602+
target=_resolve_chunked_compile_target(target),
593603
)
594604

595605

@@ -822,23 +832,28 @@ def build_chunk_precompute_bwd_metal(
822832
nheads: int,
823833
headdim: int,
824834
dstate: int,
835+
*,
836+
target: Any = None,
825837
**kwargs: Any,
826838
) -> Any:
827-
"""Compile the B0 precompute backward kernel to a Metal ``JITKernel``.
839+
"""Compile the B0 precompute backward kernel to a ``JITKernel``.
828840
829-
Outputs (dx, dB, dlog_decay, ddt) are the trailing 4 params; pass PRE-ZEROED
841+
``target`` selects Metal (default) or CUDA (``"cuda"`` / sm_121). Outputs
842+
(dx, dB, dlog_decay, ddt) are the trailing 4 params; pass PRE-ZEROED
830843
contiguous fp32 buffers positionally (dx is accumulated into — B2 wrote the
831844
D-skip path first). RULE #1: compile failures propagate (no serial fallback).
832845
"""
833846
import tilelang
834847

835-
from cppmega_mlx.nn._tilelang import _msl_transform
848+
from cppmega_mlx.nn._tilelang.mamba3_chunked_scan_core import (
849+
_resolve_chunked_compile_target,
850+
)
836851

837852
prim = chunk_precompute_bwd_metal_prim(
838853
batch, seqlen, chunk_size, ngroups, nheads, headdim, dstate, **kwargs
839854
)
840855
return tilelang.compile(
841856
prim,
842857
out_idx=[9, 10, 11, 12],
843-
target=_msl_transform._as_metal_target("metal -thread_warp_size=32"),
858+
target=_resolve_chunked_compile_target(target),
844859
)

cppmega_mlx/nn/_tilelang/mamba3_chunked_precompute_core.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -221,25 +221,31 @@ def build_chunk_precompute_metal(
221221
nheads: int,
222222
headdim: int,
223223
dstate: int,
224+
*,
225+
target: Any = None,
224226
**kwargs: Any,
225227
) -> Any:
226-
"""Compile the F0 precompute kernel to a Metal ``JITKernel``.
228+
"""Compile the F0 precompute kernel to a ``JITKernel`` (Metal or CUDA).
227229
228-
Outputs (cb, dA_cumsum, summary_states) are the 6th/7th/8th params; pass
229-
PRE-ZEROED contiguous buffers positionally (no ``out_idx`` allocation), and
230-
``torch.mps.synchronize()`` after. RULE #1: compile failures propagate.
230+
``target`` selects the codegen backend (``None`` => Metal default, ``"cuda"``
231+
=> CUDA / sm_121). Outputs (cb, dA_cumsum, summary_states) are the 6th/7th/8th
232+
params; pass PRE-ZEROED contiguous buffers positionally (no ``out_idx``
233+
allocation), and synchronize the device after. RULE #1: compile failures
234+
propagate.
231235
"""
232236
import tilelang
233237

234-
from cppmega_mlx.nn._tilelang import _msl_transform
238+
from cppmega_mlx.nn._tilelang.mamba3_chunked_scan_core import (
239+
_resolve_chunked_compile_target,
240+
)
235241

236242
prim = chunk_precompute_fwd_metal_prim(
237243
batch, seqlen, chunk_size, ngroups, nheads, headdim, dstate, **kwargs
238244
)
239245
return tilelang.compile(
240246
prim,
241247
out_idx=[5, 6, 7],
242-
target=_msl_transform._as_metal_target("metal -thread_warp_size=32"),
248+
target=_resolve_chunked_compile_target(target),
243249
)
244250

245251

@@ -363,23 +369,28 @@ def build_inter_chunk_recur_metal(
363369
nheads: int,
364370
headdim: int,
365371
dstate: int,
372+
*,
373+
target: Any = None,
366374
**kwargs: Any,
367375
) -> Any:
368-
"""Compile the F1 inter-chunk recurrence kernel to a Metal ``JITKernel``.
376+
"""Compile the F1 inter-chunk recurrence kernel to a ``JITKernel``.
369377
370-
Outputs (prev_states, final_state) are the 4th/5th params; pass PRE-ZEROED
371-
contiguous fp32 buffers positionally, ``torch.mps.synchronize()`` after.
372-
RULE #1: compile failures propagate (no serial fallback).
378+
``target`` selects Metal (default) or CUDA (``"cuda"`` / sm_121). Outputs
379+
(prev_states, final_state) are the 4th/5th params; pass PRE-ZEROED contiguous
380+
fp32 buffers positionally, synchronize the device after. RULE #1: compile
381+
failures propagate (no serial fallback).
373382
"""
374383
import tilelang
375384

376-
from cppmega_mlx.nn._tilelang import _msl_transform
385+
from cppmega_mlx.nn._tilelang.mamba3_chunked_scan_core import (
386+
_resolve_chunked_compile_target,
387+
)
377388

378389
prim = inter_chunk_recur_fwd_metal_prim(
379390
batch, seqlen, chunk_size, ngroups, nheads, headdim, dstate, **kwargs
380391
)
381392
return tilelang.compile(
382393
prim,
383394
out_idx=[3, 4],
384-
target=_msl_transform._as_metal_target("metal -thread_warp_size=32"),
395+
target=_resolve_chunked_compile_target(target),
385396
)

0 commit comments

Comments
 (0)