Opt-in two-stream + launch-elision latency optimizations for Kimi-K2.6-NVFP4 decode on B300 (TP=4) - #45452
Draft
jinhuang12 wants to merge 1 commit into
Draft
Opt-in two-stream + launch-elision latency optimizations for Kimi-K2.6-NVFP4 decode on B300 (TP=4)#45452jinhuang12 wants to merge 1 commit into
jinhuang12 wants to merge 1 commit into
Conversation
Upstream-viable subset (maintainer-approved tracks only): MoE FP4 preamble fusion on the FlashInfer-TRTLLM MoE backend. Two redundant per-MoE-layer kernel launches are elided: (1) the FillFunctor/torch.zeros that pre-zeros the scale-factor buffer before cvt_fp16_to_fp4_sf_major is skipped (returns torch.empty instead) because the sf_major (non-swizzled) cvt kernel writes every SF address, making the pre-zero redundant; (2) the per-layer e_score_correction_bias.to(bfloat16) routing-bias cast is hoisted to module init (pre-cast once), eliminating ~60 bf16_copy_kernel_cuda launches per decode step. Guarded fall-through is bit-identical when the flag is off. [VLLM_OP003_PREAMBLE_FUSION] Two-stream MLA decode pipelining. The MLA wrapper (mla.py) allocates an auxiliary CUDA stream + event pair and hands them to MLAAttention via set_aux_stream(). In mla_attention.py the kv-path tail do_kv_cache_update (concat_and_cache_mla, ~2.4us/layer) is dispatched onto the aux stream while the q-path head of forward_impl (the W_UK_T BMM, ~6.5us/layer) runs on the default stream; a pre-event/ready-event protocol synchronizes the cache write before the attention kernel (forward_mqa/forward_mha) reads the cache. Applied at both the direct-call path (MLAAttention.forward) and the compiled opaque-op path (unified_mla_kv_cache_update / unified_mla_attention_with_output). When flag off, aux_stream is None and dispatch falls through sequentially with no behavioral change. [VLLM_OP010_MLA_TWO_STREAM] Event-based MoE shared/routed two-stream overlap. Replaces the framework's Stream.wait_stream-based SharedExperts overlap (which silently degenerates to sequential under CUDA-graph capture - R5 nsys showed all MoE kernels on the same streamId) with an OP-010-style torch.cuda.Event record/wait pattern. shared_experts.py gates _use_events on the flag, builds pre/post event pairs (per-ubatch for DBO), and runs the shared-expert layer on a side stream between pre_event.wait() and post_event.record(); join_event_overlap() lets the default stream join after routed kernels are enqueued. moe_runner.py launches the shared experts EARLY (before the routed FP4 BMMs) when MULTI_STREAM_OVERLAPPED is selected and _use_events is set, then calls join_event_overlap() to capture the routed work as parallel in the CUDA-graph DAG. Flag off => _use_events False => bit-identical legacy wait_stream ordering. [VLLM_OP014_MOE_TWO_STREAM] Signed-off-by: Jin Huang <jinhun@amazon.com>
jinhuang12
force-pushed
the
ammo/kimi-k2-6-nvfp4-c5909e04-rebase-main
branch
from
June 12, 2026 22:11
a3dcc3a to
35e36ac
Compare
Contributor
|
This pull request has merge conflicts that must be resolved before it can be |
4 tasks
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.
Summary
This PR adds three opt-in decode-latency optimizations for
nvidia/Kimi-K2.6-NVFP4running TP=4 on NVIDIA B300 SXM6 (Blackwell,sm_103) with the FlashInfer-MLA attention + FlashInfer-TRTLLM MoE backends, NVFP4 weights / FP8-E4M3 paged KV cache, CUDA graphs (FULL_AND_PIECEWISE) +torch.compile. Each optimization sits behind its own environment variable that defaults to off, so with all three flags unset the code path is unchanged from base — they are guarded fall-throughs, not changes to the default execution path. All changes are pure Python undervllm/; no C++/CUDA is touched, so no rebuild is required.This branch is rebased onto current
main. The optimization commit was developed and measured on vLLM v0.22.1 (commit0decac0d96c42b49572498019f0a0e3600f50398); the rebase ontomainapplied with no logic changes (the shared-experts and MoE-runner sites were re-anchored onto upstream's renamedrouted_experts.forward_monolithicdispatch API and theSharedExperts.__init__signature). The numbers below were measured on v0.22.1 and have not been re-validated onmain.This work was validated only on B300 (
sm_103) with the FlashInfer-MLA + FlashInfer-TRTLLM MoE backends; no claim is made about other architectures or backends. The optimizations are scoped to those code paths.Optimizations
MoE FP4 preamble launch-elision —
VLLM_MOE_FP4_PREAMBLE_FUSION=1, lossless (bit-exact). On the FlashInfer-TRTLLM MoE backend, elides two redundant per-MoE-layer launches: (1) thetorch.zeros/FillFunctorthat pre-zeros the FP4 scale-factor buffer beforecvt_fp16_to_fp4_sf_major(the non-swizzledsf_majorconversion kernel writes every scale-factor address unconditionally, so the pre-zero is dead work — replaced withtorch.empty), and (2) the per-layere_score_correction_bias.to(bfloat16)routing-bias cast, hoisted to module init so it runs once instead of per decode step. With the flag off,torch.zerosand the per-layer cast are retained, so behavior is bit-identical.Two-stream MLA decode pipelining —
VLLM_MLA_TWO_STREAM_DECODE=1, lossy in bits, accuracy-preserving. The MLA wrapper allocates an auxiliary CUDA stream + event pair and hands it toMLAAttentionviaset_aux_stream(). The kv-path tail (concat_and_cache_mla) is dispatched onto the aux stream while the q-path head (theW_UK_TBMM) runs on the default stream; an event record/wait protocol guarantees the cache write completes before the attention kernel reads it. Applied at both the direct-call path and the compiled opaque-op path (unified_mla_kv_cache_update/unified_mla_attention_with_output). With the flag off the aux stream isNoneand dispatch falls through sequentially with no behavioral change.Event-based MoE shared/routed two-stream overlap —
VLLM_MOE_SHARED_EXPERTS_TWO_STREAM=1, lossy in bits, accuracy-preserving. Replaces the existingStream.wait_stream-based shared-expert overlap — which collapses to sequential execution under CUDA-graph capture (all MoE kernels land on a single stream) — with atorch.cuda.Eventrecord/wait pattern (per-microbatch for DBO). The shared-expert layer runs on a side stream while the routed FP4 BMMs run on the default stream; the shared experts are launched early (before the routed BMMs) whenMULTI_STREAM_OVERLAPPEDis selected so the routed work is captured as parallel in the CUDA-graph DAG, then the default stream joins. With the flag off the legacywait_streamordering is bit-identical.Fixed-batch latency
vllm bench latencyonnvidia/Kimi-K2.6-NVFP4, 4× B300, TP=4 / DP=1,--max-num-seqs=8 --trust-remote-code, OSL=1000, 5 iters per shape, CUDA graphs (FULL_AND_PIECEWISE) +torch.compileenabled. A = base (vLLM v0.22.1, all flags unset); B = all three flags enabled. Both arms run the same base commit0decac0d9with an identical workload; the only difference is the patched files (A imports a clean tree, B imports the patched tree with the three env flags set). The grid sweeps input length × batch size in a single model load per arm. OTPS = output tokens/sec; TPOT = time per output token (both decode-path, from per-request metrics).Every shape improves on E2E, OTPS, and TPOT; the E2E gain ranges −2.5% to −6.0%. The improvement is decode-dominated (decode is 93–97% of E2E at OSL=1000), consistent with the two-stream MLA/MoE overlap and the per-MoE-layer launch-elision acting on the decode path. On significance: using a two-sample difference-of-means test (n=5 per arm), all five shapes' E2E deltas exceed twice the standard error of the difference. Under the stricter single-arm test (delta vs twice the baseline's per-iteration standard deviation), four of five shapes clear it; the 10000/bs1 shape (Δ=0.161 s vs 2σ_A=0.181 s) is the one exception — its E2E gain is within the baseline's single-run spread, though its OTPS/TPOT move in the same direction as the others. Prefill latency deltas are small and mixed across shapes (within their own per-iteration spread), so the end-to-end gains come from decode. No shape regresses on any metric.
Correctness
GSM8K (greedy decoding, full 1319-question test split), B300, vLLM v0.22.1, A/B against the base config under the production-parity environment:
The preamble launch-elision is bit-exact (no accuracy risk); the two stream-overlap changes are not bit-exact but introduce only floating-point order-of-add churn from running independent op chains on separate streams, with accuracy held within tolerance.
Changes
7 files changed, +426 / −29, all Python under
vllm/— no C++/CUDA, no rebuild required.vllm/envs.py(three env vars, each defaulting to"0"/off).vllm/_custom_ops.py,vllm/model_executor/models/deepseek_v2.py.vllm/model_executor/layers/mla.py,vllm/model_executor/layers/attention/mla_attention.py.vllm/model_executor/layers/fused_moe/runner/shared_experts.py,vllm/model_executor/layers/fused_moe/runner/moe_runner.py.AI-assisted contribution
These optimizations were produced by an automated process driving Claude (Anthropic) under human review: each candidate was profiled, implemented behind an opt-in flag, and kept only after passing a correctness check (a bit-exact /
torch.equalargument for the lossless change, a before/after GSM8K A/B for the two stream-overlap changes) and a before/after end-to-end latency benchmark. All numbers in this PR were measured on the real B300 deployment hardware with CUDA graphs andtorch.compileenabled, and a human reviewed every change before submission. With the flags unset the default execution path is unchanged.