feat(qwen35): allow the auto scheduler policy with stream decode-overlap - #1033
Conversation
pegainfer-project#715 rejected '--qwen35-scheduler-policy auto --decode-overlap stream' at startup as an untested combination. The two controls are orthogonal in the scheduler: auto only shapes the per-step prefill budget (choose_prefill_budget); stream only changes how a Unified plan executes (async chunk launch + decode tick). On A100-40GB against a vLLM 0.27.0 baseline (1024-token prompts, greedy, zero failed requests), the combination dominates every single-lever config: | cell | base TPOT/p99 ms | combo TPOT/p99 ms | vLLM 0.27 TPOT/p99 ms | | --- | --- | --- | --- | | 1024/256 c8 | 12.23 / 65.5 | 11.94 / 34.2 | 9.88 / 12.0 | | 1024/256 c16 | 14.87 / 81.4 | 14.26 / 36.5 | 11.00 / 83.3 | | QPS16 1024/128 | 36.74 / 101.3 | 20.81 / 41.7 | 23.60 / 93.4 | The trade: open-loop TTFT at QPS16 rises 867 -> 1828 ms and output throughput drops 15%; fixed-concurrency TTFT is within +5% of base. The remaining c8 TPOT/tail gap to vLLM is kernel-side (batch decode slope), not scheduler-side. Rejection retirement is accountable: the two guards that pinned the old behavior flip to the acceptance contract (model_line::accepts_auto_policy_with_overlap) or retire with it (the lib.rs engine-level rejection probe; the CLI acceptance test plus the surviving TP / max-batch / TP-auto guards cover the failure modes it did). Evidence: release build, lib tests 98 passed, hf_golden_gate TP1 2/2 and TP2 (--ignored) 2/2, e2e_scheduler passed, and the HTTP matrix above (raw JSONs on the bench host under ~/ab_cfg_combo/). Signed-off-by: CAICAIIs <3360776475@qq.com>
FeathBow
left a comment
There was a problem hiding this comment.
Thanks, the change looks right to me and the orthogonality argument holds on a trace of the scheduler loop. Since the cited gates never actually run auto + stream (hf_golden_gate skips the scheduler and the overlap E2E starts both engines with Policy::Off), should we add an Auto + SharedSm engine to test_e2e_qwen35_shared_sm_last_decoder that asserts the same greedy tokens as the Off reference, and post that run before merging?
Review follow-up on pegainfer-project#1033: the cited gates never ran the combination — hf_golden_gate skips the scheduler and the overlap e2e started both engines with Policy::Off. Add an Auto + SharedSm engine to test_e2e_qwen35_shared_sm_last_decoder that replays the in-flight-prefill scenario (decode active, prefill overlapped, last decoder cancelled) and asserts the promoted prefill's greedy tokens equal the default-Off reference, so the auto policy's reshaped chunk boundaries are covered by the same oracle as the Off/SharedSm pair. Signed-off-by: CAICAIIs <3360776475@qq.com>
|
Done — Run on the bench host (1x NVIDIA A100-SXM4-40GB, sm_80, Qwen3.5-4B, The other gates from the PR description were re-run on the same tree earlier (lib 98 passed, hf_golden_gate TP1 2/2 + TP2 2/2, e2e_scheduler passed). |
Move the per-rank worker runtime out of the tp_executor.rs God module:
TpWorker + spawn/drop lifecycle, TpStartupGate, the NCCL startup watchdog,
TpWorkerState/TpWorkerPrepared and its command loop (run/respond/
execute_*/precapture/drop), the slot bookkeeping helpers, the shared
decode-row view/sampling helpers, recurrent-capacity math, and the worker
CublasThreadGuard binding. The entry keeps command/reply types, the
executor orchestration (including the precapture sweep), plan validators,
and response validation, reaching worker items via the same `use super::*`
style as scheduler/{backend,tp}. Rebased over pegainfer-project#1004/pegainfer-project#1005/pegainfer-project#1033/pegainfer-project#1038.
Behavior unchanged.
Retire the watchdog disarm wiring test: the happy path is exercised by
every TP e2e startup and the timeout path process-aborts by design, so it
has no assertable in-process failure mode.
…es.rs Move the reply envelope (TpWorkerReply/TpWorkerResponse), bounded response collection, per-command response validation, and the step-timeout/abort helpers out of tp_executor.rs; the three response-contract tests move with them. Behavior unchanged, with two behavior-preserving consolidations in the moved code: validate_exact_rank_responses folds its push loop into an iterator pipeline, and the three rank-0 payload validators (prefill / decode / unified) collapse into one validate_primary_responses helper over payload extractors, keeping the exact prior error messages. Rebased over pegainfer-project#1004/pegainfer-project#1005/pegainfer-project#1033/pegainfer-project#1038 (the precapture sweep keeps using validate_ack_responses, which stays production-visible).
…emit,telemetry}.rs Continue the scheduler.rs slim-down along the seams backend.rs/tp.rs already established: step drivers (batch prefill, overlap launch, unified step, pure decode, token dispatch/retirement, chunk vocabulary) move to steps.rs, terminal shutdown / closed-request pruning / rejection fan-out move to emit.rs, and the PEGAINFER_ITL_DEBUG telemetry trio moves to telemetry.rs. scheduler/mod.rs keeps the request/artifact types, the entry points (including the pegainfer-project#1033 policy-aware start), load publishing, and the main loop. Behavior unchanged; moved items gain pub(super) only where the loop, backend, tp, emit siblings, or scheduler tests name them.
Move the per-rank worker runtime out of the tp_executor.rs God module:
TpWorker + spawn/drop lifecycle, TpStartupGate, the NCCL startup watchdog,
TpWorkerState/TpWorkerPrepared and its command loop (run/respond/
execute_*/precapture/drop), the slot bookkeeping helpers, the shared
decode-row view/sampling helpers, recurrent-capacity math, and the worker
CublasThreadGuard binding. The entry keeps command/reply types, the
executor orchestration (including the precapture sweep), plan validators,
and response validation, reaching worker items via the same `use super::*`
style as scheduler/{backend,tp}. Rebased over pegainfer-project#1004/pegainfer-project#1005/pegainfer-project#1033/pegainfer-project#1038.
Behavior unchanged.
Retire the watchdog disarm wiring test: the happy path is exercised by
every TP e2e startup and the timeout path process-aborts by design, so it
has no assertable in-process failure mode.
Signed-off-by: CAICAIIs <3360776475@qq.com>
…es.rs Move the reply envelope (TpWorkerReply/TpWorkerResponse), bounded response collection, per-command response validation, and the step-timeout/abort helpers out of tp_executor.rs; the three response-contract tests move with them. Behavior unchanged, with two behavior-preserving consolidations in the moved code: validate_exact_rank_responses folds its push loop into an iterator pipeline, and the three rank-0 payload validators (prefill / decode / unified) collapse into one validate_primary_responses helper over payload extractors, keeping the exact prior error messages. Rebased over pegainfer-project#1004/pegainfer-project#1005/pegainfer-project#1033/pegainfer-project#1038 (the precapture sweep keeps using validate_ack_responses, which stays production-visible). Signed-off-by: CAICAIIs <3360776475@qq.com>
…emit,telemetry}.rs Continue the scheduler.rs slim-down along the seams backend.rs/tp.rs already established: step drivers (batch prefill, overlap launch, unified step, pure decode, token dispatch/retirement, chunk vocabulary) move to steps.rs, terminal shutdown / closed-request pruning / rejection fan-out move to emit.rs, and the PEGAINFER_ITL_DEBUG telemetry trio moves to telemetry.rs. scheduler/mod.rs keeps the request/artifact types, the entry points (including the pegainfer-project#1033 policy-aware start), load publishing, and the main loop. Behavior unchanged; moved items gain pub(super) only where the loop, backend, tp, emit siblings, or scheduler tests name them. Signed-off-by: CAICAIIs <3360776475@qq.com>
Move the per-rank worker runtime out of the tp_executor.rs God module:
TpWorker + spawn/drop lifecycle, TpStartupGate, the NCCL startup watchdog,
TpWorkerState/TpWorkerPrepared and its command loop (run/respond/
execute_*/precapture/drop), the slot bookkeeping helpers, the shared
decode-row view/sampling helpers, recurrent-capacity math, and the worker
CublasThreadGuard binding. The entry keeps command/reply types, the
executor orchestration (including the precapture sweep), plan validators,
and response validation, reaching worker items via the same `use super::*`
style as scheduler/{backend,tp}. Rebased over pegainfer-project#1004/pegainfer-project#1005/pegainfer-project#1033/pegainfer-project#1038.
Behavior unchanged; the watchdog disarm test moves into worker.rs with
its implementation.
Signed-off-by: CAICAIIs <3360776475@qq.com>
…es.rs Move the reply envelope (TpWorkerReply/TpWorkerResponse), bounded response collection, per-command response validation, and the step-timeout/abort helpers out of tp_executor.rs; the three response-contract tests move with them. Behavior unchanged, with two behavior-preserving consolidations in the moved code: validate_exact_rank_responses folds its push loop into an iterator pipeline, and the three rank-0 payload validators (prefill / decode / unified) collapse into one validate_primary_responses helper over payload extractors, keeping the exact prior error messages. Rebased over pegainfer-project#1004/pegainfer-project#1005/pegainfer-project#1033/pegainfer-project#1038 (the precapture sweep keeps using validate_ack_responses, which stays production-visible). Signed-off-by: CAICAIIs <3360776475@qq.com>
…emit,telemetry}.rs Continue the scheduler.rs slim-down along the seams backend.rs/tp.rs already established: step drivers (batch prefill, overlap launch, unified step, pure decode, token dispatch/retirement, chunk vocabulary) move to steps.rs, terminal shutdown / closed-request pruning / rejection fan-out move to emit.rs, and the PEGAINFER_ITL_DEBUG telemetry trio moves to telemetry.rs. scheduler/mod.rs keeps the request/artifact types, the entry points (including the pegainfer-project#1033 policy-aware start), load publishing, and the main loop. Behavior unchanged; moved items gain pub(super) only where the loop, backend, tp, emit siblings, or scheduler tests name them. Signed-off-by: CAICAIIs <3360776475@qq.com>
…es.rs Move the reply envelope (TpWorkerReply/TpWorkerResponse), bounded response collection, per-command response validation, and the step-timeout/abort helpers out of tp_executor.rs; the three response-contract tests move with them. Behavior unchanged, with two behavior-preserving consolidations in the moved code: validate_exact_rank_responses folds its push loop into an iterator pipeline, and the three rank-0 payload validators (prefill / decode / unified) collapse into one validate_primary_responses helper over payload extractors, keeping the exact prior error messages. Rebased over pegainfer-project#1004/pegainfer-project#1005/pegainfer-project#1033/pegainfer-project#1038 (the precapture sweep keeps using validate_ack_responses, which stays production-visible). Signed-off-by: CAICAIIs <3360776475@qq.com>
…emit,telemetry}.rs Continue the scheduler.rs slim-down along the seams backend.rs/tp.rs already established: step drivers (batch prefill, overlap launch, unified step, pure decode, token dispatch/retirement, chunk vocabulary) move to steps.rs, terminal shutdown / closed-request pruning / rejection fan-out move to emit.rs, and the PEGAINFER_ITL_DEBUG telemetry trio moves to telemetry.rs. scheduler/mod.rs keeps the request/artifact types, the entry points (including the pegainfer-project#1033 policy-aware start), load publishing, and the main loop. Behavior unchanged; moved items gain pub(super) only where the loop, backend, tp, emit siblings, or scheduler tests name them. Signed-off-by: CAICAIIs <3360776475@qq.com>
…es.rs Move the reply envelope (TpWorkerReply/TpWorkerResponse), bounded response collection, per-command response validation, and the step-timeout/abort helpers out of tp_executor.rs; the three response-contract tests move with them. Behavior unchanged, with two behavior-preserving consolidations in the moved code: validate_exact_rank_responses folds its push loop into an iterator pipeline, and the three rank-0 payload validators (prefill / decode / unified) collapse into one validate_primary_responses helper over payload extractors, keeping the exact prior error messages. Rebased over pegainfer-project#1004/pegainfer-project#1005/pegainfer-project#1033/pegainfer-project#1038 (the precapture sweep keeps using validate_ack_responses, which stays production-visible). Signed-off-by: CAICAIIs <3360776475@qq.com>
…emit,telemetry}.rs Continue the scheduler.rs slim-down along the seams backend.rs/tp.rs already established: step drivers (batch prefill, overlap launch, unified step, pure decode, token dispatch/retirement, chunk vocabulary) move to steps.rs, terminal shutdown / closed-request pruning / rejection fan-out move to emit.rs, and the PEGAINFER_ITL_DEBUG telemetry trio moves to telemetry.rs. scheduler/mod.rs keeps the request/artifact types, the entry points (including the pegainfer-project#1033 policy-aware start), load publishing, and the main loop. Behavior unchanged; moved items gain pub(super) only where the loop, backend, tp, emit siblings, or scheduler tests name them. Signed-off-by: CAICAIIs <3360776475@qq.com>
…ap (#1034) * feat(qwen35): keep the auto prefill budget running under stream overlap With 'auto + stream' the finishing-window full deferral (a decode-priority tick when an active request has <= 4 tokens left) no longer buys decode latency: the overlapped chunk already runs off the decode step, so the deferral only delays prefill. Skip it when overlap is enabled and keep the capped FIFO-front budget. A100-40GB, 1024-token prompts vs the merged combination ('70a600b7'+#1033): | QPS16 1024/128 | TPOT | ITL p99 | TTFT | out tok/s | | --- | --- | --- | --- | --- | | combo (window kept) | 20.81 | 41.71 | 1828 | 873 | | combo, overlap-aware | 23.77 | 41.65 | 1264 | 992 | | vLLM 0.27.0 | 23.60 | 93.43 | 218 | 1404 | c8/c16 unchanged within run noise (p99 34.5/36.4 ms). The redundant QPS16 TPOT win (already parity with vLLM) is traded for 31% TTFT and 14% throughput at identical tail. The remaining TTFT/throughput gap to vLLM is total-throughput-bound (kernel-side), not schedulable. Evidence: lib tests 98 passed (incl. the new adaptive_prefill_budget_keeps_prefill_running_under_stream_overlap), hf_golden_gate TP1 2/2 + TP2 (--ignored) 2/2, e2e_scheduler passed; HTTP cells above with zero failed requests (raw JSONs under ~/ab_cfg_combo2/ on the bench host). Signed-off-by: CAICAIIs <3360776475@qq.com> * docs(qwen35): bump adaptive policy freshness stamp to 2026-09 Review follow-up on #1034: the stream-overlap finishing-window change and its A/B evidence update this doc's guidance, so the Last touched stamp must move with it (AGENTS.md docs rule). Signed-off-by: CAICAIIs <3360776475@qq.com> * refactor(qwen35): hoist the overlap probe out of the scheduler loop Review pass on #1034: the Single-backend overlap probe ran three times per step (budget choice, ITL plan kind, Unified dispatch) even though the backend is fixed for the loop's lifetime. Hoist one binding, and mark the policy-doc measurement as a single run. Signed-off-by: CAICAIIs <3360776475@qq.com> * docs(qwen35): bind the deferral-reclaim A/B to its measured source Address review on #1034: the finishing-window deferral reclaim numbers (TTFT 1828 -> 1264 ms, output 873 -> 992 tok/s) were measured on fb16fc1 + this change, so record that revision separately from the 70a600b-based combination table in unified-prefill-overlap.md. Rework the stream-overlap budget test's tail assertion per review: feed the same non-empty prefill queue through build_next_plan and assert the tick stays a unified prefill+decode plan, instead of asserting a decode-only tick on a separate empty queue whose message contradicted the case's kept-budget expectation. adaptive-scheduler-policy.md's Last touched already reads 2026-09 (8919e81). Signed-off-by: CAICAIIs <3360776475@qq.com> --------- Signed-off-by: CAICAIIs <3360776475@qq.com>
Summary
Retire the #715 startup rejection that forced
--qwen35-scheduler-policy=offwhenever--decode-overlap streamwas enabled, and allow the two opt-in controls to be combined.The two controls are orthogonal in the scheduler:
autoonly shapes the per-step prefill budget (choose_prefill_budget);streamonly changes how a Unified plan executes (async chunk launch on a second stream + decode tick). The rejection was a conservative "untested combination" guard, not a mechanical conflict.Changes
auto-vs-streamrejection in the Qwen3.5 engine entry (lib.rs) and the CLI validation layer (model_line.rs).model_line::rejects_auto_policy_with_overlapto the acceptance contract (accepts_auto_policy_with_overlap); retire the engine-level rejection probe inlib.rs— the surviving TP /--max-batch <= 32/ TP-rejects-autoguards keep every other fail-closed path.docs/models/qwen35/unified-prefill-overlap.md: the guard list no longer names the combination, and the TL;DR records the measured effect.Evidence (1x A100-40GB, upstream/main
70a600b7+ this change, vLLM 0.27.0 baseline)1024-token prompts, greedy,
vllm bench serve, zero failed requests; single run per cell (gap-map grade):The combination dominates every single-lever config (
streamalone,autoalone, fixed 512-token chunks) and beats vLLM 0.27 on c16 ITL p99 and on QPS16 TPOT and ITL p99. The trade: open-loop TTFT at QPS16 rises867 → 1828 msand QPS16 output throughput drops 15%; fixed-concurrency TTFT stays within +5% of base. Individual-lever matrix and analysis: #727 (comment).Correctness on the combination: release lib tests 98 passed,
hf_golden_gateTP1 2/2 and TP2 (--ignored) 2/2,e2e_schedulerpassed.Claim boundary
Still opt-in (
offremains the default for both knobs);streamkeeps the--max-batch <= 32cap and single-GPU-only restrictions. Single-run numbers on one GPU — not a parity or production-readiness claim. The remaining c8 TPOT/tail distance to vLLM is kernel-side (batch decode slope), not addressed here.