[Bugfix] Max-load throughput cliff when max_num_seqs is not a multiple of 8 - #57355
Merged
benchislett merged 3 commits intoSep 17, 2026
Merged
Conversation
Co-authored-by: OpenAI Codex <noreply@openai.com> Signed-off-by: Andy Lo <andy@mistral.ai>
Co-authored-by: OpenAI Codex <noreply@openai.com> Signed-off-by: Andy Lo <andy@mistral.ai>
max_num_seqs is not a multiple of 8
andylolu2
marked this pull request as ready for review
September 17, 2026 11:35
andylolu2
requested review from
ProExpertProg,
WoosukKwon,
houseroad,
mgoin,
robertgshaw2-redhat,
tlrmchlsmth,
yewentao256 and
youkaichao
as code owners
September 17, 2026 11:35
benchislett
enabled auto-merge (squash)
September 17, 2026 14:56
|
✅ @andylolu2, CI is now available for this PR.
|
Member
|
/ci run |
|
❌ This PR is 17 commits behind upstream |
Contributor
Author
|
/ci run |
|
✅ Triggered Buildkite CI #89668 for commit |
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.
Why
By default, cudagraphs are captured in (mostly) multiples of 8. When
max_num_seqsis not set to a multiple of 8, it has the unfortunate side effect that the last bucket of batch sizes doesn't fall into a FULL cuda graph. For example, withmax_num_seqs=100(and no spec decode), we get:This is quite bad practically, because servers can get stuck in the slow PIECEWISE mode when it's overloaded (num_running_reqs = max_num_seqs).
In a TP4/B200 reproduction this was not a small boundary effect: batch 97 started behind batch 96 and progressively slowed over a 2,000-token decode, eventually falling below 1,000 tok/s/server. Adding the legal FULL-100 shape removed the cliff.
Fix
This is actually already correctly handled when spec decode is turned on by #50488, but not when spec dec is off. The fix is to simply piggy-back off the PR's changes: when the inferred default is used without speculative decoding, add
max_num_seqsas the widest uniform-decode shape if it fits under the existing capture ceiling:For the example above, the ladder becomes:
The unchanged graph manager then produces FULL descriptors through 100, while later sizes remain PIECEWISE-only.
Benchmark evidence
The reproducer used Mistral-Small-4-119B-2603 with TP4 on B200,
max_num_seqs=100, logprobs enabled, 2,000 generated tokens, and a production-shaped prompt distribution with roughly equal mass around 8k, 16k, 32k, 64k, and 127k input tokens.Batch 97 improved 2.55× (+154.6%) and became comparable to batch 96. Before the fix its server-throughput windows decayed from 2,922 tok/s near output token 302 to 829 tok/s in the final window. After the fix they stayed flat at 3,923, 3,890, 3,862, and 3,824 tok/s. Total time fell from 129.72 s to 52.41 s.
An Nsight trace showed that the PIECEWISE path accumulated tensor-parallel rank drift: attention remained around 0.58 ms, while graph-launch gaps grew from about 0.69 ms to 5.5 ms and MNNVL all-reduce wait medians grew from about 10 µs to 3.6–4.7 ms. This PR avoids that path for a legal decode batch; it does not attempt to independently repair PIECEWISE rank drift.
Test results
Revert check: with the production hunk reverted while retaining the new test, both cases fail (
2 failed): neither 100 nor 101 is present in the inferred capture sizes.No model evaluation was run because this changes only which execution graph serves an already-valid shape; model operations and outputs are unchanged.
Not a duplicate
I searched open PRs for
max_num_seqs,cudagraph,capture sizes,off-stride, anduniform decode. The nearby work addresses different contracts:uniform_decode_sizesfor speculative decoding and deliberately kept non-speculative defaults unchanged. This PR closes that remaining non-speculative boundary hole using its abstraction.max_cudagraph_capture_size; here that value is 200, so it still does not add the required 100-request boundary.No open PR covers an off-stride, non-speculative
max_num_seqsboundary.Reviewer guide
vllm/config/vllm.py: the two-line behavior change reuses the uniform-decode capture-size path; the nearby comment is updated to match its broadened scope.tests/compile/test_config.py: one new parameter exercises the existing capture-boundary contract atmax_num_seqs=100.There are no moves, generated files, or mechanical propagation changes.
AI assistance disclosure
OpenAI Codex assisted with the investigation, patch, tests, duplicate-work audit, and PR write-up. I reviewed every changed line and the benchmark evidence before submission.