refactor: unify scheduler subclasses into ContinuousScheduler + SchedulerPolicy.#1992
Open
weizhehuang0827 wants to merge 8 commits into
Open
refactor: unify scheduler subclasses into ContinuousScheduler + SchedulerPolicy.#1992weizhehuang0827 wants to merge 8 commits into
weizhehuang0827 wants to merge 8 commits into
Conversation
weizhehuang0827
requested review from
Clement-Wang26,
DongheJin,
DragonFive,
JimHsiung,
Kang-Meng,
RobbieLeung,
XuZhang99,
liujinguang0125,
liutongxuan,
walsonyang,
xiao-yu-chen,
yingxudeng,
yq33victor and
zhang-minchao
as code owners
July 21, 2026 08:00
weizhehuang0827
force-pushed
the
feat/scheduler-refactor
branch
2 times, most recently
from
July 21, 2026 08:16
778a6fc to
b0a1c79
Compare
JimHsiung
previously approved these changes
Jul 21, 2026
weizhehuang0827
force-pushed
the
feat/scheduler-refactor
branch
3 times, most recently
from
July 21, 2026 11:49
a93047a to
a92d79d
Compare
…ler + SchedulerPolicy Replaces ChunkedPrefillScheduler, PrefillOnlyScheduler, MixScheduler with SchedulerPolicy hierarchy (PrefillFirst / DecodeFirst / UnifiedPolicy). Adds chunk_queue for chunked prefill preemption support. Renames urgency_density to multi_slo_and_prio.
- Fix doubled prefill_queue_->size() in is_sole_fresh_request check (was always false, blocking sole-request admission) - Fix doubled prefill_queue_->size() in metrics gauge - Remove dead if (request->offline()) branches that pushed to the same queue unconditionally - Re-enable AdmitsBothWhenCacheHoldsBoth test and provide model_args() and max_seqs_per_batch required by new ContinuousScheduler base
- Restore online/offline queue separation in PDOOCScheduler by adding prefill_queue_offline_ and decode_queue_offline_ members - Fix inverted condition in handle_abnormal_request (was && should be !&&!) - Include all queues in num_waiting_requests metric - Convert ScheduleBudget::exhausted() to free function (struct style rule) - Add missing braces in prefill_first_policy.cpp
Chunked prefill for models with linear attention layers (GDN) requires chunk boundaries aligned to block_size so linear-state checkpoints can be saved/restored at correct positions. The refactor dropped this logic. - Add has_linear_attention_layers field to SchedulerState - Restore block-alignment in allocate_for_prefill when has_linear_attention_layers && enable_prefix_cache - Restore every-chunk re-match in allocate_shared_blocks_for - Fix FakeEngine::model_args() in tests
Old code used `Platform::uses_model_cp_partition() ? 1 : cp_size` to skip chunk alignment when the model handles CP partition internally. The refactor passed options_.cp_size() directly, causing unnecessary alignment on model-partitioned backends.
- Full-prefill path: add allocate_shared_blocks_for call before allocation so prefix cache hits are discovered - Full-prefill CP alignment: add Platform::uses_model_cp_partition() guard (same as chunked path)
…guard - Remove unreachable speculative decode logic from allocate_for_prefill (decode sequences never call this function in the new architecture) - Remove redundant block_size > 0 guard (block_size is always positive)
…tch_mode - Delete use_mix_scheduler gflag entirely (DEFINE, DECLARE, config assign) - Fix resolve_batch_mode: multi_slo_and_prio no longer forces enable_mix_batch=true when MTP/CP is active, so MTP + SLO-aware scheduling correctly uses PrefillFirstPolicy with urgency reorder
weizhehuang0827
force-pushed
the
feat/scheduler-refactor
branch
from
July 22, 2026 06:55
a92d79d to
290a032
Compare
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.
Description
Unify 4 scheduler subclasses (
ContinuousScheduler,ChunkedPrefillScheduler,PrefillOnlyScheduler,MixScheduler) into a singleContinuousSchedulerdriven by aSchedulerPolicyclass hierarchy. Net -1775 lines of code while preserving all scheduling behaviors.Motivation
The old design had 4 scheduler classes with heavily duplicated scheduling logic (block allocation, preemption, decode scheduling, latency-aware budget, prefix cache matching, etc.) spread across each subclass. Any bug fix or new feature had to be applied to all 4 implementations independently. This refactor extracts the common logic into a shared
SchedulerPolicybase class, and lets each policy subclass only define batch assembly order (how prefill and decode requests are prioritized within a step).Architecture (After)
Note:
FixedStepsScheduleris designed for generative recommendation workloads and completely overridesprepare_batch()with its own logic. It does not useSchedulerPolicy— it inheritsContinuousSchedulerfor lifecycle management only.Three-Queue Design
The refactored
ContinuousSchedulermaintains three explicit queues (replacing the old singlewaiting_priority_queue_+ implicitrunning_queue_approach):prefill_queue_chunk_queue_decode_queue_unified_queue_This separation makes the scheduling invariants explicit:
chunk_queue_requests are preemptable (their partial KV can be evicted to make room for decode)decode_queue_requests have committed KV and should not be preempted unless absolutely necessaryprefill_queue_holds fresh arrivals that haven't allocated any blocks yetBatchMode and Policy Selection
resolve_batch_mode()determines theBatchModestruct from user configuration, thencreate_scheduler_policy()selects the concrete policy:The new
--enable_mix_batchgflag (defaulttrue) replaces the old--use_mix_schedulerflag. Users can explicitly set--enable_mix_batch=falseto force exclusive batch mode regardless of other settings.SchedulerState Boundary
Policies operate solely through the
SchedulerStatestruct — they never accessContinuousSchedulermembers directly. This provides a clean boundary:Common Scheduling Primitives (in SchedulerPolicy base)
The following operations are shared across all policies, eliminating the code duplication:
schedule_prefill_from_queue— iterates a prefill/chunk queue, computes token budget per sequence, allocates blocks, handles beam-search expansion, publishes in-batch prefix cacheschedule_decode_from_queue— iterates decode queue, allocates decode blocks, handles latency-aware budget checking, preemption when blocks exhaustedcompute_prefill_tokens— determines per-sequence token count respectingmax_tokens_per_chunk, CP alignment (maybe_align_cp_chunk_tokens), and remaining budgetallocate_for_prefill— allocates KV blocks for a sequence, handling full-prefill vs chunked-prefill paths, GDN linear-state block alignment, prefix cache matchingallocate_shared_blocks_for— prefix cache matching at chunk boundaries (with special every-chunk re-match for linear-state models)Key Invariants Preserved
has_linear_attention_layers && enable_prefix_cache, chunk boundaries are rounded down toblock_sizeso linear-state checkpoints land at recoverable positions. Every chunk re-matches prefix cache.Platform::uses_model_cp_partition()guard: When the model handles CP partition internally, the scheduler skips CP alignment (treats effectivecp_size=1).UnifiedPolicyuses the 3-argkv_cache_manager->allocate(seq, tokens, copy_blocks)whenhost_blocks_factor > 1.0, accounting for H2D copy block budget.PDOOCSchedulermaintains separateprefill_queue_offline_anddecode_queue_offline_queues. A batch consists entirely of online OR entirely of offline requests.remaining_token_budget > min_speculative_tokens_requiredbefore scheduling a decode request.transfer_blockscall: After batch creation,kv_cache_manager->transfer_blocks(batches)is called to drain pending H2D/D2H KV transfers (host prefix cache framework).Future Work
DisaggPDScheduler,DisaggPDChunkedPrefillScheduler,PDOOCScheduler) still contain substantial standalone scheduling logic bypassing theSchedulerPolicyabstraction. A follow-up PR by @JimHsiung will refactor these.SchedulerStateis currently passed by reference to every policy method. A future cleanup will make it a policy member (set once per step) to simplify all signatures.PrefillFirstPolicy::adjust_latency_budget_and_reorderandUnifiedPolicy::get_latency_budget_and_request_order— to be extracted into a shared base method.Related Issues
N/A (architecture refactoring)
Change Type
Pull Request Checklist
PR Title and Commit Messages
<type>: <subject>.Pre-commit Checks
pre-commitby runningpip install pre-commitor an equivalent command.pre-commit install.pre-commit run --all-filesand fixed any reported issues.Self Review
.agents/skills/code-review/references/custom-code-style.md, especially code written or assisted by AI.mainbranch.Build and Test Coverage
python setup.py build testhas passed on a CUDA machine.python setup.py build testhas passed on an NPU machine.python setup.py build testhas passed on an MLU machine.Reviewer Notes
Test Setup
Base commit:
d5ce3007f(before1d0056b23which introduces an unrelatedbatch_input_builderregression)Unit Tests (all PASSED):
scheduler_test(57 tests) — covers all policy paths, PD chunked prefill, disagg PD schedulerServing Tests (TP=2, DeepSeek-V3.2-w8a8-5layer, Ascend 910B npu:12+npu:13):
Each case: smoke test (1 curl request) + load test (ShareGPT dataset, 50 requests, max-concurrency 10, request_rate=inf)
chunked_prefill=falsechunked_prefill=truechunked_prefill=true, MTP=onchunked_prefill=true, multi_slo_and_priochunked_prefill=true, MTP=on, multi_slo_and_priochunked_prefill=true, enable_mix_batch=falsechunked_prefill=false, multi_slo_and_prioAdditional validation (Qwen2-7B, TP=2, max_tokens_per_batch=2500):
PD Disaggregation E2E Test (etcd + xllm_service + prefill + decode):
enable_disagg_pd=true,enable_chunked_prefill=trueKnown Gaps
handle_prefill_requests_impl/handle_decode_requests) — to be addressed in follow-up by @JimHsiungenable_chunked_prefill=falsecrashes on commits after1d0056b23due to an unrelatedbatch_input_builderregression (confirmed on main without this PR)