Repository navigation
[Fix] scheduler: Publish Mamba prefill cache updates before preparing the next batch - #39765
yuchenwang3 wants to merge 2 commits into
Conversation
Origin: sgl-project#39765 "[Fix] Publish Mamba prefill cache updates before preparing the next batch" Upstream commit: - f273a50 Fix Mamba prefill cache publication before overlap batch preparation Manual merge notes: - Cherry-pick auto-merged scheduler.py cleanly; the upstream hunk applies verbatim next to the fork-local opportunistic-flush block, which is preserved (the flush still runs at the disable_overlap/processed-prefill sync boundary). - No fork-local scheduler logic was displaced: `_has_pending_mamba_cache_update` only reads req.kv / batch state that the fork shares with upstream. - New test registered under test/registered/unit/managers/ (valid taxonomy).
Origin: sgl-project#39765 "[Fix] Publish Mamba prefill cache updates before preparing the next batch" Upstream commit: - f273a50 Fix Mamba prefill cache publication before overlap batch preparation Manual merge notes: - Cherry-pick auto-merged scheduler.py cleanly; the upstream hunk applies verbatim next to the fork-local opportunistic-flush block, which is preserved (the flush still runs at the disable_overlap/processed-prefill sync boundary). - No fork-local scheduler logic was displaced: `_has_pending_mamba_cache_update` only reads req.kv / batch state that the fork shares with upstream. - New test registered under test/registered/unit/managers/ (valid taxonomy).
Origin: sgl-project#39765 "[Fix] Publish Mamba prefill cache updates before preparing the next batch" Upstream commit: - f273a50 Fix Mamba prefill cache publication before overlap batch preparation Manual merge notes: - Cherry-pick auto-merged scheduler.py cleanly; the upstream hunk applies verbatim next to the fork-local opportunistic-flush block, which is preserved (the flush still runs at the disable_overlap/processed-prefill sync boundary). - No fork-local scheduler logic was displaced: `_has_pending_mamba_cache_update` only reads req.kv / batch state that the fork shares with upstream. - New test registered under test/registered/unit/managers/ (valid taxonomy).
|
@merrymercy Could you review the Mamba prefill cache-publication ordering fix, or help route it to the appropriate scheduler owner? The PR includes the controlled regression, 27 passing tests plus 16 subtests, and a full-server SM90 smoke, with the limitations recorded in the description. The CI gate for |
|
@hnyls2002 Could you review the Mamba prefill cache-publication ordering? The fix processes the pending prefill before the next batch captures its KV slots. Rebased onto |
f273a50 to
0b8607e
Compare
|
@xiezhq-hermann Could you take a look at the Mamba prefill cache ordering here? The pending prefill is processed before the next batch captures its KV slots. The current head still needs CI authorization. |
…refill cache updates before preparing the next batch Wrong-output class (family of stack entry 196/sgl-project#39209). With `extra_buffer`(+lazy) and overlap scheduling, `event_loop_overlap` can call `get_next_batch_to_run()` — which captures `req_to_token` for the next batch — BEFORE the previous prefill's radix insertion (still sitting in `result_queue`) has replaced the request's private KV slots with canonical tree slots and freed the duplicates. The first dependent decode/verify step then reads a prefix mapping that no longer exists. Fix (upstream): before preparing the next batch, ask `_has_pending_mamba_cache_update()` whether the head of `result_queue` is an extend batch with a tracked, unfinished, non-retracted, non-skip-insert request; if so, `pop_and_process()` first and skip the overlap path for that iteration (`disable_overlap_for_batch` forced False, the "process the last batch" branch skipped since the queue was already drained). Gate is deliberately conservative — returns False (behaviour byte-identical to today) for DP/MLP-sync ranks (they need a rank-agreement decision the PR does not make), for `tree_cache.disable`, and for caches without `enable_mamba_extra_buffer`. Applied cleanly on our tree (`git apply -3`, scheduler.py region at event_loop_overlap; our prewarm/engine-init lines untouched). Brings upstream's CPU test test/registered/unit/managers/test_mamba_prefill_cache_boundary.py (5P/12sub, mocks the loop via an unbound `Scheduler.event_loop_overlap`). NEEDS A GPU A/B: this adds a synchronisation point (an early `pop_and_process()` + result drain) into the overlap hot path for hybrid `extra_buffer` prefills, so throughput/TTFT of prod Qwen3.8-Flash-Next must be measured after restart; rollback = revert this commit. (cherry picked from commit 93ea0732ba88fd6fa3d5272495878dfcae274b71)
…entries 122,198,200,211,215,220,267,268,305 Re-plants the scheduler.py fork-hunks (replant/...managers__scheduler.py.fork-hunks.patch) onto the post-sgl-project#46977 admission structure. tp_worker marker consumed (15/23 B4 markers still on the tree; per-file counts, damage-state from entry 310's map). PER-HUNK DECISIONS (all VERIFIED against the merged tree, not taken on faith): - entry 122 (sgl-project#40458): grammar-sync overlap disable RE-PLANTED for mixed mode (is_decode() OR is_mixed()) + the fork's trimmed comments; upstream's host-draft-only arm was the pre-sgl-project#40458 form (origin/main verified: absent). - entry 220 (sgl-project#39765, OPEN port): _has_pending_mamba_cache_update + the early pop_and_process/short-circuited disable-overlap RE-PLANTED into event_loop_overlap; the require_mlp_sync/disable/extra-buffer gate verbatim. - entry 198 (sgl-project#37612): THE CATCHER — B3-5 landed the raiser; the try/except MambaCowAllocError around init_next_round_input (res=NO_TOKEN, request stays queued; the revert block + unconditional alloc_group_end were already in the merged tree = the no-leak halves VERIFIED PRESENT) RE-PLANTED with the import. COW component test re-anchored to the #46970x kv_storage seam the B3-5 replant moved _state() behind (stub was the pre-split mamba_allocator shape). - entry 268 (sgl-project#46643, OPEN port -- VERIFIED NEVER MERGED: git log 12dc623..2a6c204 --grep 46643 = ZERO; only sgl-project#45491/sgl-project#45435/sgl-project#45472/sgl-project#45670 of the briefed merged-list arrived in-range): the UNCONDITIONAL batch_is_full clear RE-PLANTED on the scheduler side, subsuming the entry-198 hybrid-SSM arm and the priority-preemption/is_empty arms (policy side lands in B4-3). - entry 267 (sgl-project#46496): retract_decode new_token_ratio FLOOR clamp RE-PLANTED. - entry 211: triton_load_watch.prewarm_device_loads(self.model_worker) line RE-PLANTED between install() and mark_serving_started (the utils-side function survived the merge; only the call site was dropped). - entry 200 (sgl-project#43527) CAPACITY PAIR RE-PLANTED: model_runner .logical_max_total_num_tokens passes allocator= again (the configurator's PageInterleavePoolAllocator -> allocator.size branch SURVIVED the merge byte-identical, so the merged tree had the fork predicate with upstream's consumer-side multiplications = DOUBLE-COUNT); the three scheduler * kv_shard_widening multipliers back to the fork form (pool-stats shape, load_inquirer, and the init_req one-page-per-shard subtraction); tp_worker drops page_interleave_shard_size x + import. Upstream's invariant-checker hunk: OBSOLETE (the constructor no longer takes max_total_num_tokens -- absorbed by refactor). - entry 215 (sgl-project#44351/sgl-project#44401/sgl-project#44420): VERIFIED ALREADY PRESENT at the merge (filter_batch-every-step + reset ride, num_held_rows, budget_state FIRST statement, PD-gate deletion all byte-compare to the fork form) -- DEDUPED, no edit. Entry 107's local early-revision of sgl-project#40940 stays superseded (entry 215 rule), and its re-plant here would double-count. - entry 220's/305's pause-window busy accounting + write_fence_stream + is_empty arm: VERIFIED PRESENT (entry 305 landed patch-identical via sgl-project#46127; the fence assignment is the B3-2 controller half) -- DEDUPED. TEST FIXTURE RE-ANCHORS (not B2's: no markers, pure new-upstream-contract drift, per the entry-198 fixture-adaptation precedent): the two hybrid-ssm admission stubs gain disagg_prefill_bootstrap_queue=None (upstream's routing-key bootstrap_queue arg in calc_priority arrived in-range). PRUNE vs BREAKAGE (managers −523sub audit): the three managers test files vanished in-range (test_schedule_policy_dfs_weight, test_scheduler_batch_is _full_latch, test_uno_token_accounting) were ALL deleted by upstream's OWN sgl-project#45672 prune (3438ed6). Two are byte-identical upstream copies = zero fork delta. The third carried a 3-line fork comment/stub delta whose SEMANTICS the re-planted unconditional clear pins: its case was the is_empty-arm mirror of test_scheduler_hybrid_ssm_admission_latch (present, green, hybrid_swa/non-hybrid arms included) -- NOT re-homed, recorded. ACTUALS (damage -> after, CVD=9, prod venv, PYTHONPATH=python, per-file): test_scheduler_hybrid_ssm_admission_retry 4F -> ** 4P ** test_scheduler_hybrid_ssm_admission_latch 3F -> ** 4P ** test_mamba_prefill_cache_boundary 2F/12subF -> ** 5P/12sub EXACT (220 pin) ** test_scheduler_retract_ratio 1F -> ** 2P EXACT (267 pin) ** test_scheduler_init_req_max_new_tokens 1F -> ** 10P/6sub ** test_scheduler_grammar_mixed_tail_sync 1F -> ** 4P ** adjacent scheduler-stub files (held_row, running_batch_finished, pause_generation, disagg_idle, dp_converted, hicache_events) 56P/64sub UNMOVED -- no regression from the clear/catcher/capacity edits. Lint: ruff format + F821/E9 clean on all five files; imports OK (no GPU execution, no push). Co-Authored-By: OpenCode <noreply@opencode.ai>
|
Merged main at |
Motivation
With Mamba
extra_buffercaching and overlap scheduling, the next decode/verify batch can capturereq_to_tokenbefore the previous prefill result publishes its cache insertion. That insertion can replace freshly computed KV slots with the tree's canonical slots and free the duplicates. The first dependent step then uses a different prefix mapping from later steps.Processing the result after
get_next_batch_to_run()is too late: batch preparation has already captured the mapping.Related to #38140, which addresses the same ordering issue for plain radix caches and explicitly leaves Mamba cache timing unchanged. This patch covers tracked Mamba prefills.
Modifications
Latest validation (November 5)
Merged main
c03b6b3dfinto head1ccea14a1d9d77b959450e55a9da357652d2c001, preserving branch history. The conflict resolution uses upstream's early-result processing path for pending tracked Mamba cache publications and retains its opportunistic unified-memory flush. The regression fixture follows the current scheduler iteration and early-processing interfaces.All 5 Mamba cache-boundary tests pass in a CPU source-method harness using the actual scheduler loop and predicates. The harness avoids optional server/GPU imports; normal test collection in the local environment stops at missing
starlette. All applicable changed-file pre-commit hooks andgit diff --checkpass. No GPU or server run was performed on this head.Previous validation (October 3)
Rebased onto main
10edb05e72afb79ecfdf7dbe7603f90ba05ad3eeat0b8607ec03cb10d6fb0afee433d1a879667ecb90. This includes the required CI minimum base8854857a991e06b3ef9c4caeae34c94fc3588336.The Mamba prefill-publication, batch-result boundary, and chunked-abort CPU suites pass all 10 tests, including the existing subtests. All applicable changed-file pre-commit hooks pass. The earlier SM90 server smoke is historical; it has not been rerun on this rebased head.
Previous accuracy tests (pre-rebase revision)
Base:
9c8d4641ff6415a640352a950b98e312479ddf6a.The new regression exercises the real overlap loop with a controlled cache publisher. On unmodified main it captures the old slots before publication, including when the existing post-selection synchronization is enabled. It also launches a dependent batch before handling a prefill that finishes or fails. These checks pass with the patch.
Source tested before this rebase on an SM90 CUDA GPU with Python 3.12, PyTorch 2.13.0+cu130, Triton 3.7.1, and sglang-kernel 0.4.7 (also passed on PyTorch 2.11 / Triton 3.6):
27 passed, 16 subtests passed. All applicable pre-commit hooks and
git diff --checkpass.Full-server smoke with
Accio-Lab/occamy-1.0, BF16, FA3,extra_buffer, TP=1, and overlap enabled: both main and the patch complete cold/warm requests and three fixed-length 128-token requests. The natural-stop case returns the same 9 tokens in all four cold/warm runs, with 384 cached prompt tokens on a hit. This single-request smoke checks for regressions; the controlled scheduler test above reproduces the mapping-publication failure.Cold/warm logprobs are not bit-exact in this configuration (maximum difference 0.016763 on both main and the patch). This PR does not claim general cold/warm numerical determinism.
Speed Tests and Profiling
Same shared SM90 GPU and server configuration, 422 prompt tokens, 128 generated tokens, one cold-cache request followed by two warm-cache requests:
These are small-sample smoke timings on a shared GPU, not evidence of a speedup or a concurrent-serving throughput benchmark. The fix synchronizes at tracked Mamba prefill publication; the steady decode overlap path remains unchanged.
Checklist
CI States
Latest PR Test (Base): ❌ Run #42095571707
Latest PR Test (Extra): ❌ Run #42095571740
Latest PR Test (AMD ROCm 10): ⏳ Run #42095571365
Tip
Base CI only covers the core cases, and its workload may not exercise your change. Use
/rerun-testto run the tests your change touches, or copy this prompt for an agent:AI assistance: OpenAI Codex performed this rebase and local validation.