Skip to content

[None][feat] Add Rubin (sm_107) MiniMax-M3.1 low-latency decode targets - #21038

Draft
peihu-nv wants to merge 38 commits into
NVIDIA:mainfrom
peihu-nv:peihengh/m31-vr-decode-pr-v5
Draft

peihu-nv wants to merge 38 commits into
NVIDIA:mainfrom
peihu-nv:peihengh/m31-vr-decode-pr-v5

Conversation

@peihu-nv

@peihu-nv peihu-nv commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

@coderabbitai summary

Description

Depends on #20917 (GB300 MiniMax-M3.1 decode targets). This PR builds on its shared DSpark and runtime-hook integration; until #20917 merges, the diff against feat/minimax-m3.1-flash also shows its commit. The context (prefill) target for Rubin is a separate PR stacked on this one.

MiniMax-M3.1 NVFP4 decode on Rubin (sm_107) currently falls back to the built-in model. This PR adds three modeling_v2 targets, routed on sm_107 by TP width:

Target Topology What it serves
minimax_m31_nvfp4__sm_107__tp8 TP8, experts TP8 low-latency K7 decode
minimax_m31_nvfp4__sm_107__tp4 TP4, experts TP4 low-latency K7 decode
minimax_m31_nvfp4__sm_107__tp2 TP2, experts TP2 decode baseline (aggregated serving and disaggregated generation)

Each target follows the layout of the GB300 targets in #20917:

  • per-TP staircase targets;
  • runtime hooks that stay inert unless a target arms them;
  • every item default ON, with a TRTLLM_M31_DISABLE_<ITEM>=1 kill switch (m31_switches); items that are still opt-in use TRTLLM_M31_ENABLE_<ITEM>=1;
  • compiled ops under cpp/tensorrt_llm/kernels/minimaxM3Sm107Tp{2,4,8} and thop, built only for sm_10x, with fake (meta) registrations;
  • bundled tactic pins (m31_tuning/) and ahead-of-time CuTe DSL objects (cute_aot_data/) shipped as package data, each guarded by the native build / toolchain it was produced with.

It also restores two DecoderRunner.__init__ defaults that #20917 dropped (guided_decoder and lora_model_config, both None); without them every server start fails in warmup with an AttributeError.

Shared changes armed by the sm_107 targets

These are inert unless a target arms them through RuntimeHooks (or an explicit config option):

  • runtime hooks: step-input pack and prologue op, draft block-offset patching, fused spec offsets, first-content emission, one-event iteration timing, a late-compile detector, the MXFP8 pinned-tactic table, log-only work counters;
  • DFlash / DSpark drafter APIs (fc TP split, fused all-reduce + RMSNorm, windowed-decode cap, full-slot context arena) and a parallel DSpark context advance;
  • MSA backend forward_staging, MSA KV-cache fixes (pool zero-init, exact KV-size estimate), standalone-drafter KV pool fixes, saturating FP8 casts;
  • disaggregated serving: the DSpark drafter's context window travels with the target KV (DisaggregatedParams.draft_ctx_state);
  • indexer_kv_dtype='nvfp4' (prototype): NVFP4 index-K pages for the MSA implementation;
  • native ops: TOPP_DET v3 list-path / wide-histogram variants (schema arguments default off), a slot-indexed chain speculative sampling variant, REJ v3 selected only on sm_107, a cluster-16 router GEMV, an FC-shard GEMM;
  • requirements.txt pins nvidia-cutlass-dsl[cu13]==4.8.0 (the release, same as upstream main) instead of 4.8.0.dev0.

Shared paths taken only by the sm_107 targets

These shared paths are off by default for every model and GPU. Routing arms them, process-wide, when it selects an sm_107 MiniMax-M3.1 target, before the model is built (arm_sm107_shared_opt_ins). Each keeps its kill switch, and the same paths are available to other models as RuntimeHooks opt-ins.

