Repository navigation
[Perf][Mamba] Reduce host overhead in the Mamba2 Triton SSD prefill path - #61497
Conversation
6b7d288 to
02c2029
Compare
|
This pull request has merge conflicts that must be resolved before it can be |
|
✅ @jhaotingc, CI is now available for this PR.
|
… per-launch Python Write Mamba2 prefill final states from the state-passing kernel. The varlen SSD scan returned states[last_chunk_indices] and the mixer then scattered them with ssm_state[state_indices_tensor_p] = final_states: an int32->int64 index cast plus a gather, and another cast plus an index_put, i.e. 4 extra launches per Mamba layer in every prefill or mixed step. _state_passing_fwd_kernel already holds each sequence's final state in registers after its last chunk, so it now stores it straight into the sequence's ssm_state slot (same fp32 -> state-dtype cast as before). The mixer passes ssm_state and state_indices_tensor_p when the step has no prefill checkpoints; the checkpoint path keeps returning all chunk states. Implementations without the fused write (the CPU path) still return the final states and mamba_chunk_scan_combined_varlen scatters them. The SSD warmup also compiles the new HAS_FINAL_STATES variant. state_indices_tensor_p is the Mamba block table's first column from the first prefill row on: - It is strided when a request owns more than one state block (speculative state blocks under plain MTP, or align mode), so the kernel indexes it with its stride. - Its 16-byte alignment changes with the number of decode rows in a step. The kernel does not specialize final_state_indices_ptr on alignment, so mixed steps do not compile new variants while serving. Trim per-launch Python in the Triton SSD path. Every eager Mamba2 prefill or mixed step pays the Python cost of five Triton SSD launches per Mamba layer. On a B200 host one varlen SSD scan (1069-token prefill) spent 250 us of host time, most of it outside the kernels: - Triton's Autotuner.run rebuilds its cache key from every argument on each launch (dict copies plus str(dtype) for each tensor argument), about 7-19 us per launch depending on the argument count. launch_autotuned() (triton_helpers.py) remembers the config the autotuner picked for a caller-supplied key and launches the JIT function with it directly. Each wrapper passes the values of its autotune key arguments and the dtypes of its tensor arguments, which determine Triton's key, so the selected config is unchanged. - _chunk_cumsum_fwd, _chunk_state_fwd, _state_passing_fwd and _bmm_chunk_fwd switched the current device around their launch, but _chunk_scan_fwd, launched in the same scan, does not, so the scan already requires its inputs on the current device. Drop the four guards. - einops.rearrange parsed its pattern three times per scan; use flatten/unflatten. - CudaPlatformBase.is_arch_support_pdl() queried the device on every call (causal_conv1d launches call it twice per layer); cache it, since vLLM assumes all visible GPUs share an architecture. Host time per scan drops from 250 us to 168 us (about -1.9 ms per mixed step with 23 Mamba layers); tests/kernels/mamba/test_mamba_ssm_ssd.py passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Jhao-Ting Chen <jhaotingc@nvidia.com>
02c2029 to
0857c5d
Compare
|
/ci run |
|
❌ @jhaotingc, A reviewer with write access must run |
|
@tomeras91 am I too slow to run the CI? TY! |
|
Hi @ZJY0516 do you mind taking a look at this PR? Thank you! I saw you are doing some mamba improvement. |
Review feedback: callers should not maintain a Triton key list by hand. launch_autotuned() now builds its cache key from the launch's own kwargs: the values of the kernel's autotune `key` arguments (Autotuner.keys) plus the dtype, or None, of every argument. That is the information Triton's Autotuner.run puts in its key (key argument values plus the dtype of each argument that has one), so the reused config is the one Triton would pick, and the five SSD call sites drop their hand-written key tuples. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Jhao-Ting Chen <jhaotingc@nvidia.com>
|
Hi @ZJY0516, now there's no maintaining of a cache key. Launched_autotune derive key from launch. I also tried Would you mind giving PR a look again? TY. This is for a Nemotron Lightning ckpt. Agent summary of the issue:
|
|
/ci run --allow-stale |
|
✅ Triggered Buildkite CI #96086 for commit
|
Overview
Reduces host overhead in the Mamba2 Triton SSD prefill path. This PR is
part of a series that brings piecewise CUDA graphs to Mamba2 prefill steps. Tested with #61293
applied, but it does not depend on it.
Claims
MTP=2: 1.04-1.08x at concurrency 4-64, 1.02x at 128 and 1.01x at 1-2.
layer, about 1.9 ms less per mixed step with 23 Mamba layers. Every prefill or mixed step also
issues 4 fewer launches per Mamba layer.
Validation
End-to-end serving throughput, before vs after, on the same node with identical flags:
mainat 8c60cf0 (this PR's base, which includes [MoE] Shape-based padding for FlashInfer TRT-LLM MXFP8 experts (gated and non-gated) #61293). After: the same, plusthis PR.
throughput_1k,low_entropycategory (512 prompts),max_tokens200, served through aiperf.mean over both serves, so the concurrency 64 and 128 rows are the mean of 6 runs.
Values are OTPS/gpu (output tokens/s per GPU):
Mid concurrencies have the most prefill and mixed-batch interruptions, so they gain the most; the
effect is smaller at low and high concurrency.
Accuracy: GPQA-diamond through NeMo Gym on 1x GB300, TP1, MTP=2 (198 questions x 8 repeats, thinking on,
temperature 1.0, top_p 0.95), with the serve command below plus
--max-model-len 262144 --reasoning-parser nemotron_v3. pass@1, mean over the 8 repeats ± standard error:--enable-prefix-caching --mamba-cache-mode align, no ReplaySSM): 75.25 ± 0.63%Serve and client commands
The default Triton SSD scan runs the prefills;
--mamba-backend flashinferonly selects the decodestate update.
Client, run once per concurrency level:
Details
The Mamba2 mixer runs eagerly in prefill and mixed prefill+decode steps, so its host-side overhead
lands directly on step latency. This matters most in two cases:
request.
to hide the CPU overhead.
Before this PR, a varlen SSD scan plus the final-state write spent 263 us of host time per Mamba
layer, mostly Python launch overhead, and each layer issued 4 extra launches to write final states
back. Changes, all on the default Triton SSD path:
ssm_stateslot from_state_passing_fwd_kernel,replacing the index casts, gather and
index_put. The checkpoint path and CPU path are unchanged.launch_autotuned(), which reuses the autotuner's chosen config and skips Triton's per-launchkey rebuild. It builds its cache key from the launch's own arguments (the values of the kernel's
autotune
keyarguments plus each argument's dtype, the information Triton's key holds), so callerskeep no key list and the selected config is unchanged.
flat at 1-2 and 128; it accounts for 43 of the 84 us saved per layer.
einops.rearrangewithflatten/unflatten.CudaPlatformBase.is_arch_support_pdl().Pull Request Checklist
I used vLLM's
/pr-checklistskill. (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.