fix(ENG-CUDAGRAPH-BREAK): a batch of one-token prefill chunks dispatches as uniform decode, on both eligibility predicates
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 423
- Forks
- 53
- Avg merge
- 20h 26m
- Merged PRs (30d)
- 310
Description
Row: ENG-CUDAGRAPH-BREAK
Found by wave PORTQ-4 (#2680)
re-deriving PORT-NOW entry 146 of 5559679229..e126687a9a, upstream
0a94d85a66 vllm#51865
"[Bugfix][MRV2] Require all requests to be decoding for uniform-decode dispatch".
What is wrong
A batch of N one-token prefill chunks is dispatched as uniform decode, because
neither eligibility predicate tests for prefill.
src/vllm/v1/worker/gpu/cudagraph_dispatch.h:168
if (!q.has_value() || *q == 1) return q;
GraphEligibleQueryLen (:162-176) returns a uniform-decode answer at q == 1
with no prefill conjunct at all. Its q > 1 arm (:169-174) is stricter than
upstream — it requires every request to be verifying at exactly q-1 drafts — so
only q == 1 is exposed.
The second predicate has the same hole:
src/vllm/v1/worker/gpu/runner.cpp:2603-2604
const bool pure_decode = attn_meta.num_actual_tokens == num_reqs &&
gdn_meta.num_prefill_tokens == 0;
gdn_meta is built only for GDN models (runner.cpp:2475,2479,2534-2538; the
comment at :2475 says "skipped — gdn_meta stays default-empty"), so on every
non-GDN model the only prefill conjunct is inert, and a batch of N one-token
prefill chunks satisfies num_actual_tokens == num_reqs.
Both values ship to every registered model on ModelForwardInput
(runner.cpp:2715 .pure_decode, :2723 .uniform_query_len; field declared
include/vllm/model_executor/models/model_registry.h:582) and gate the decode-graph
drivers — qwen3_moe_registry.cpp:117, deepseek_v2_registry.cpp:116,
glm4_moe_lite_registry.cpp:135, qwen3.cpp:1176.
Size
A per-step has_prefill boolean threaded into both predicates. The signal already
exists in this tree's vocabulary and needs no new plumbing: the prefill-row test is
seq_len <= prefill_len (include/vt/cuda/combine_tokens.h:32-34), and both arrays
are already in scope at the dispatch point — input_batch_.prefill_len and
step.seq_lens are used together at runner.cpp:2433 and :2442, upstream of
:2603 in the same function.
Roughly 20-40 product lines across cudagraph_dispatch.h (a has_prefill parameter
on GraphEligibleQueryLen, mirroring upstream's get_uniform_decode_token_count)
and runner.cpp (compute the reduction, add it to pure_decode), plus tests in
tests/vllm/v1/worker/gpu/test_cudagraph_dispatch.cpp, which already exercises the
shape predicates at :32-51,104-109.
What was NOT determined — read this before sizing the severity
No wrong token was demonstrated. Nothing was executed. Two mitigations argue the
blast radius may be narrower here than upstream:
combine_tokensalready distinguishes prefill rows from decode rows by
seq_len <= prefill_lenindependently of this predicate, so input ids are not
mis-sourced.SpecAsDecodeQueryLenreturns 0 atq == 1
(include/vllm/v1/attention/backend.h:196-205), so the attention-lane
classification is unaffected.
What remains exposed is the decode-graph capture and replay itself taking a step
that contains prefill rows. Whether that is a correctness fault or a shape mismatch
needs a run. The predicate divergence from upstream is unambiguous either way,
and 0a94d85a66 is post-pin: git show 5559679229:vllm/v1/worker/gpu/cudagraph_utils.py
has get_uniform_token_count at :92 with no has_prefill.
Prior triage, and a record defect it carries
.agents/sync/2026-09-01-cdefd9d.md:1952 already ranks this fourth on the #2524
worked list, and its substance and line number are both correct. Its path is
not: it cites include/vllm/v1/worker/gpu/cudagraph_dispatch.h:168, and there is
no include/ copy of that file — it lives at
src/vllm/v1/worker/gpu/cudagraph_dispatch.h.
This is the second time that same wrong path has been found in that record.
.agents/sync/2026-09-03-portq3.md:132-133 corrected it for a different entry and
fixed it only inside its own report. This wave annotates the source.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with src/vllm/v1/worker/gpu/cudagraph_dispatch.h and runner.cpp, especially GraphEligibleQueryLen and the pure_decode calculation around the cited lines. Read the existing predicate cases in tests/vllm/v1/worker/gpu/test_cudagraph_dispatch.cpp, then run those tests and inspect the prefill-row signal used earlier in runner.cpp. Done means prefill-containing batches are excluded by both eligibility predicates and the relevant tests pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- machine-learning
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100