Path Kill switch
KV GPU prefix prefetch (py_executor): waiting context requests have their host-tier prefix staged to the GPU and are admitted after a short rank-agreed hold (KV cache manager V2 + block reuse). TRTLLM_M31_DISABLE_KV_GPU_PREFETCH=1
DFlash / DSpark draft KV reserve + guard page (kv_cache_manager_v2): the draft KV manager reserves the drafter's block width per step and keeps page 0 as a guard, so drafter writes through unallocated block-table entries cannot clobber another request's page. TRTLLM_M31_DISABLE_DRAFT_KV_RESERVE=1, TRTLLM_M31_DISABLE_DRAFT_KV_GUARD=1
Context-only prompt tokens as an int32 array (llm_request; host arrays only). TRTLLM_M31_DISABLE_L1_TOKEN_ARRAY=1
MSA NVFP4 sparse prefill BF16X / E4M3X fork (msa_prefill), when the fmha_sm100 fork is installed and the geometry has 16 query heads per KV head. TRTLLM_M31_DISABLE_TP2CTX_BF16X=1
MSA indexer proxy max-score buffer reuse across sparse layers (msa_indexer). TRTLLM_M31_DISABLE_PROXY_MS_REUSE=1
Context one-hot draft probabilities via index_fill_ (speculative/interface; same elements, no host sync). TRTLLM_M31_DISABLE_ONEHOT_INDEX_FILL=1
DFlash / DSpark host-side context allocation when the draft block table counts full rows (dflash). TRTLLM_DFLASH_DISABLE_HOST_CTX_ALLOC=1
Shipped ahead-of-time CuTe DSL objects (cute_aot): an unarmed process compiles every kernel in-process with no lookup or export. TRTLLM_M31_CUTE_AOT_DISABLE=1

toppRenormDetV3Sm103's TOPP_V6 chain-prefetch summation runs only on sm_107 devices; GB300 callers of the #20917 targets keep the staged loop (TRTLLM_M31_DISABLE_TOPP_V6=1 turns it off on sm_107 too).

Other shared behaviour changes (no kill switch; correctness fixes and validation):

  • DFlash / DSpark context one-hot draft probabilities clear the full draft-probability width, not the draft logits' width, so a vocab-sharded drafter cannot leave stale columns for new requests.
  • The one-model speculative sampler rejects requests with a logits_processor (it was silently never called), and the disaggregated peer check rejects replicated roles whose per-layer cache sizes differ (e.g. different indexer_kv_dtype).
  • The scheduler reads a request's prompt length from num_input_tokens when present (same value, no token-list build).

Items and numerics

Unless noted, an item is exact: bitwise against its stock path in eager and graph mode, gated by kernel oracles. These items are not bitwise: same inputs and rounding points, different fp32 reduction order:

