Repository navigation
Conversation
…ention steps With FlashMLA mega attention the Q half of fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert only zero-pads the chunk-interleaved wq_b output to the kernel's 64 heads. With 32 live heads (TP2 of a 64-head model) that is 32 KiB read and 64 KiB written per token, against about 1.8 KiB for the KV insert sharing the launch. On eager steps of at least 1024 tokens (the op's NUM_TOKEN_CUTOFF), such layers now run the op in its existing KV-only mode (q_head_padded=0) and build the padded Q with _pad_interleaved_q, a Triton copy over (token, 16-dim chunk) rows that copies the live slots and writes +0.0 into the padding slots. The kernel is registered for JIT warmup. Captured CUDA graphs, smaller steps, other head counts, other attention backends and other platforms keep the fused launch. The result is bit-identical for NaN-free Q: with norm and RoPE off the op's live-Q path round-trips each element through fp32 unchanged and pads with +0.0, and the KV-only mode runs the same per-token KV code. test_q_interleaved_pad_copy_matches_fused_insert compares cache bytes and padded Q bits with the fused launch at 17, 1024 and 2049 tokens, with and without DP-padding rows. The PCP stand-in layer in test_compressor_kv_cache.py declares the new _pads_q_by_copy attribute. Signed-off-by: bofengl <bofengl@nvidia.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Overview
Makes the per-layer Q pad + KV insert of DeepSeek-V4.1 mega-attention layers 1.9-2.15x faster on eager steps of 1024 tokens or more at TP2, with bit-identical outputs. On layers with 32 live heads padded to 64 (TP2 of a 64-head model), those steps
now run the fused insert op in its existing KV-only mode and build the zero-padded, chunk-interleaved Q with a contiguous Triton copy, instead of padding Q inside the fused op.
Part of vllm-project#57448, on top of FlashMLA mega attention (vllm-project#56935) and the interleaved V4.1 KV records (vllm-project#61371); related: vllm-project#58872, vllm-project#55654, vllm-project#60076 (see Not a duplicate).
Claims
its KV half, and it writes Q one (token, head) slot per warp, in 16-element pieces
64 * 16elements apart. The copy writes each token's 32 rows of 1024 elements contiguously.backends and other platforms run exactly as before. There is no new flag, env var or config option.
VLLM_GPU_SYNC_CHECK=errordetects no host sync.Validation
Kernel time of the Q pad + KV insert per layer, fused op vs KV-only launch + copy (bf16, 32 live / 64 padded heads,
python bench_q_pad_copy.pybelow: each arm replayed as a CUDA graph and timed with CUDA events, median; correctness asserted beforetiming; two runs agree to within 0.1%):
The shipped copy launch config (
BLOCK_ROWS=4, 4 warps) is the fastest of the 12 configurations swept at 2048 tokens and tied fastest at 8192.bench_q_pad_copy.py
In serving (TP2, mega attention selected explicitly, Engram offload and indexer settings as in the DeepSeek-V4.1-Flash recipe). 8192-token steps are above the default CUDA-graph capture sizes, so they run eagerly and take the new path: the torch
profiler of an 8192-token prefill shows
_pad_interleaved_q_kernel(server flag--profiler-config '{"profiler": "torch", "torch_profiler_dir": "./prof"}', then the bench command below with--profile --num-prompts 2).Correctness: unit tests (CUDA GPU with the
_Cextension built):test_q_interleaved_pad_copy_matches_fused_insert(new)test_fused_deepseek_v4_qnorm_rope_kv_insert.py, whole filetest_v41_pcp_swa_cache_matches_non_pcp_writerThe new test runs the KV-only launch plus
_pad_interleaved_qand the fused launch on the same input at 17, 1024 and 2049 tokens (both sides of the op'sNUM_TOKEN_CUTOFF), with and without 5 trailing DP-padding rows that have no slot. It requiresidentical cache bytes and identical padded-Q bits, with the copy writing into a NaN-filled buffer.
Lint:
ruff checkandruff format --check(0.14.0),typos,py_compileand the local pre-commit hooks (check_spdx_header,check_forbidden_imports,check_torch_cuda,check_boolean_context_manager,check_init_lazy_imports,validate_config,check_test_tethering) pass on the four changed files, and so does the pre-commit mypy hook (tools/pre_commit/mypy.py, Python 3.11).CI coverage: the new test is in
tests/kernels/test_fused_deepseek_v4_qnorm_rope_kv_insert.py, which:nvidia: (H100) DeepSeek V4 Kernels,:nvidia: (B200) DeepSeek V4 Kernelsand the:amd: (MI355 DPX) DeepSeek V4 Fused Kernelsmirror run. ThisPR's test change triggers them, but their
source_file_dependenciesdo not listvllm/models/deepseek_v41/attention.py, so a later change to the copy alone would not re-run the test; I can add it if wanted.test_compressor_kv_cache.pyruns in:nvidia: (B200) Miscellaneous Kernels. No CI job serves mega attention at TP2 (the DeepSeek-V4.1 GSM8K config in CI is TP4, where the copy is off), so the serving check above is a manual run.Details
Motivation. Mega attention applies Q RoPE itself and reads Q chunk-interleaved: per token, 32 chunks of
[heads, 16]with the padding heads at the tail of every chunk. The V4.1 layer normsqrbeforewq_b, so the Q half offused_deepseek_v4_qnorm_rope_kv_rope_quant_insertis a pure zero-pad in that layout. With 32 live heads padded to 64 it reads 32 KiB and writes 64 KiB per token, against about 1.8 KiB for the KV half sharing the launch, so on large steps the op's timeis mostly the Q pad. The op processes Q one (token, head) slot per warp, and in this layout a head's 16-element lane chunks sit
padded_heads * 16elements apart; the copy instead writes each (token, chunk) row of 1024 elements contiguously.Design.
_pads_q_by_copyis set once per layer: the layer reads Q interleaved (accepts_unnormed_unroped_query), has 32 live heads padded to 64, andmax_num_batched_tokens >= 1024. Such layers register_pad_interleaved_qfor JIT warmup, so the firstlong prefill does not compile it.
_fused_qnorm_rope_kv_inserttakes the copy when the layer qualifies, the step has at least 1024 tokens and the current stream is not capturing. The op then runs withq_head_padded=0, the KV-only mode TP1 mega attention already uses onmain: it schedules no Q slots and runs the same per-token RoPE, MXFP8 quantization and paged insert, skipping DP-padding rows as before (
fused_deepseek_v4_kv_rope_insertlaunches the same KV kernel; passing 0 keeps the existing call)._pad_interleaved_qthen writes the padded Q into a fresh[num_tokens, 64, 512]tensor._pad_interleaved_qis a Triton kernel overnum_tokens * 32rows, one per (token, 16-dim chunk): 512 live elements copied, then 512 zeros. It goes throughtriton_kernel_dispatcher_with_warmuplike other warmed Triton launchers.NUM_TOKEN_CUTOFF, where it switches to its reduced-grid kernel; smaller steps keep one launch. The copy would also win at 512 tokens (1.61x above), but steps that small normally run inside captured CUDA graphs, which keep thefused pad.
_fused_qnorm_rope_kv_insert, next to the existing interleaved-Q handling; a mega-attention override would have to duplicate that method's KV-insert setup.Exactness. With norm and RoPE off, the op's live-Q path converts bf16 to fp32 and back with no arithmetic in between, which reproduces every non-NaN value bit for bit, and it stores +0.0 in the padding slots. The copy moves the bits unchanged and
stores the same +0.0. The KV half runs the same code in both modes, and the test compares the cache bytes with
rtol=0, atol=0.Tradeoffs and limitations.
tl.arangeneeds, so TP4/TP8 keep the fused pad.captured sizes of 1024 tokens or more.
Compatibility.
_pads_q_by_copyis false for them; mega attention itself is SM10x-only. DeepSeek-V4 (non-4.1) code is untouched.test_v41_pcp_swa_cache_matches_non_pcp_writernow declares_pads_q_by_copy=False, since that test calls_fused_qnorm_rope_kv_inserton it. The SM90-only token-shardedfused_wqa_wkv([Perf][DSv4.1] Split fused_wqa_wkv by token across TP ranks on SM90 vllm-project/vllm#58872) never meets mega attention, andforward_kv(decoder replay) builds no Q.q.shape[0], a flag set at init and the stream's capture state.Not a duplicate. On 2026-10-18 I searched open PRs and PRs since 2026-08-15 for "pad interleaved q", "qnorm rope kv insert", "Q pad mega" and "heads padded 64 decode"; none moves the mega-attention Q pad out of the fused insert. Related work: vllm-project#56935
added mega attention and the fused interleaved pad this PR bypasses on large eager steps; vllm-project#61371 changed the V4.1 KV record, which both launch modes share; vllm-project#58872 added the token-sharded
fused_wqa_wkvwhose_SHARD_FUSED_WQA_WKV_MIN_TOKENSsits nextto the new constant; vllm-project#60076 (open) switches the ROCm DSpark context-KV insert to the existing KV-only
fused_deepseek_v4_kv_rope_insertand touches neither mega attention nor its Q pad (it and this PR edit different tests intests/kernels/test_compressor_kv_cache.py). vllm-project#55654 (open) adds KV-only insert ops (fused_deepseek_v4_kv_rope_quant_insert, andfused_deepseek_v4_kv_norm_rope_quant_insertwith kv_norm folded in) so DeepSeek-V4's DSpark_insert_context_kvnolonger builds a dummy query. This PR adds no KV op and touches neither DSpark nor DeepSeek-V4: it calls the existing
q_head_padded=0mode of the fused op from the V4.1 attention layer. That path also sees DP-padding rows (more KV rows than slots),which the fused op's KV-only mode skips and vllm-project#55654's ops reject. The only file both PRs touch is
tests/kernels/test_fused_deepseek_v4_qnorm_rope_kv_insert.py, where each adds tests.AI assistance. AI assistance (Claude Code) was used to develop this change, port it onto current main, run vLLM's
pr-checklistskill and draft this description. I reviewed every changed line and ran the validation reported above.Pull Request Checklist
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.