Repository navigation
Conversation
… step-loop runtime hooks Adds the modeling_v2 target minimax_m3_nvfp4__sm_103__tp4 (GB300, TP4, Eagle3 one-model speculation) and the executor seams it opts into. - Runtime hooks (off unless a model declares them): packed step inputs, skipped unchanged H2D uploads, a private graph metadata pool, sampler greedy-token and step-fold paths, MSA runtime policy, and rank-merged autotuned tactics. Greedy-only outputs are kept only when nothing reads the logits (no guided decoder, non-empty batch). - TP4 decode target: fused attention with long-context selection, a pre-wait-staged NVFP4 QKV/indexer producer and index scorer, multicast all-reduce + RMSNorm boundaries with the MoE finalize folded in, MXFP8 GEMVs, CuTe DSL MoE engines, an exact greedy and sampled verify, a fused draft loop, and lossless 12-bit coded BF16 target and draft heads. - Explicit dispatch: every covered shape is a certified catalog cell; under modeling_v2: require a covered shape that cannot run raises. Every item is on by default with one constant that turns it off; no environment reads. - Numerics match stock up to summation order; the long-context selector breaks >1024-way score ties by its contract (smaller block). Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
Pick the row source explicitly, raise when neither logits nor greedy tokens are present, and assert logits before the fast greedy kernel. Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
Adds the modeling_v2 target minimax_m3_nvfp4__sm_103__tp8 (GB300, 8 ranks over two trays, tensor-parallel attention and experts, one-model Eagle3 or no speculation) and routes world = tp = moe_tp = 8 to it; the tp4 branch of the router is unchanged. - Decode path: the tp4 target's exact decode path at the TP8 geometry, plus the TP8 items: the 1..8-token MoE engine at expert width 384 (logits replicated 32x at <= 4 tokens), an 8-head one-launch long-context sparse decode staged before its grid wait at <= 8 rows, a fused index score + top-16 select for spans of <= 2 requests, the producer releasing its dependents on entry, the long-row select's early release at <= 8 rows, the TP8 MXFP8 GEMV table, fixed-tactic low-M BF16 GEMMs for the drafter linears, the drafter decode's KV split capped at 16K for one request, BF16 target and draft heads, and the drafter fc forked beside the verify head. - Isolation: the target imports nothing from the tp4 target; the MoE engine and the MX GEMV op it changes are its own copies inside the target. The two new kernels are compiled ops (cpp/tensorrt_llm/kernels/minimaxM3Tp8, trtllm::minimax_m3_tp8_*). A drift test pins the files copied unchanged from the tp4 target. - fmha/flashinfer_trtllm_gen.py applies a layer's KV-split cap (msa_decode.dense_decode_max_seq_len); a no-op unless a layer sets one. - Same contract as the tp4 target: every kernel a catalog entry with CELLS and a torch reference, dispatch.py listing each fast path's rows, a covered shape that cannot run raising under modeling_v2: require, kernels built before warmup, every item on by default with one constant to turn it off. - Numerics match stock up to summation order (BF16 heads and drafter, NVFP4 experts, MXFP8 projections). Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
CUtensorMap already carries its own alignment, which is stricter than 64 bytes in newer CUDA toolkits; clang-hosted nvcc rejects the weaker alignas with "alignment cannot be set to less than the default alignment" (SBSA build). Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
Resolve two additive conflicts with the Qwen4-exp modeling_v2 target by keeping both router entries and both routing tests. Port the sampler's greedy_tokens_usable to main's request_row_params after the TorchSampler strategy refactor (NVIDIA#20188) removed _request_strategy. Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
- Register fake implementations for the new step_inputs_scatter and MiniMax-M3 router, low-M GEMM, producer and allreduce-norm ops. - Give the warmup tests' stand-in engines the default runtime hooks the decoder runner now reads. - Leave the identity table unset in the piecewise-attention metadata mock. - Stub the eager K/V view resolution only while the runtime-policy fixture builds its manager, so the pool-view tests still exercise get_buffers. Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
Restructure the minimax_m3_nvfp4__sm_103__tp4 and __tp8 targets into the flat layout the modeling_v2 exemplars use, with no change to what they compute. Each target owns its own copy of every file (the targets share no code): * modeling.py: the dispatch tables (FAST_PATHS, row ranges, miss/uncovered policy), the flat core, PrefillTarget / DecodeTarget, the one-model Eagle3 drafter and worker, the step fold, runtime hooks and the shell. The core selects the target on num_contexts alone: every pure generation step is DecodeTarget's (its decode kernels up to 48 tokens, its own generic layers for wider steps or embeddings), every step with context rows PrefillTarget's. The two targets share no body: each owns its layer, attention and MoE compositions, and its step_args is the one projection of the MSA metadata its forward reads (DecodeTarget states num_contexts=0 / num_ctx_tokens=0). Shared module-level code is limited to single-call op adapters that read no step metadata. * weights.py: the language-model and drafter weight tables (host-side TP slicing, consumed checkpoint keys released during the load; at TP8 the 4 KV / index heads are duplicated on rank pairs as the built-in loader does). * ops/: catalog entries with certified cells for every call the model makes; kernel wrappers route through their entries. The MSA and drafter attention entries carry a numeric reference and GPU tests that drive every cell against real caches and prepared metadata. * kernels/: the CuTe DSL GEMV, MoE engine and embedding+first-norm kernels move into each target (the old shared cute_dsl_kernels/minimax_m3 copy is removed); capacity annotations on every constant. * The legacy dispatch/layers/linears/runtime/spec_fold/spec_worker modules are removed; the unit tests are ported to the flat targets. * The unfused QKV / index projection runtime (fuse_qkv_index_projection=False, FP8 indexer, FP8 or NVFP4 main KV) runs as the built-in model does. Routing (models/minimax_m3/routing.py) now declines what the targets do not build or run, so modeling_v2="auto" falls back to the built-in model and "require" names the missed criterion: * Eagle3 drafters other than the targets' one-layer drafter (read from the drafter checkpoint's config.json). * Runtimes other than MSA with an FP8 indexer and an FP8 or NVFP4 main KV cache. The resolver hands routing modules that set ROUTES_ON_RUNTIME a RuntimeFacts (sparse attention config and the KV cache quantization the engine will run with); only MiniMax-M3 sets it. _validate_runtime stays as the backstop. Adds a whole-model accuracy test under modeling_v2="require" (GSM8K without speculation, and with one-model Eagle3 plus a chat-GSM8K acceptance probe), on the 4-GPU GB300 list (TP4) and the 2-node GB300 list (TP8). Bitwise identical to the previous targets on GB300 (tokens and per-step outputs on every rank, no speculation and Eagle3, fused and unfused projections, TP4 and TP8, including pure generation steps wider than the decode kernels' 48 tokens). Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
Resolve cache_manager.py: keep the decode shortcuts and pooled views, and the HND main KV storage with the strided NHD view from NVIDIA#20159. Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
|
/bot run --disable-fail-fast |
…ls in M3 folders
Review follow-up, no logic change:
- cpp/tensorrt_llm/kernels/stepInputsScatter.{cu,h} move to kernels/minimaxM3/.
- The MiniMax-M3 torch ops (thop/minimaxM3*Op.cpp and stepInputsScatterOp.cpp) move to thop/minimaxM3/, which adds
them to th_common_attention with target_sources() (as thop/moe/ does for th_common_moe).
- The fake impls of the ops the decode targets add (step_inputs_scatter, minimax_m3_mxfp8_low_m_gemm,
minimax_m3_router_gemv / _gemv_fp4 / _tc, minimax_m3_producer_pw, minimax_m3_mc_allreduce_norm) move from
custom_ops/cpp_custom_ops.py to modeling_v2/models/minimax_m3/fake_ops.py, imported by the MiniMax-M3 modeling_v2
package, by pyexecutor/step_inputs.py and by the fake-registration test.
Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
…ough runtime_hooks_of The autotuner and CUDA-graph warmup passes read merge_tuned_tactics_across_ranks from the runner's cached hooks, which only the constructor sets; engines built another way (main's warmup tests drive DecoderRunner._run_autotuner_warmup on a stub) failed with an AttributeError. They now read runtime_hooks_of(self.model), the all-off default for a model that declares no hooks, the same value the constructor caches. Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
… HND storage layout Since NVIDIA#20159 the dense KV storage is head-major and get_buffers hands a token-major request a transposed view. The reused pool views (reuse_pool_views) cached the untransposed view and transposed it on every call, so a reused view was a new tensor each time. The transpose is now part of the view's cache key and applied before caching. The runtime-policy tests expect the view in the manager's own layout over its physical one. Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
Resolve runner.py after NVIDIA#20134 split DecoderRunner by phase: the model's runtime hooks now reach the phase that uses them. The runner keeps the step scopes, the graph-runner config and the greedy-token logits; the input preparer the H2D skip copies, the step-input pack and the speculative step fold; the metadata the MSA policy and the greedy RNG upload skip; the graphs the short/long split and the post-capture tactic merge; the warmup the post-autotune tactic merge (through runtime_hooks_of). Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
cd5f671 to
5d24e81
Compare
|
/bot run --disable-fail-fast |
|
PR_Github #79103 [ run ] triggered by Bot. Commit: |
…ares is_spec_decode DecoderRunner reads DecoderRunnerConfig.is_spec_decode at construction (the spec step fold binds the engine buffers). The compiled-body validation test builds the runner from a SimpleNamespace config; give it the field the real config always has. Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
Resolve the modeling_v2 router against NVIDIA#20267: route() now takes the resolver's KV-cache algorithm as its fifth argument for every routing module, and modules that set ROUTES_ON_RUNTIME (MiniMax-M3) still receive runtime=RuntimeFacts; the MiniMax-M3 route() accepts the new argument. Keep both sides' new helpers (RuntimeFacts / resolved_mode and tp_reaches_across_nodes) and both test-list additions. Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
|
PR_Github #79103 [ run ] completed with state
|
5d24e81 to
4d16da2
Compare
|
/bot run --disable-fail-fast |
Resolve the MiniMax-M3 index-score kernels against NVIDIA#19275 (c146094), which split cute_dsl_kernels/ into the domain kernel trees: the TP4 and TP8 m3_index_score imports now take cute_ptx_utils and utils from tensorrt_llm._torch.kernels.blackwell, and the docstring points at attention/kernels/blackwell/minimax_m3_index_decode_score.py. Both moved modules are unchanged (pure renames). Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
… (sm_107) TP2 / TP4 / TP8 in modeling_v2 Three modeling_v2 targets for the MiniMax-M3 NVFP4 checkpoint on Rubin (sm_107), minimax_m3_nvfp4__sm_107__tp2, __tp4 and __tp8, in the flat layout of the sm_103 targets: one modeling.py per target, a weights.py weight table and an ops/ catalog entry with certified cells and a test for every kernel the model calls. Routing picks a target only for the matching checkpoint, SM and width. sm_107 enablement: Arch.SM_107 in the catalog and the (10, 7) routing entries; the compiled trtllm::minimax_m3_sm107* kernels (built for sm_107 only; torch ops in thop/minimaxM3/, fake impls in the M3 fake_ops module) and the shared TF32 router GEMV and spec-tail draft-input kernels; the CuTe DSL decode MoE engines, MX GEMV and fused embedding + first RMSNorm; the decode runtime pieces (step-input pack, graph runner hooks, step fold, MSA decode metadata reuse, the SM107 NVFP4 -> FP8 KV fallback skipping MiniMax-M3 MSA, late-compile detection), each inert until a target's runtime hooks turn it on; the FMHA fallback serving SM107 paged prefill at the TRT-LLM Gen extra page sizes (the 128-token KV pages its MiniMax-M3 caches opt into); and the scoped PrimTS work-tile rebind in the vendored FlashInfer patch. The TP2 target serves decode steps of 1..192 rows: context steps of 9..48 rows route on the tensor-core router, steps with context rows run under breakable prefill CUDA graphs (the drafter's attention in an eager break) and take the MC finalize boundary at 9..48 rows, and decode steps of >= 33 requests take the stock MSA merge path (MergeMxfp8.MAX_ROWS 128; no canon profile exceeds 96 rows). Runtime items: pinned FlashInfer CuTe DSL MXFP8 tactics above 32 rows on SM107, for models whose runtime hooks opt in (the MiniMax-M3 sm_107 targets do); the AllReduce tuner leaves NCCL_SYMMETRIC out of its candidates only when TRTLLM_AR_TUNE_NCCL_SYMMETRIC=0 (opt-in; the default 1 keeps the old candidates); PyExecutor emits a request's first content after two iterations when the model's runtime hooks ask for it (the MiniMax-M3 sm_107 targets do; other models keep the stock schedule). No lossy steps: every kernel keeps the stock op's precision. The TP4 and TP8 targets produce bitwise the same tokens and per-step outputs as the pre-restructure sm_107 targets, with and without Eagle3. Two known deltas against the previous wide TP2 target (the 9..128-row cells this commit folds in), both measured with greedy sampling: under Eagle3, at 12 or more concurrent requests the drafter's BF16 linears past its low-M tactic table can resolve a near-tie differently from the in-tree drafter, so some draft tokens differ, while the target's tokens are identical and its logits bitwise; without speculation (not a production configuration), decode steps of 9..48 rows are bitwise on the first such step and differ after it (cause not isolated), with identical output tokens in every comparison. Steps of 1..8 rows are bitwise to the previous TP2 target. Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
4d16da2 to
7333ac8
Compare
|
/bot run --disable-fail-fast |
|
PR_Github #79291 [ run ] triggered by Bot. Commit: |
|
PR_Github #79291 [ run ] completed with state
|
|
@farazkh80, could you help to review this PR as trt-llm-torch-attention-devs? Thanks~ Assignment explanation
Affinity and workload are snapshots at selection, not live counters. Existing reservations on this PR are counted once. |
…efault device The import-time probe built its tensors without a device. When the module is first imported while a model is built under the meta device, the probe tensors are meta and torch.equal raises. Pin them to the CPU, as the comment already states. Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
|
/bot run --disable-fail-fast |
| @@ -0,0 +1,3 @@ | |||
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |||
| # SPDX-License-Identifier: Apache-2.0 | |||
| """MiniMax-M3 NVFP4 / sm_103 / tp4.""" | |||
There was a problem hiding this comment.
@peihu-nv , one thing I want to clarify is that, we dont require ONE modeling folder to support just ONE EXACT parallel config, it can be TP general if most of the code are shared between different TP sizes.
See this example https://github.com/NVIDIA/TensorRT-LLM/pull/20267/changes
There is no strict threshold on what percentages of differences leads to specialization, you can use your best judgement.
But my normal mental model is that TP 2/4/8 may not be that different in terms of kernels/fusion patterns?
You can decide, if this TP2/4/8 specialization is what you intend, its also fine, just leave a msg.
|
PR_Github #79695 [ run ] completed with state
|
| self._gsm8k(eagle3=True) | ||
|
|
||
|
|
||
| class TestModelingV2MiniMaxM3Nvfp4Sm107Tp2(_MiniMaxM3Target): |
There was a problem hiding this comment.
These SM107 accuracy cases are not registered in a matching CI stage; the added GPU entries exercise SM103.
farazkh80
left a comment
There was a problem hiding this comment.
Approved from attention perspective, with a couple questions (non-blocking).
| { | ||
| return; | ||
| } | ||
| if (isContextKernel(runnerParams.mKernelType)) |
There was a problem hiding this comment.
The new context warmup grid runs on every arch, but on SM100/SM103 no context kernel goes through NVRTC, so it only adds startup time there. Should we gate it on SM107?
| # MiniMax-M3 MSA keeps NVFP4 only on its sparse layers, which MSA and | ||
| # Triton serve; its dense and draft layers stay FP8 for trtllm-gen. | ||
| sparse_config = model_config.sparse_attention_config | ||
| uses_minimax_m3_msa = (isinstance(sparse_config, |
There was a problem hiding this comment.
With modeling_v2 off by default, this also moves stock MiniMax-M3 on Rubin to NVFP4 sparse KV. Was that path checked on Rubin, or only the sm_107 targets?
@coderabbitai summary
Description
Depends on #20306 (flat staircase layout of the sm_103 targets). This PR adds only the last commit; please review that commit.
This PR brings the MiniMax-M3 NVFP4 decode targets to Rubin (sm_107):
minimax_m3_nvfp4__sm_107__tp2,__tp4and__tp8. They follow the flat layout of the sm_103 targets: onemodeling.pyper target, aweights.pyweight table, and anops/catalog entry with certified cells and a test for every kernel the model calls. The router picks a target only for the matching checkpoint, SM and width, andmodeling_v2: requiremakes it mandatory.Performance
On Rubin (VR200), MiniMax-M3 NVFP4 AgentX with Eagle3, against the stock decode path (
modeling_v2off):sm_107 enablement
Arch.SM_107, and routing entries for (10, 7) at 2, 4 and 8 ranks, with the same drafter and runtime declines as sm_103.minimaxM3Sm107Tp{2,4,8}with theirtrtllm::minimax_m3_sm107*thop ops, plus two shared GEN kernels: the TF32 tensor-core router GEMV and the spec-tail draft-input step.RuntimeHooksturns it on, so other models are unchanged.WorkTileInforebind to its own compiles, so other CuTe DSL kernels (e.g. FA4) are unaffected.RuntimeHooks; the MiniMax-M3 sm_107 targets do, and other models are unchanged. The AllReduce tuner leavesNCCL_SYMMETRICout of its candidates only withTRTLLM_AR_TUNE_NCCL_SYMMETRIC=0(opt-in; the default keeps the old candidates).The targets
All three share one structure; they differ in rank geometry and row ranges.
m3_sparse_decode_lcm3_sparse_decode_lcm3_sparse_decode_lc8TRTLLM_MODELING_V2_VALUE_DUMP), as in the sm_103 targets. Every fast path is on by default and has one module constant that turns it off. Capacity bounds carry a# capacity:annotation.Aggregated (wide) decode at TP2
Numerics. No lossy steps. Most kernels are bitwise equal to the stock op they replace; the rest reduce in a fixed order, are deterministic in eager and graph mode, and stay within the stock kernel's error band. One known divergence: the TP4 drafter all-reduce + RMSNorm always sums like ONESHOT, so under a forced NCCL strategy its rounding differs from stock NCCL. Against the earlier wide TP2 reference, target tokens are identical: at 12+ concurrent requests under Eagle3 some draft tokens can resolve near-ties differently (target logits bitwise), and without speculation (not a production configuration) decode steps of 9..48 rows are bitwise on the first such step and differ after it, with identical output tokens. Steps of 1..8 rows are bitwise for prompts up to about 12K tokens, and at TP8 also for a single 83K-token request (prefill and decode).
Not in this PR: batch 4/8 decode at TP4 / TP8 (follow-up). The prefill (context) optimizations are in #20629.
Known behaviours: The pinned SM107 MXFP8 GEMM tactics are armed process-wide by an executor whose model opts in, so another model in the same process at the same GEMM shape also uses them; its outputs are bitwise unchanged (every pinned tactic equals the default), and the LLM API runs one executor per worker process.
Test Coverage
new_tokensin Eagle3 context steps. Re-run on the final commit at TP2 and TP4 (no speculation and Eagle3): bitwise at every step of 1..8 rows; at TP2 the first difference is a 45-row context step, which this PR moves to the wide cells.executor/,speculative/, attention): 255 pass, 1 skipped.PR Checklist
Please review the following before submitting your PR:
PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.
PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.
Test cases are provided for new code paths (see test instructions)
If PR introduces API changes, an appropriate PR label is added - either
api-compatibleorapi-breaking. Forapi-breaking, includeBREAKINGin the PR title.Any new dependencies have been scanned for license and vulnerabilities
CODEOWNERS updated if ownership changes
Documentation updated as needed
Update tava architecture diagram if there is a significant design change in PR.
The reviewers assigned automatically/manually are appropriate for the PR.
Please check this after reviewing the above items as appropriate for this PR.
GitHub Bot Help
To see a list of available CI bot commands, please comment
/bot help.