Item What changes
VR_GEMV48 / LINEAR_CUBLASLT 33..64-row MXFP8 projections on the same quantized activation
S9B drafter all-reduce fused with add + RMSNorm
MXDRAFT drafter MXFP8 GEMMs; changes only the draft, never the accepted-token rule (ON at TP8 and TP2, OFF at TP4)
LC_ONEDIV (TP4) up to 16 query rows, the long-context sparse-decode split-K merge divides once per output (fixed order, no atomics; within the float64 reference's error band)

S2 (drafter fc row split), S5 (window-capped drafter decode), MCAG, S9B_MC, REJ_SLOTS and SLOT_IDS_CHANGED are compiled in but default OFF.

Measured results (VR200, end to end)

AgentX-style multi-turn agentic workload (SemiAnalysis InferenceX client, agentx-harness 1.0.12 preset), K7 DSpark speculation, sampled (T 1, top-p 0.95, rejection sampling), 3600 s per cell. Acceptance: synthetic (forced) K7 AL 4.04; natural AL not demonstrated. (The acceptance counters read 4.039 on every generation endpoint, which reflects the forcing.) Y = output-inclusive total token throughput per GPU; X = per-user generation tokens/s.

Cell Topology Y per GPU X median X P90 TTFT p50
agg c20 1x TP2 (2 GPUs) 76.87K 535.8 479.3 0.17 s
agg c32 1x TP2 128.38K 410.4 282.0 0.25 s
agg c56 1x TP2 212.97K 262.2 200.9 0.23 s
agg c68 1x TP2 243.10K 209.4 163.6 0.34 s
disagg c16 ctx 1x TP2 + gen 1x TP8 (batch 1) 11.86K 1310.6 1227.2 0.60 s
disagg c24 ctx 1x TP2 + gen 2x TP4 (batch 1) 19.64K 1024.7 980.6 0.36 s
disagg c64 ctx 1x TP2 + gen 4x TP2 (batch 2) 50.72K 658.3 611.5 1.34 s
  • The aggregated cells ran a build whose decode code differs from this commit only by the fake (meta) registrations of the native SwiGLU-OAI / MoE-finalize ops and their unit tests. The disaggregated cells ran this decode code exactly, with the context role served by the stacked prefill PR's target.
  • Two files in this commit are formatting-only changes (identical AST) relative to the measured build: catalog/spec_decode/dspark_ctx_advance.py and tests/unittest/_torch/thop/parallel/test_minimax_m31_dspark_glue_ops.py.
  • The disaggregated TP8 / TP4 generation cells run one request per generation server by configuration (latency cells).
  • Native builds are not byte-reproducible; the measured images are the builds the tactic pins were recorded on (the pins carry a native-build guard).

Disclosures and known gaps

  • The disaggregated c24 cell had 3 failed requests out of 4,049. Two come from the generation server's first verify after KV receive using stale draft probabilities (fixed in the follow-up commit "Disaggregated generation: one-hot draft probs on the first verify under rejection sampling" on this PR; the measured cells ran without it); each was followed by repeated stock post-processing errors on the same request. The third is a client transport error.
  • One DSpark drafter-input kernel is loaded from disk on first use rather than at warmup (a fix is parked).
  • The measured runs pre-seeded Triton / FlashInfer / Inductor JIT caches from a named seed; without it these compile at startup.
  • TP4 / TP8 generation: the dense SwiGLU above 8 rows runs the Triton kernel (the native SwiGLU-OAI / MoE-finalize ops are TP2-only); the TP4 / TP8 cells above ran batch 1.
  • The context-phase CTXNORMQ boundary is not taken on sm_107 (the op from [None][perf] Add GB300 MiniMax-M3.1 low-latency decode targets #20917 is built for SM103 only).
  • With max_seq_len 1,048,576, set max_batch_size explicitly: at the default (2048) the MSA backend's worst-case plan overflows its int32 KV offsets at startup. A fix (int64 offsets or a clamp) follows.
  • The sm_107 context target loads no bundled tactic pins yet, so it autotunes at startup.
  • Drafter determinism above 8 concurrent requests relies on the draft KV reserve + guard above.
  • MSA vendoring and the optional flashinfer-cubin dependency for Rubin are in the stacked prefill PR.

Updates on this PR after the first commit

Landed:

  • Disaggregated generation first-verify fix: a request received from the context server enters with dummy draft tokens and a one-hot draft-probability row under rejection sampling, so its first verify cannot read a stale or uninitialized row (CPU unit test test_disagg_gen_first_verify_onehot.py).
  • TOPP_V6 limited to sm_107 devices, and the LlmRequest token-array fast path limited to host arrays (a device array takes the sequence cast, which rejects it, as before the fast path).
  • The shared paths above armed only for the sm_107 targets, from routing, so the target directories (the shipped AOT objects' source digests) are unchanged.
  • DSpark drafter-inputs kernel: no Triton specialization on the batch-dependent accepted-token stride and view alignment, so a batch shape warmup never ran no longer compiles after the server is ready. No kill switch: it is a compile-specialization hint with no alternate path (bitwise unchanged).
  • The iteration-stats RPC is awaited instead of blocking the event loop (TRTLLM_M31_DISABLE_A4_ASYNC_STATS=1 restores the blocking call).
  • Two diagnostics, off unless their environment variable is set: a phase-armed Nsight capture window (TRTLLM_M31_PHASE_NSYS_ARM) and a per-rank host-path probe (TRTLLM_M31_HOST_PATH_PROBE).
  • TP2 sequence-parallel router: each rank computes its owned half of the rows with a native TF32 GEMM (trtllm::m31_router_half_pin) at the full-step row counts qualified bitwise against the full-row reference; other layouts keep the full-row path (TRTLLM_M31_DISABLE_ROUTER_HALF_PIN=1). The measured effect is small (about 0.02-0.03% of mixed-step time; no decode-step change).
  • The executor's iteration timing uses native GPU timestamps on the sm_107 targets instead of timed CUDA events (TRTLLM_M31_DISABLE_GPU_TIMESTAMPS=1).
  • The GPU byte oracles for the router pin and the drafter-inputs kernel need an sm_107 GPU and run locally; their CPU counterparts are in the CPU test list.
  • Validated on VR200 on a rebuilt image: native, speculative, top-p (1,002 sampling oracles, bitwise), opt-in, context-target and disaggregated unit-test suites, and a serve smoke of the aggregated and context roles with engagement, tactic pins and ahead-of-time objects unchanged from the measured build.

Pending:

  • Bundled tactic pins for the TP4 / TP8 / TP2 disaggregated generation roles.
  • The MSA int32 KV-offset fix above.

Test Coverage

  • tests/unittest/_torch/modeling_v2/models/minimax_m3/minimax_m31_nvfp4__sm_107__tp{2,4,8}/: kernel oracles (bitwise or reduction-order), dispatch, kill switches, routing, prebuild and work-counter tests.
  • tests/unittest/_torch/thop/parallel/ native-op tests (incl. fake registrations), tests/unittest/_torch/multi_gpu/test_minimax_m31_{f3_ar_prepushed,s2_moe_tail}.py (bitwise to the stock all-reduce chains).
  • CPU tests added to l0_cpu.yml: test_m31_pins.py, test_cute_aot.py, test_flashinfer_autotune_cache.py; the two TP2 multi-GPU tests are listed in l0_dgx_b200.yml.
  • tests/unittest/disaggregated/: draft-KV transfer, rank-info, hook-off and switch tests.
  • On VR200 at the measured build: the native-op and speculative-decoding unit-test suites passed, and a serve smoke test passed for the aggregated, context and generation roles (pins loaded on every rank, no late compiles after ready).

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-compatible or api-breaking. For api-breaking, include BREAKING in 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.

pranav-nvidia and others added 22 commits October 22, 2026 18:03
MXFP8 non-expert projections with NVFP4 routed experts on the existing model:
model_config expands the packaging's expert recipe into the mixed per-layer
config, the weight mapper renames the packed expert tensors and inverts their
global scales, MXFP8 index projections and fused MXFP8 shards load their own
block-scale rows, the sparse cache manager takes the layer schedule from the
checkpoint and counts index-K bytes, the router GEMM runs in FP32 cuBLAS, and
the VL input processor loads the HF processor only for image requests.

Signed-off-by: Pranav Shrestha <pshrestha@nvidia.com>
MiniMaxM3Gate keeps a BF16 split of its FP32 router weight (three terms
along K, built at load_weights, the multi-term emulation of CUTLASS's 3xTF32
GEMMs). BF16 inputs run one cuBLAS BF16 GEMM with FP32 accumulation over the
replicated input instead of an FP32 cast plus a SIMT SGEMM; FP32 inputs keep
the FP32 cuBLAS GEMM. Top-k parity against an FP64 reference is tested.

Signed-off-by: Pranav Shrestha <pshrestha@nvidia.com>
KV pool and block-scale views are built once per layer and layout, the fused
QKV/index producer caches its per-layer-invariant arguments, block-id rows are
copied from the page-index buffers, the MSA metadata builders do their
batch-sized index arithmetic in numpy, and the NVFP4 decode and CSR prefill
wrappers resolve the device capability and the fmha_sm100 API once. Same
kernels, same arguments; adds prefix-reuse and BF16-cache decode tests.

Signed-off-by: Pranav Shrestha <pshrestha@nvidia.com>
…SM107

The MiniMax-M3 Triton NVFP4 sparse decode kernels run on SM107, so keep
NVFP4 KV for that path instead of falling back to FP8 and include SM107
in the decode capability list and test guards.

Signed-off-by: Pranav Shrestha <pshrestha@nvidia.com>
…deo tokens per patch

trtllm-serve and the multimodal prompt builders mark their rendered TextPrompt
with chat_template_applied; the input processor uses it verbatim instead of
wrapping it in a second user turn, normalises a framed marker, and rejects a
marker/media count mismatch before the HF processor runs. Media arrive as
uint8 numpy, and video tokens count one start/end bracket per temporal patch.

Signed-off-by: Pranav Shrestha <pshrestha@nvidia.com>
…king on multimodal requests

Forward the pre-fusion token ids as orig_input_ids so the spec worker
(DSpark/DFlash, MTP, Eagle) sees them after fuse_input_embeds drops
input_ids, and slice the vision embeddings with find_input_mm_embeds so a
cached prefix or a chunk boundary inside a placeholder span no longer
trips the token-count check.

Signed-off-by: Pranav Shrestha <pshrestha@nvidia.com>
The input processor implements the token-ID expansion hooks but never
set the opt-in flag, so a prompt_token_ids + image request was
detokenized and re-templated, doubling the image marker and failing the
marker count check. Set the flag and test the dispatch end to end.

Signed-off-by: Pranav Shrestha <pshrestha@nvidia.com>
The drafter's nested HF config is flattened, its checkpoint tensors renamed,
MXFP8 linears dequantized and Gemma norm weights folded at load. Its KV pool
uses its own page size, is funded for its attention window plus the chunk in
flight, and the unpaired draft mirror is sized from the target request and
keeps the chunk start as history; the block decode reads a rebased table.

Signed-off-by: Pranav Shrestha <pshrestha@nvidia.com>
… generation-init requests

A generation worker receives the prompt KV instead of prefilling it and
starts the drafter on a fresh slot, so the draft mirror never holds K/V
for the transferred prompt. Treat that prompt as history on gen-init so
the drafter's window trims the mirror to its tail plus headroom, instead
of asking for a prompt-sized chunk that exceeds the pool funding.

Signed-off-by: Pranav Shrestha <pshrestha@nvidia.com>
…ligned reused prefix

On an unpaired draft pool the block decode reads a view rebased by the rounded-up base of a reused prefix.
While the stored tail is shorter than that base, entry 0 of the view lies past the request's allocation and resolves to page 0, so the block's own K/V landed in another request's page and the drafter stayed blind until the context caught up.
The view is now shifted only to the page holding the context length on those steps; the shortened read length is unchanged, so nothing below the base is read.
Adds a CPU test that walks every context length of that window.

Signed-off-by: Pranav Shrestha <pshrestha@nvidia.com>
…sing in step

Resolve the parser's mode from thinking_mode or the prompt-resolved relay instead
of the bare thinking flag, so a prefilled <mm:think> block is read as reasoning.
Report truncated reasoning as reasoning in the non-streaming parse and make the
streaming parser emit the same split: unopened </mm:think> is a delimiter, text
before an opening tag is content, partial tags are withheld at chunk boundaries.

Signed-off-by: Pranav Shrestha <pshrestha@nvidia.com>
The deployment guide gains a linkable "Serving MiniMax-M3.1-Flash" section
(bundled DSpark drafter, KV cache reuse, NVFP4 KV cache on SM107, thinking_mode
and reasoning parsing) and states that both MoE backends load the NVFP4 expert
packaging with the same static recipe. The supported-models matrices and the
sparse-attention page now say block reuse is supported and mixed MXFP8/NVFP4 loads natively.

Signed-off-by: Pranav Shrestha <pshrestha@nvidia.com>
…okenization cache

`TransformersTokenizer` delegates `num_special_tokens_to_add` to the wrapped
tokenizer (the base class raises), so the MiniMax-M3 input processor's
tokenization-cache check runs. `resolve_hf_chat_template` falls through to the
tokenizer's template when the processor's `chat_template` is None.

Signed-off-by: Pranav Shrestha <pshrestha@nvidia.com>
….4.0a1

The side-branch release job derives its wheel and container version from tensorrt_llm/version.py, so the branch gets its own alpha base instead of sharing the main nightlies' 1.4.0rc1 line.

Signed-off-by: Pranav Shrestha <pshrestha@nvidia.com>
…#20564)

Signed-off-by: Yanchao Lu <yanchaol@nvidia.com>
(cherry picked from commit ee7c59a)
Signed-off-by: Yanchao Lu <yanchaol@nvidia.com>
Use the 1.4.0a0 base required by the side-branch release convention and keep the README badge and example dependency constraint aligned.

Signed-off-by: Yanchao Lu <yanchaol@nvidia.com>
Signed-off-by: Tyler Burt <195370667+tburt-nv@users.noreply.github.com>
(cherry picked from commit c962edd)
Signed-off-by: Yanchao Lu <yanchaol@nvidia.com>
Add SM103 TP8 single-request decode through eight rows and TP4 decode through 32 rows, with shared native kernels and guarded DSpark runtime integration.

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
Add the MiniMax-M3.1 NVFP4 decode targets for Rubin (sm_107) at TP2, TP4
and TP8, stacked on the GB300 low-latency decode targets.

- Decode megakernel paths for sm_107: fused attention / MoE engines,
  multicast all-reduce + norm, the F3 prepushed all-reduce, the S2 MoE
  tail, DSpark drafting with C++ glue ops, and deterministic top-p.
- Native SwiGLU-OAI and MoE finalize ops for the TP2 target, bit for bit
  the Triton kernels they replace.
- CuTe DSL kernels loaded from ahead-of-time objects shipped in the
  wheel, keyed on the toolchain, kernel sources and model switches.
- Bundled tactic pins (name-keyed, plus index-keyed entries under a
  native-build guard), also served under TRTLLM_CACHE_DIR.
- Disaggregated serving: the DSpark draft context window transfers with
  the KV cache.
- LlmRequest takes context-only prompt tokens as an int32 array.
- DFlash rejection sampling writes full-width context one-hot draft
  probabilities.
- nvidia-cutlass-dsl is pinned to the 4.8.0 release.

Each optimization is on by default with a TRTLLM_M31_DISABLE_<ID> kill
switch.

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
…t verify under rejection sampling

A generation-only request received from the context server entered with dummy (or context-server) draft tokens, so the
one-hot placeholder row was never requested (py_needs_onehot_draft_probs is set only for requests without draft
tokens) and its first rejection-sampling verify read the slot's stale or uninitialized draft-probability row. A stale
row accepts the dummy tokens; an uninitialized one can make chain_speculative_sampling emit an out-of-range token id.

disagg_gen_first_draft_tokens (speculative/utils.py) now picks the first draft tokens at receive: under rejection
sampling always dummy zeros with the one-hot row requested (no proposal row exists on this server); otherwise the
previous behaviour. CPU UT in speculative/hw_agnostic.

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
…ost arrays

- toppRenormDetV3Sm103: the TOPP_V6 chain-prefetch summation now runs only on
  sm_107 devices (the same per-device compute-capability check as REJ v3);
  other devices, including GB300 callers, run the staged loop as before.
  TRTLLM_M31_DISABLE_TOPP_V6 still turns it off everywhere. Each kernel
  variant sets its own shared-memory attribute.
- LlmRequest binding: the int32 token-array fast path accepts host arrays
  only (nb::device::cpu); a device array takes the element-wise cast instead
  of a memcpy from a device pointer. Unit test with a CUDA tensor.

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
…gets that opt in

Shared paths this PR added were on for every model that reached them. Each
is now a model opt-in that only the sm_107 MiniMax-M3.1 targets arm, and
keeps its kill switch:

- RuntimeHooks fields (default False): draft_kv_reserve_guard (armed by the
  KV-cache creator before sizing), kv_gpu_prefetch, context_token_array,
  msa_prefill_fork, proxy_ms_reuse, context_onehot_index_fill and
  dflash_host_ctx_alloc (armed by the executor before warmup).
- Ahead-of-time CuTe DSL objects are served only after routing selects an
  sm_107 target (cute_aot.arm, before the model builds its kernels); an
  unarmed process compiles with no lookup, export or log line.
- The TP2 / TP4 / TP8 targets arm all of them, so their behaviour is
  unchanged.

Unit tests: the opt-in and kill-switch semantics, the unarmed AOT path, and
a source check that every sm_107 target arms each shared opt-in.

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
A CUDA int32 tensor no longer takes the host-array copy; it takes the
sequence cast, which rejects it, as before the fast path existed. The test
asserts that rejection instead of a read through the element-wise cast.

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
…not opt in

The startup line reads "disabled (kill switch)" only when the kill switch
is set; a model that does not opt in reads "inactive (the model does not
opt in)". The parametrized state test covers both.

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
The shared opt-ins are now armed by routing, process-wide, when it selects
an sm_107 target (arm_sm107_shared_opt_ins, before the model is built),
instead of through the targets' RuntimeHooks. The target directories are
the source digest of their shipped ahead-of-time CuTe DSL objects, so
leaving them unedited keeps every shipped object valid. The RuntimeHooks
fields stay as the opt-in for other models. Unit test: routing arms every
opt-in before returning an sm_107 target, and the helper sets every flag.

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
The DSpark drafter-inputs Triton kernel no longer specializes on the
accepted-token buffer's row stride or the alignment of its post-context
view. Both depend on the step's batch composition, so a specialization
compiled a new variant at a batch shape warmup never ran (a late compile
after the server was ready). Both feed scalar per-program loads only, so
not specializing them costs nothing and the outputs are bitwise unchanged.

No kill switch: this is a compile-specialization hint with no alternate
path; turning it off would only reintroduce the late compile.

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
…nt loop

GenerationExecutorProxy.aget_stats and GenerationExecutorRpcProxy.aget_stats
fetched stats with a blocking .remote() that waits up to `timeout` for a
record. trtllm-serve calls them on the event loop that also sends every
response and token, so whenever no record is ready the server stalls.
They now issue the same RPC through remote_future() and await it, then
drain the iteration-result queue as before: same records, same order.

TRTLLM_M31_DISABLE_A4_ASYNC_STATS=1 restores the blocking call.

Unit test: both proxies return the same records in the same order with
and without the switch, and the event loop keeps running while the
awaited RPC is in flight (it does not while the blocking one is).

Ported from dongfengy/tekit b84c17672 (GLM-5 Rubin branch).

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
… probe

Two executor diagnostics, armed by environment only and off by default.
Off, profile_step pays one None check per iteration.

TRTLLM_M31_PHASE_NSYS_ARM=<marker> (phase_capture.py): rank 0 reads a
JSON marker (schema_version 1, capture_at_unix_s, num_steps, max_seconds)
at most once per second and, at the requested wall-clock time, starts a
CUDA_PROFILER-only window through PyExecutorProfileManager.start_profile,
whose broadcast starts every rank on the same iteration. The window stops
after num_steps or max_seconds (a non-blocking stop: the scheduled stop
config plus its broadcast). It covers a chosen phase of a running server
for nsys --capture-range=cudaProfilerApi. Never during warmup; refused
together with the iteration-range or torch-trace profiling variables.

TRTLLM_M31_HOST_PATH_PROBE=<dir> (host_path_probe.py): per iteration and
rank, host-only wall-clock stamps (loop top, forward issue and return,
the wait on the previous step's sampler event) and the forward's
context / generation request counts, written as int64 rows. The wait
is the host's headroom over the GPU. No CUDA events and no added
synchronization: the probe waits on the same event the sampler then
waits on.

Unit test (CPU): off by default with the executor untouched, a single
None check in profile_step's per-iteration function, the marker poll
rate, start and both stop reasons, malformed markers, warmup, rank and
conflict guards, and the probe's rows and call passthrough.

Adapted from dongfengy/tekit 8b5caa029 (GLM-5 phase-armed capture) and
the per-rank host-path fixture its host commits report.

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
The async stats test needs no device; list it with the neighbouring
executor RPC tests so CI collects it.

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
…2 GEMM at qualified row counts

The TP2 target's sequence-parallel router computes each rank's owned half of
the rows with a native TF32 GEMM (trtllm::m31_router_half_pin, built into
th_common) instead of the full-shape router, at the full-step row counts that
were qualified bitwise against the full-row cuBLAS reference; every other
layout keeps the stock full-row path. Routing arms it when it selects the
TP2 target. Kill switch: TRTLLM_M31_DISABLE_ROUTER_HALF_PIN=1.

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
- unittest/others/test_m31_router_half_pin.py (CPU, collected by the
  l0_cpu unittest/others entry): arming, the kill switch, idempotence and
  the fallback to the full-row path.
- unittest/_torch/thop/parallel/test_m31_router_half_pin.py (GPU, sm_107):
  bitwise against the full-row reference at every qualified shape and
  ownership, in eager mode and under CUDA graph replay with changing
  inputs, plus the unqualified-layout guards.

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
…1 sm_107 targets

The executor records timed CUDA events around every decode step: the
per-request perf metrics' forward start, forward end and sample end, and
the iteration log's device-step boundary. A timed record waits for its
stream to drain, and while one is in flight the next decode graph's
launch waits behind it: a few microseconds per record at every step
boundary.

GpuTimestamp (perf_metrics_manager.py) keeps the torch.cuda.Event
surface the executor uses (record, query, synchronize, elapsed_time).
record() makes the timestamp's own non-blocking follower stream wait for
the recording stream, then stores %globaltimer into a device word with
the new native op trtllm::minimax_m31_globaltimer_stamp (one thread,
kernels/minimaxM31/globaltimerStamp.cu), copies it to pinned host memory
and records an untimed event, all on the follower. Under stream capture
it records nothing. timing_event() returns it for the perf metrics and
the iteration log's events once armed.

Routing arms it process-wide for the sm_107 targets
(arm_sm107_shared_opt_ins -> arm_gpu_timestamps), without editing the
target directories. It arms only when the native op is present and
pinned memory is preferred; otherwise timed events stay.
TRTLLM_M31_DISABLE_GPU_TIMESTAMPS=1 keeps timed events. Each process
logs one "[M31] executor timing: ..." line.

Measured (same node, untraced, c1 K7 decode with the perf metrics and
the iteration log on, replay period of the dominant decode graph, the
Triton stand-in stamp): TP4 -9.5 us median / -7.6 us trimmed mean per
step, TP8 -6.7 / -11.2 us (about -0.2..-0.4% of the step).

Unit tests: GpuTimestamp elapsed time matches timed events over a spin,
query / unrecorded semantics, no record under capture, timing_event
follows the arm, the kill switch; the shared opt-ins test covers the new
arm call, flag and kill switch.

Ported from dongfengy/tekit 5f03c8e89 (GLM-5), with the stamp as a
native op.

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
…mestamps

GpuTimestamp now writes its stamp only with the native op
(trtllm::minimax_m31_globaltimer_stamp), looked up once when routing arms
GPU timestamps; without the op the executor keeps timed CUDA events. The
measurement-only Triton stand-in (TRTLLM_M31_GPU_TIMESTAMPS_TRITON_PROTO)
is removed from runtime code. The arm logs one line, "[M31] executor
timing: GPU timestamps" or "timed CUDA events".

The GPU unit test stands in a test-local %globaltimer store when the
native op is not built, so the timestamp's stream and timing logic stays
covered on such images.

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>
…uter half-row pin

The router half-row op is built into th_common, so the fallback that loaded
a separate libm31_router_half_pin.so (never built) and its imports are
removed. No behaviour change.

Signed-off-by: peihengh <259410613+peihu-nv@users.noreply.github.com>

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants