Repository navigation
[Feat] scheduler: Gate --prefill-decode-interval on running batch size - #44001
Conversation
|
/tag-and-rerun-ci |
|
TODO before this is ready to merge:
|
Add --prefill-decode-interval-min-running-requests (default 0, unchanged behavior). While the running batch holds fewer requests than the threshold, a pending interval is cleared and prefill is admitted every round, so the batch fills up quickly; once it reaches the threshold the interval protects decode as before. The check runs when prefill would be deferred, so a batch that shrinks below the threshold mid-interval resumes admission at once. Under DP attention each rank compares its own running batch; arming still uses the globally synchronized extend flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The parent flag was still on the server_arguments_undocumented.txt allowlist. Lift its help text into server_arguments.mdx and drop it from the list. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
f05c69b to
7c07efe
Compare
|
Done, all three items:
Local check on the rebased tree: For the record, the two red |
|
[by Claude Code] Thanks for this. We hit a related case and want to suggest a small extension, since it touches the same check. The case. When nothing is decoding, there is no decode ITL for the interval to protect, yet with the default We measured this end to end; numbers will come with the follow-up PR. Suggestion. Treat "nothing is decoding" as below the threshold even at the default def _should_defer_prefill(self, running_batch: ScheduleBatch) -> bool:
if self._prefill_decode_interval_remaining == 0:
return False
# With no decode to protect, waiting only idles the GPU between chunks.
# DP keeps the synchronized cadence because peer ranks may still be decoding.
nothing_decodes = running_batch.is_empty() or running_batch.is_prefill_only
if running_batch.batch_size() < self.prefill_decode_interval_min_bs or (
nothing_decodes and not self.require_mlp_sync
):
self._prefill_decode_interval_remaining = 0
return False
self._prefill_decode_interval_remaining -= 1
return TrueWhy it is safe:
We have three unit tests for exactly this, which pass with the change above:
We are happy to push them to this branch or open a small follow-up after this lands, whichever you prefer. |
Resolve conflict with sgl-project#45672 (deleted test_scheduler_prefill_decode_interval.py): keep only the min-bs cases. Append prefill_decode_interval_min_bs to POSITIONAL_FIELD_ORDER required by sgl-project#44991. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Preview approval requiredThis pull request comes from a fork, so Mintlify won't build a preview until someone with write access approves commit |
|
@akhilg-nv Thanks — agreed this is a real bug: the interval counts loop iterations, so with nothing decoding it just idles the GPU between chunks. It's independent of this PR (it reproduces on main with the flag unset), and this PR is meant to keep the default exactly unchanged, so I'd rather not fold a default-on change in here. Please open the small follow-up with your three tests and VR200 numbers once this lands; happy to review. In the meantime |
Motivation
--prefill-decode-interval Nforces N decode-only rounds after every prefill, regardless of how many requests are running. That protects decode ITL once the batch is large, but while the batch is still small it slows its growth: each waiting request waits up to N rounds to be admitted. Throughput-bound deployments (e.g. DeepSeek-V4.1-Flash with--max-running-requests 48 --prefill-decode-interval 4, where TTFT matters less than TPS) want eager admission until the batch is reasonably full, and the interval only after that.Modifications
--prefill-decode-interval-min-bs(default0= current behavior). The interval only applies while the running batch has at least this many requests.Scheduler._should_defer_prefill(running_batch), not when the interval is armed. Below the threshold, any pending interval is cleared and prefill is admitted. So a batch that shrinks below the threshold mid-interval (requests finishing) resumes admission immediately.is_extend_in_batch, and the threshold is compared against each rank's own running batch (no batch size is synced across ranks). A rank below the threshold admits prefill every round. Because that prefill re-arms the interval on every rank, a rank that is still above the threshold keeps deferring until the small rank crosses the threshold or drains its queue, or until its own batch shrinks below the threshold. This is bounded and adds no collective, so no desync is introduced; with the default0nothing changes. Gating on a group-level batch size (e.g. a slot in the DP sync tensor) can be a follow-up if a deployment needs it.server_arguments.mdx, and while there, a row for--prefill-decode-intervalitself (it was still on theserver_arguments_undocumented.txtallowlist; removed from the list).Related: #43844 (chunk-only interval protection) also changes this code path. The two policies compose; whichever lands second needs a trivial rebase.
Accuracy Tests
No model-forward change, only scheduling admission timing.
Unit tests, rebased onto main
e262b44eef1(CPU-only run; the touched tests do not need a GPU):test_scheduler_prefill_decode_interval.py+test_scheduler_hicache_events.py: 11 passed, 7 subtests passed. New cases: below threshold admits and clears; at/above threshold defers; shrinking below threshold mid-interval ends it.test_server_args.py(TestPrepareServerArgs): 22 passed, 25 subtests passed, including the new negative-value rejection.test_server_args_docs_coverage.py: 4 passed. Red check: with--prefill-decode-intervalleft on the allowlist,test_undocumented_allowlist_is_currentfails as expected.Earlier run on an rx devbox at the pre-rebase head (main
d8f85223485):test_scheduler_prefill_decode_interval.py,test_scheduler_hicache_events.py,test_scheduler_chunked_req_gate.py,test_scheduler_dp_converted_batch_full.py,test_server_args_docs_coverage.py,test_server_args.py,test_model_overrides.py: 430 passed, 310 subtests passed.ruff check(pre-commit rule set) +ruff format+isortclean on all changed files.Speed Tests and Profiling
Not yet benchmarked end to end. Expected effect: identical to today when the flag is unset. With it set, at low concurrency the effect is the same as interval 0; near the batch cap it matches the configured interval.
Checklist
🤖 Generated with Claude Code
CI States
Latest PR Test (Base): ⏳ Run #39730845141
Latest PR Test (Extra): ❌ Run #39730845015
Latest PR Test (AMD ROCm 10): ⏳ Run #39730845098