[None][perf] Fuse MiniMax-M3 MSA per-layer KV-cache writes into one kernel#16755
Merged
Conversation
zheyuf
force-pushed
the
perf/m3-msa-prep
branch
2 times, most recently
from
July 24, 2026 02:22
fe63ae9 to
5611b35
Compare
…ernel Each MSA layer wrote its new-token main K, main V, and (sparse layers) index-K through three separate aten advanced-indexing writes, and every write re-derived the same (page, within-page) slot split on device: a cast, a floor-div, and a modulo kernel per write. At 60 layers that is ~730 tiny kernels per decode step (~12 per sparse layer), all captured into decode CUDA graphs and re-executed every step. Replace them with one Triton launch per layer that scatters all three tensors into their paged HND caches, deriving each token's slot split from out_cache_loc in-register and casting to the cache dtype on store (which folds in the FP8 KV-cache cast). The fused write runs at the top of the layer's attention core, before the indexer proxy pass reads the index-K cache; run_indexer and run_msa_paged_gqa skip their legacy writes via an explicit prewritten marker and fall back unchanged whenever the fused path reports an unsupported layout. Measured on 4x B200 (TP4/EP4, MXFP8, MSA, decode CUDA graphs, 1k/1k, concurrency 64), warm A/B on TOT (incl. NVIDIA#16695), mean of 2 runs: +6.4% output throughput (2166 -> 2304 tok/s), -20.6% total kernel launches, aten elementwise time per rank-step 2.59 ms -> 0.96 ms, with GEMM/NCCL/FMHA buckets unchanged. NVFP4 (fp8 KV) shows +7.9%. Signed-off-by: Zheyu Fu <zheyuf@NVIDIA.com>
… module Keep only the reference-parity test (8 parametrized cases covering fp8/bf16 caches, 1/4 KV heads, with/without the index cache) and move it into test_minimax_m3_msa_backend.py; drop the layout-rejection, int64-offset, and boundary-slot micro-tests along with the standalone scatter test file. Signed-off-by: Zheyu Fu <zheyuf@NVIDIA.com>
zheyuf
marked this pull request as ready for review
July 24, 2026 08:57
zheyuf
requested review from
PerkzZheng,
WeiHaocheng,
brb-nv,
pcicotti,
peihu-nv and
pengbowang-nv
and removed request for
a team,
PerkzZheng,
WeiHaocheng and
pengbowang-nv
July 24, 2026 08:57
WeiHaocheng
approved these changes
Jul 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Where this PR targets at:
MiniMax-M3 MSA per-layer KV-cache writes fusion (K, V, index-K):
Take a sparse layer as example:
The problem
Each MSA layer writes its new-token main K, main V, and (sparse layers) index-K. Writing of K, V and index-K takes four tiny kernels: division, remainder, index cast, index_put scatter. So that's 3*4=12 kernel launches per sparse layer. At 60 layers that is ~720 tiny kernels per decode step, all captured into decode CUDA graphs and re-executed on every step. It accumulates to large launch overhead.
The fix
This PR replaces them with one Triton launch per layer (~720 → 60) launches on the write path each iter).
Measured impact (4x B300, InferenceMAX-style serving benchmark)
trtllm-serve+benchmark_serving, NVFP4 + fp8 KV cache, MSA, decode CUDA graphs + overlap scheduler, no spec decode, random 8k/1k, identical seeded prompt sets, JIT/autotuner warmed.Nsys trace for a layer (look at green parts)
Before fix: 4 tiny kernels-to-be-fused for index-K, then 3 kernels for indexer, then 8 tiny kernels-to-be-fused for K and V.

After fix: 1 fused Triton launch (contains previous 12 kernels for K, V and K-index), then 3 kernels for indexer. So it's fuse + reorder: compute the page/offset once, write K + V + index-K together, before the indexer runs.

Test Coverage
PR Checklist