Repository navigation
[Perf][DSv4.1] Split fused_wqa_wkv by token across TP ranks on SM90 - #58872
Merged
zyongye merged 3 commits intoOct 16, 2026
Merged
Conversation
Signed-off-by: Shuolei Wang <shuoleiwang123@gmail.com>
ShuoleiWang
force-pushed
the
perf/dsv41-shard-attn-input-proj
branch
from
September 30, 2026 15:23
203af50 to
4b2949f
Compare
ShuoleiWang
marked this pull request as ready for review
October 13, 2026 06:17
zyongye
approved these changes
Oct 15, 2026
zyongye
enabled auto-merge (squash)
October 15, 2026 00:50
Member
|
/ci run |
|
✅ Triggered Buildkite CI #94982 for commit |
Contributor
Author
|
/ci retry |
|
✅ Queued 1 failed job(s) for retry in Buildkite CI #94982. |
This was referenced Oct 16, 2026
Open
6 tasks done
michael-gpu
pushed a commit
to practical-inference/vllm-ampere-extended
that referenced
this pull request
Oct 20, 2026
…llm-project#58872) Signed-off-by: Shuolei Wang <shuoleiwang123@gmail.com>
6 tasks done
3 of 5 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.
Purpose
Without sequence parallelism, every TP rank computes DeepSeek-V4.1's
fused_wqa_wkv(5120 → 1280 + 512) for all tokens, so all ranks do the same work. The checkpoint stores this weight as FP8 with UE8M0 block scales, which vLLM runs through its MXFP8 path. On SM90 that path is a W8A16 Marlin GEMM: on H20 it takes 1.79 ms per layer for an 8192-token prefill chunk, 72 ms per chunk over the 40 layers.This PR splits the projection by token for batches of at least 2048 tokens. Each rank projects its token shard (
sp_shard), andsp_all_gatherrebuildsqr_kv. The change is confined toDeepseekV4Attention._fused_wqa_wkv_gemm:_run_parallel_input_projectionsis unchanged, so the multi-stream decision is the same as on main._shard_fused_wqa_wkvin the NVIDIA decoder layer enables the split only when all of the following hold:fused_wqa_wkvruns as a native MXFP8 GEMM, far cheaper than W8A16 Marlin, so the gather would be a much larger share of the cost. I have not measured SM100, and the H20 results below do not carry over to it.TP4 and TP2 take the same gate but were not measured. On Hopper they need pipeline parallelism to hold the checkpoint.
The compressor's
fused_wkv_wgateand the indexer'sweights_projstay replicated, and_run_parallel_input_projections, which can run them on separate streams, is unchanged. They appear in only a few layers:At 8192 tokens, splitting them as well would save at most another 457 µs in a KV-source layer (last column of the second table; layer 20's compressor is half as wide). In the four indexer-only layers the projection is only 5120 → 32. This PR saves about 1.45 ms in each of the 40 layers, so it limits the split to this dominant projection.
Why 2048 tokens. On H20 the split is faster from 48 tokens (first table below). H100 and H200 have about 6.7 times H20's dense throughput with the same 900 GB/s NVLink, so the replicated GEMM costs much less there while the gather costs the same.
I have no H100 or H200 to measure on, so I scaled the H20 numbers pessimistically:
Under these assumptions the break-even is about 1450 tokens, and 2048 is the next power of two. A measurement on H100 or H200 would confirm or retune it.
Related work. I found no upstream PR for the case without sequence parallelism. The fork commit bytedance-iaas/vllm@383776ac20 splits
fused_wqa_wkvthe same way, but behind an environment variable that is off by default, and not when speculative decoding is on.Test Plan
Kernel benchmark. Setup:
0.30.1rc1.dev85+g8b84e1506with this diff applied as a Python overlay.fused_wqa_wkvis built as the model builds it, with random weights and the checkpoint's quantization, so SM90 runs Marlin. The second table times_run_parallel_input_projectionsfrom this branch, which calls_fused_wqa_wkv_gemm, with the model's projection shapes and three aux streams.Timing follows
.agents/skills/kernel-microbenchmark:The benchmark script is not part of this PR; I can add it under
benchmarks/kernels/if that helps.Serving. TP8 without expert parallelism. DSpark uses synthetic acceptance with a mean acceptance length of 3.51, the same distribution in both arms:
One
vllm bench servecall per point:Each arm ran on a fresh server, in the order this PR (B1), main (A1), main (A2), this PR (B2).
Accuracy. Full GSM8K (1,319 questions) through
/v1/chat/completions, with real DSpark verification (rejection_sample_method: block) instead of synthetic acceptance:chat_template_kwargs: {"thinking": false});max_tokens1024, 32 concurrent requests;####, or else by its last number.Test Result
fused_wqa_wkv, split vs replicated (µs per call, TP8, every batch size split so the crossover is visible):Up to 4096 tokens the gather is the custom all-reduce's all-gather; from 6144 tokens it is NCCL. From 4096 tokens on, the replicated Marlin GEMM runs at 83–84 TFLOPS.
Input projections of one attention layer, main vs this PR (µs; only this stage is timed, not the whole layer):
qr_kvdiffers from the replicated projection by at most 0.031 across two sweeps with different inputs. That is bf16 rounding from Marlin's M-dependent tiling.kv_scoreandindexer_weightsare bitwise identical to main.Serving and accuracy. The serving numbers are a sanity check with two servers per arm, not a precise measurement. Lower TTFT and higher output throughput are better, and Change compares the means of the two arms.
At 1K in / 1K out, C1, no batch reaches 2048 tokens, so both arms run main's kernels. The two servers of this PR differ by 1.2% there, more than the gap between the arms.
In three runs of main on the same nightly, one of them the run above, GSM8K accuracy ranged from 96.36% to 96.97%.
AI assistance: Claude Code helped with the implementation and ran the benchmarks. I reviewed every changed line and checked the results above.
🤖 Generated with Claude Code