Skip to content

feat(qwen35): allow the auto scheduler policy with stream decode-overlap - #1033

Merged
FeathBow merged 2 commits into
pegainfer-project:mainfrom
CAICAIIs:feat/qwen35-auto-with-overlap
Sep 7, 2026
Merged

feat(qwen35): allow the auto scheduler policy with stream decode-overlap#1033
FeathBow merged 2 commits into
pegainfer-project:mainfrom
CAICAIIs:feat/qwen35-auto-with-overlap

Conversation

@CAICAIIs

@CAICAIIs CAICAIIs commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

Retire the #715 startup rejection that forced --qwen35-scheduler-policy=off whenever --decode-overlap stream was enabled, and allow the two opt-in controls to be combined.

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 on a second stream + decode tick). The rejection was a conservative "untested combination" guard, not a mechanical conflict.

Changes

  • Remove the auto-vs-stream rejection in the Qwen3.5 engine entry (lib.rs) and the CLI validation layer (model_line.rs).
  • Flip model_line::rejects_auto_policy_with_overlap to the acceptance contract (accepts_auto_policy_with_overlap); retire the engine-level rejection probe in lib.rs — the surviving TP / --max-batch <= 32 / TP-rejects-auto guards keep every other fail-closed path.
  • Update 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):

cell base TPOT / ITL p99 ms combo TPOT / ITL p99 ms vLLM 0.27 TPOT / ITL 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 combination dominates every single-lever config (stream alone, auto alone, 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 rises 867 → 1828 ms and 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_gate TP1 2/2 and TP2 (--ignored) 2/2, e2e_scheduler passed.

Claim boundary

Still opt-in (off remains the default for both knobs); stream keeps the --max-batch <= 32 cap 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.

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 FeathBow left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@CAICAIIs

CAICAIIs commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator Author

Done — test_e2e_qwen35_shared_sm_last_decoder now starts a third engine with Qwen35SchedulerPolicy::Auto + Qwen35DecodeOverlap::SharedSm (commit 00f0088) that replays the in-flight-prefill scenario against the same default-Off reference: background decode 512/128, one 8192-token prefill of max_tokens=2 overlapped in-flight, assert_no_generated_event before the overlap bound, then the last decoder is cancelled and the promoted prefill must finish with exactly the reference's greedy tokens — proving the auto policy's reshaped chunk boundaries survive the overlap oracle.

Run on the bench host (1x NVIDIA A100-SXM4-40GB, sm_80, Qwen3.5-4B, PEGAINFER_TEST_MODEL_PATH=/mnt/data/models/Qwen3.5-4B, release build at 00f0088cbd16740c3effc16a7dacbd1b339e979c):

cargo test --release -p pegainfer-qwen35 --features qwen35 \
  --test e2e_scheduler test_e2e_qwen35_shared_sm_last_decoder -- --nocapture

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out; finished in 15.10s

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).

@FeathBow FeathBow left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm :)

@FeathBow
FeathBow merged commit fb16fc1 into pegainfer-project:main Sep 7, 2026
17 checks passed
CAICAIIs added a commit to CAICAIIs/pegainfer that referenced this pull request Sep 8, 2026
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.
CAICAIIs added a commit to CAICAIIs/pegainfer that referenced this pull request Sep 8, 2026
…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).
CAICAIIs added a commit to CAICAIIs/pegainfer that referenced this pull request Sep 8, 2026
…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.
CAICAIIs added a commit to CAICAIIs/pegainfer that referenced this pull request Sep 8, 2026
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>
CAICAIIs added a commit to CAICAIIs/pegainfer that referenced this pull request Sep 8, 2026
…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>
CAICAIIs added a commit to CAICAIIs/pegainfer that referenced this pull request Sep 8, 2026
…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>
CAICAIIs added a commit to CAICAIIs/pegainfer that referenced this pull request Sep 8, 2026
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>
CAICAIIs added a commit to CAICAIIs/pegainfer that referenced this pull request Sep 8, 2026
…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>
CAICAIIs added a commit to CAICAIIs/pegainfer that referenced this pull request Sep 8, 2026
…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>
CAICAIIs added a commit to CAICAIIs/pegainfer that referenced this pull request Sep 10, 2026
…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>
CAICAIIs added a commit to CAICAIIs/pegainfer that referenced this pull request Sep 10, 2026
…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>
CAICAIIs added a commit to CAICAIIs/pegainfer that referenced this pull request Sep 10, 2026
…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>
CAICAIIs added a commit to CAICAIIs/pegainfer that referenced this pull request Sep 10, 2026
…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>
FeathBow pushed a commit that referenced this pull request Sep 10, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants