[Perf][GDN] Share GDN metadata across cache groups for native MTP of any depth - #699
Merged
yangzhuxinyzx merged 2 commits intoSep 26, 2026
Merged
Conversation
added 2 commits
September 26, 2026 06:03
…racle The shared fused-metadata buffers are sized for the largest graph batch, and the fused kernel writes a step's rows only up to num_actual_tokens. The shadow oracle (VLLM_SM70_DFLASH2_GDN_METADATA_SHADOW) nevertheless required every entry of spec_sequence_masks after the live speculative rows to be False, including rows past num_actual_tokens that no graph replay of this step reads. Those rows were allocated with torch.empty, so the oracle failed or passed depending on leftover device memory. Allocate the mask zeroed, and check only the padded rows this step writes and replays. Replay behavior is unchanged; the oracle is debug-only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Leon <tomsel1010.us2@gmail.com>
…any depth 1CatAI#684 made native MTP4 classify each decode batch once and build every GDN cache group's state rows in one fused launch, instead of one metadata build per group per step. Nothing in that path depends on the draft depth except num_spec_state_tokens: the shared request metadata, the fused state rows (width num_spec + 1) and the sequence-length align column are all written per row. Only the gates required num_speculative_tokens == 4. Accept native MTP at any depth. The VLLM_SM70_MTP4_* switches and their defaults are unchanged and still roll the paths back; the names keep their historical prefix. The shared-metadata and fused-replay tests now run MTP1 through MTP4, each fused case at every verifier query length and in both cache modes, against separate per-group builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Leon <tomsel1010.us2@gmail.com>
Leonccaa
pushed a commit
to Leonccaa/1Cat-vLLM
that referenced
this pull request
Sep 26, 2026
Under DCP2 each rank holds half of a target QSA layer's main K/V. The shared CSA+linear pool sizes a block so that one physical page holds one recurrent (GDN) state, so keeping a whole state-sized page per sharded layer doubled the global block span (3,200 tokens instead of DCP1's 1,600 with E4M3 KV). The replicated selector, the draft and every request's recurrent-state blocks paid for that doubled span, short requests fit worse than on DCP1, and a prefix shorter than 3,200 tokens could never be reused. A block now spans cache_config.block_size global tokens at every DCP size: a sharded target layer holds block_size // dcp slots per rank, and the allocator packs sharded owners whose pages cannot hold a state, in layer order, as few per physical page as hold one (two at DCP2). A packed page interleaves its members one kernel block at a time, so each member is a strided view of the shared tensor and every kernel that honours the block stride (the cache write and the QSA sparse attention) works unchanged. - qsa_dcp_block_geometry returns span = block_size; the model asks the platform to align the block size to kernel block x dcp so the rank-local share stays kernel aligned (an lcm with any user block size). - The allocator groups owners (_pack_csa_linear_main_kv), places recurrent states per physical owner, and emits one KVCacheTensor per physical page with packed_members. - The v2 GPU runner lays out packed members as interleaved strided views; the v1 runner and pipeline parallelism reject packed configs. - The offload worker registers a packed page once, by its first member, covering every member's kernel blocks of that pool block. - The replicated draft expands its table from the target's rank-local page. Fewer physical owners per block (7 instead of 13) give six GDN state groups instead of three. Each group builds its metadata every decode step unless native MTP shares that metadata across groups (1CatAI#684 for MTP4, 1CatAI#699 for any depth). TP4 V100, MTP3, CUDA graphs, GPU KV tokens (DCP1 -> DCP2): - E4M3 KV (E4M3 MTP lane and grouped CPU offload applied): block 1,600 on both; 32K/C2 396,336 -> 493,244 (the previous 3,200-token span gave 439,296); 262,144/C4/GMU 0.96 775,096 -> 1,178,337 (1,142,744); repeated 2K/8K prefix TTFT 834/818 ms before this change, now 270/256 ms (DCP1 226/235). - FP16 KV: block 800 on both; 32K/C2 284,341 -> 404,706; 262,144/C4/GMU 0.96 433,401 -> 718,015. - Accuracy gate identical to DCP1 (tokens and MTP acceptance) with both KV dtypes; offload eviction+recovery, mixed prefill/decode and 5x30K admission regressions pass with DCP1-identical outputs. Unit tests cover the geometry, allocator packing and capacity projection, interleaved views (NHD/HND), offload registration, and a V100 test that writes packed members through the real cache writer and reads them with QSA sparse attention bit-identically to unpacked caches (FP16 and E4M3). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Leon <tomsel1010.us2@gmail.com>
Leonccaa
pushed a commit
to Leonccaa/1Cat-vLLM
that referenced
this pull request
Sep 26, 2026
Describe target-only DCP2 for Qwen4Exp QSA on TP4 V100: cache ownership, the packed layout and its geometry, the attention path, the related changes (1CatAI#664 for E4M3 KV with MTP; 1CatAI#617/1CatAI#598 for grouped CPU offload, whose DCP pieces this series adds; 1CatAI#684/1CatAI#699 for GDN metadata), the validation gates and results, and the known limits. Assisted-by: OpenAI Codex Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Leon <tomsel1010.us2@gmail.com>
This was referenced Sep 26, 2026
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
Follow-up to #684 (see this comment). #684 made native MTP4 classify each decode batch once and build every GDN cache group's state rows in one fused launch. Before that, metadata was built once per group per step, at about 0.65 ms of host time per group on V100.
Nothing in that path depends on the draft depth except
num_spec_state_tokens. The shared request metadata, the fused state rows (widthnum_spec + 1) and the sequence-length align column are all computed per row. Only the two gates requirednum_speculative_tokens == 4, so MTP1–MTP3 deployments, including our MTP3 Qwen3.8 serving stack, still build per group.This PR:
gdn_attn.py,mamba_hybrid.py). TheVLLM_SM70_MTP4_SHARED_GDN_METADATA/VLLM_SM70_MTP4_FUSED_GDN_METADATAswitches and their defaults are unchanged and still roll the paths back; the names keep their historical prefix, and comments and log lines now say MTP.VLLM_SM70_DFLASH2_GDN_METADATA_SHADOW, debug-only), in a separate commit. The fused kernel writes a step's rows only up tonum_actual_tokens, but the oracle required every entry of the sharedspec_sequence_masksbuffer after the live rows to be False, including rows no graph replay of this step reads. That buffer came fromtorch.empty, so the oracle passed or failed depending on leftover device memory; widening the tests to more depths made that visible. The mask is now allocated zeroed and the oracle checks only the padded rows the step writes and replays. Replay behavior is unchanged.Test Plan
pytest tests/v1/attention/test_gdn_metadata_builder.py tests/v1/spec_decode/test_dflash2.pyon V100 (SM70).test_mtp_shared_gdn_metadata_matches_legacyandtest_mtp_fused_gdn_group_metadata_matches_legacy_replaynow cover MTP1 through MTP4 against separate per-group builds. The fused test runs every verifier query length at each depth, in bothnoneandaligncache modes, with the shadow oracle on. Run twice to check stability.FULL_AND_PIECEWISEgraphs), where we ran an equivalent implementation (details in the [Perf] Share Flash-Next decode fast paths with MTP4 by default #684 comment).Test Result
Essential Elements of an Effective PR Description Checklist
supported_models.mdandexamplesfor a new model.🤖 Generated with Claude Code