{{ message }}
Repository navigation
Conversation
…teps In align mode with speculative decoding, a prefill chunk that exports an internal checkpoint skipped relocating the previous step's speculative scratch block. The block stayed at the natural index of the checkpoint position, was never written, and was still cached under that position's block hash next to the real checkpoint block. Lookups return the first block registered under a hash, so later prefix-cache hits resumed the linear-attention state from the stale block and produced garbage. Relocate scratch blocks below the checkpoint index in checkpoint steps too, and drop the extra speculative blocks from the allocation cap and admission estimate, since relocation now covers them. AI assistance was used (Claude). The change was reviewed and tested by the submitter. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Simon Veitner <sveitner@redhat.com>
simon-veitner-redhat
marked this pull request as ready for review
October 2, 2026 08:55
simon-veitner-redhat
requested review from
ApostaC,
WoosukKwon,
alexm-redhat,
heheda12345,
ivanium,
njhill,
orozery,
robertgshaw2-redhat and
ywang96
as code owners
October 2, 2026 08:55
Collaborator
|
✅ @simon-veitner-redhat, CI is now available for this PR.
|
|
✅ Triggered Buildkite CI #92804 for commit |
Collaborator
CI selector (shadow): 29 test steps (45 jobs) instead of 60 (77 jobs)Shadow mode: this changes nothing about what CI runs. It shows what the evidence-based selector would pick for this PR, next to today's rules. How it works. Feedback welcome: reply here if it would skip a step this change needs, or runs something unrelated.
Selector would run (29)
Would skip (today's rules run them) (49)
Would add (today's rules do not run them) (18)
AMD mirrors: would skip (44)
AMD mirrors: would add (15)
2 changed files · base |
YibaiMeng
added a commit
to CentML/vllm
that referenced
this pull request
Oct 7, 2026
…teps Port of vllm-project#59759 (merged 2026-10-06). In a checkpoint step allocate_new_blocks did not relocate the previous chunk's speculative scratch blocks, so one could stay below the checkpoint column, never written, where cache_blocks could register it as a full-block prefix entry. Relocate scratch blocks below the checkpoint index (one at the checkpoint index stays and becomes the checkpoint block); checkpoint steps of allocated requests no longer need num_speculative_blocks extra new blocks, in the allocation cap and in the admission estimate alike. On this branch be3d9b8 (cache_blocks skips every column below the checkpoint column) already keeps such a block out of the prefix cache, so the upstream symptom is not reachable here; the upstream test passes with and without this commit, and fails only with be3d9b8 reverted. Both stay: this commit alone does not cover the sub-block prefix-hit copy case of be3d9b8 (test_checkpointed_chunk_skips_sub_block_hit_copy fails without it). Upstream test test_mamba_checkpoint_hash_maps_only_to_checkpoint_block included. (pre-commit ruff SIM102 at line 1614 is pre-existing, untouched.) Signed-off-by: Yibai Meng <yibaim@nvidia.com>
6 tasks done
michael-gpu
pushed a commit
to practical-inference/vllm-ampere-extended
that referenced
this pull request
Oct 20, 2026
…teps (vllm-project#59759) Signed-off-by: Simon Veitner <sveitner@redhat.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Robert Shaw <114415538+robertgshaw2-redhat@users.noreply.github.com>
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.

Overview
With MTP and prefix caching on, hybrid models such as GLM-5.3-Flash can return garbage when a request reuses a cached prompt prefix. Some prefill steps save a checkpoint of the linear-attention state in the middle of a chunk. In those steps the scheduler leaves an unused MTP scratch block in the slot that belongs to the checkpoint. That block is never written, yet it is registered in the prefix cache ahead of the real checkpoint, so later requests resume from it. This change moves the scratch block out of the way in those steps too, as already happens in every other step.
The skip was added together with internal checkpoints in #53614. GLM-5.3-Flash reaches this path since #56960. The branch is based on current main (58b3298). No open PR changes speculative-block relocation; #57128 changes the Mamba lookup and does not touch this path.
Claims
--speculative-config '{"method":"mtp","num_speculative_tokens":1}'.mamba_cache_modeother thanalign, or for steps without an internal checkpoint.Validation
Setup: RedHatAI/GLM-5.3-Flash-NVFP4 on 2 H200 GPUs, served with default settings plus MTP:
Reproducer (script below): send a long prompt while another long prompt is still being processed, then send the exact same prompt again. The second request reuses the cached start of the prompt.
File: /home/erdos/2024/4/4 # File: /home/erdos/2024/4/4 ..., while the first answer was a normal list of function names.Other checks:
test_mamba_checkpoint_hash_maps_only_to_checkpoint_blockfails on main and passes with the fix.pytest tests/v1/core/test_single_type_kv_cache_manager.py tests/v1/core/test_prefix_caching.py tests/v1/core/test_mamba_align_chunk_split.py tests/v1/core/test_prefix_replay.py tests/v1/core/prefix_cachepasses; the GPU part,test_hybrid_prefix_cache_hit_rate.py(baseandmtp), was run on one H200.repro.py
Example output on main (before the fix):
With the fix:
Details
In align mode a request keeps its running state in the block after the last computed one, followed by
num_speculative_blocksscratch blocks. A normalallocate_new_blockscall moves the old scratch blocks that fall below the new running state to the end of the table and nulls their slots. In a checkpoint step this was skipped (if blocks_allocated and not checkpoint_block).With MTP, the checkpoint position P is shifted one block down. When one of the previous step's scratch blocks sits at index
P // block_size - 1(with k=1: the chunk starts one block below P), it occupies the natural slot of P. The checkpoint itself is written into a new block atcdiv(end, block_size) - 2, one index above it. In the same step,cache_full_blocksregisters the unwritten scratch block under hash(P), and_cache_partial_tail_blockthen adds the real checkpoint block under the same hash.get_one_blockreturns the first block registered, so later hits at P resume from the scratch page. The entry stays until eviction, so every request that shares this prefix is affected.It happens when a prompt's final prefill chunk starts one block below the shifted checkpoint, so it depends on prompt length and on how much of each step's token budget other requests use. Overlapping long prompts make it much more likely. For example, with a second request of 22350 tokens, a first request of up to 23424 tokens triggers it (3/3) and one of 23440 tokens does not (0/3).
The fix relocates scratch blocks below the checkpoint index (
block_idx < num_skipped_blocks - checkpoint_block). A scratch block exactly at the checkpoint index stays and becomes the checkpoint block, since the worker exports the checkpoint into that column. Because relocated blocks are reused, checkpoint steps for already-allocated requests no longer neednum_speculative_blocksextra new blocks. The allocation cap inallocate_new_blocksand the admission estimate inget_num_blocks_to_allocateboth drop that term, so they stay equal. The existingtest_mamba_checkpoint_admission_matches_allocationand the new test both check this.On unfixed builds, disabling MTP or prefix caching avoids the bug.
Possible CI follow-up: no current job would catch this. The only nightly on this path, the B200 Kimi-K3 spec-decode GSM8K job, uses ~640-token prompts, and GSM8K accuracy does not move noticeably. A small extra phase in that job could catch this class of bug. After the GSM8K pass, it would send about 15 long prompts with lengths spread over one chunk-budget period, then send each again and check that the cached answer starts the same as a fresh computation. On GLM-5.3-Flash such a phase failed on unfixed main in 3 of 3 runs and passed with this fix in 3 of 3. I can send it as a separate PR if that is useful.
AI assistance (Claude) was used; the change was reviewed and validated by the submitter.
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.
🤖 Generated with Claude Code