[Bugfix][DSv4] Skip zero-query-len prefill chunks in FlashMLA sparse prefill (complements #49059) - #51489
[Bugfix][DSv4] Skip zero-query-len prefill chunks in FlashMLA sparse prefill (complements #49059)#51489fireflyc wants to merge 1 commit into
Conversation
|
👋 Hi! Thank you for contributing to the vLLM project. 💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in PRs do not trigger a full CI run by default. Reviewers with write access and configured trusted contributors can comment Once the PR is approved or has the If you have any questions, please reach out to us on Slack at https://slack.vllm.ai. Agent GuidelinesIMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban. 🚀 |
d00227b to
54dc4e1
Compare
…prefill Fix a crash where KVTransfer/SimpleCPUOffloadConnector schedules prefill requests with 0 tokens while their KV is being loaded. Such requests land in the prefill region with query_len == 0; get_prefill_chunk_plan could emit a chunk covering only zero-query-len requests, so query_start == query_end and _forward_prefill passed an empty tensor to flash_mla_sparse_fwd, which failed to build the output TMA descriptor (s_q == 0) and aborted at phase1.cuh:614. - get_prefill_chunk_plan: skip zero-query-len requests when advancing chunk boundaries so they never form their own chunk. - _forward_prefill: skip chunks with query_end <= query_start, and return early when the chunk plan is empty. - Add unit tests for the zero-query chunk-planning cases. Signed-off-by: fireflyc <fireflyc@gmail.com> Co-authored-by: Claude <noreply@anthropic.com>
54dc4e1 to
b8ca6f3
Compare
|
SM90 validation from a different producer of the zero-query state: we semantically backported these guards onto stock v0.26.0 without #49302, so PIECEWISE replay padding still produces the #54179 phantom requests. On 4x H20 TP4, all previously crashing cases pass: 512-token, 2,400-token cold, 312K cold, and 312K prefix-hit resend. Instrumentation: the planner skip fired 432 times; the backend guard fired 0 times; no Caveat: v0.26.0's layout required semantic adaptation, so this was not verbatim PR-head validation. We also did not run #51486's |
|
Thanks @zihanlin-ai for this — it's genuinely useful. Your instrumentation actually gives the strongest evidence for the structure we chose: the planner skip firing 432× while the backend guard fired 0× confirms that avoiding the zero-token chunk at the source is the real fix, and the Thanks again — this moves the PR forward. |
Why
[Fixes #51486] DeepSeek-V4 sparse prefill crashes with
Assertion res == CUresult::CUDA_SUCCESS failed (.../phase1.cuh:614)when KVTransfer /SimpleCPUOffloadConnectorschedules prefill requests with 0 tokens while their KV is being loaded.When such a request sits in the batch's prefill region with
query_len == 0,DeepseekSparseSWAMetadata.get_prefill_chunk_plancan emit a chunk covering only zero-query-len requests, soquery_start == query_end._forward_prefillthen slicesq/outto an empty tensor and passes it toflash_mla_sparse_fwd, which fails to build the output TMA descriptor (s_q == 0) and aborts.This is not a FlashMLA kernel bug — it is a missing invariant in the DeepSeek-V4 sparse-prefill chunking: it assumed every prefill-region request has ≥ 1 query token, which the scheduler (KVTransfer async load) does not guarantee.
What
vllm/v1/attention/backends/mla/sparse_swa.py—get_prefill_chunk_plannow skips zero-query-len prefill requests when advancing chunk boundaries, so they never form their own (empty) chunk.vllm/models/deepseek_v4/nvidia/flashmla.py—_forward_prefillcomputes the chunk's query range before any KV gather, skips chunks withquery_end <= query_start(defense in depth), and returns early when the plan is empty (all placeholder requests).tests/kernels/attention/test_flashmla_sparse.py— two unit tests covering the chunk planner with zero-query-len requests (skipped/never-empty) and the all-zero → empty-plan case.Behavior of the existing chunk planner is unchanged when there are no zero-query requests (existing test
test_deepseek_v4_prefill_chunk_planning_expands_for_short_sequencesstill passes).Why not duplicating an existing PR
query_start == query_end) but only for the FlashInfer / SM120 path (vllm/models/deepseek_v4/nvidia/flashinfer_sparse.py). It does not touch the FlashMLA path (vllm/models/deepseek_v4/nvidia/flashmla.py) or the shared chunk planner (vllm/v1/attention/backends/mla/sparse_swa.py).query_start == query_endempty-chunk bug, manifesting as a FlashMLA TMA-descriptor assert atphase1.cuh:614instead of FlashInfer's reshape error.gh pr list --repo vllm-project/vllm --state open --search "..."on 2026-08-08: no PR other than [Bugfix][DSv4][SM120] Skip empty sparse-MLA prefill chunks #49059 addresses this. If maintainers prefer, these two changes can be consolidated into [Bugfix][DSv4][SM120] Skip empty sparse-MLA prefill chunks #49059.Test plan
.venv/bin/python -m pytest tests/kernels/attention/test_flashmla_sparse.py -v -k "chunk_plan"— passed (3 tests: the existing chunk-plan test + the 2 new ones).mypyvar-annotatederror oncoveredfrom the first run was fixed withcovered: list[int] = []).Model evaluation
This change only skips chunks that produce zero output tokens (placeholder requests); it does not alter the computation or output of any real prefill token, so no numerical/accuracy change is expected.
Real smoke test: served DeepSeek-V4-Flash-0731 with the reproduction config below (the same one that previously crashed with the
phase1.cuh:614TMA-descriptor assert) and completed mixed decode/prefill requests without the crash:then load-tested with:
evalscope perf \ --parallel 48 \ --api openai \ --min-tokens 1024 \ --max-tokens 1024 \ --prefix-length 0 \ --min-prompt-length 32768 \ --max-prompt-length 32768 \ --number 100 \ --tokenizer-path Qwen/Qwen2.5-VL-3B-Instruct \ --name $result_name