Skip to content

[Fix] scheduler: Publish Mamba prefill cache updates before preparing the next batch - #39765

Open
yuchenwang3 wants to merge 2 commits into
sgl-project:mainfrom
yuchenwang3:fix/mamba-prefill-cache-boundary-20260916
Open

yuchenwang3 wants to merge 2 commits into
sgl-project:mainfrom
yuchenwang3:fix/mamba-prefill-cache-boundary-20260916

Conversation

@yuchenwang3

@yuchenwang3 yuchenwang3 commented Sep 16, 2026 •

Copy link
Copy Markdown

Motivation

With Mamba extra_buffer caching and overlap scheduling, the next decode/verify batch can capture req_to_token before 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

  • Process a pending tracked Mamba prefill result before selecting/preparing the next batch.
  • Consume each result once and retain the opportunistic unified-memory flush at the synchronization boundary.
  • Keep overlap for untracked prefills, disabled caches, and decode requests in mixed batches.
  • Leave DP/MLP-sync scheduling unchanged; that path needs a collective decision and is outside this patch's scope.

Latest validation (November 5)

Merged main c03b6b3df into head 1ccea14a1d9d77b959450e55a9da357652d2c001, 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 and git diff --check pass. No GPU or server run was performed on this head.

Previous validation (October 3)

Rebased onto main 10edb05e72afb79ecfdf7dbe7603f90ba05ad3ee at 0b8607ec03cb10d6fb0afee433d1a879667ecb90. This includes the required CI minimum base 8854857a991e06b3ef9c4caeae34c94fc3588336.

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

python -m pytest \
  test/registered/unit/managers/test_mamba_prefill_cache_boundary.py \
  test/registered/unit/managers/test_scheduler_decision_batch_params.py \
  test/registered/unit/managers/test_batch_result_processor_mamba_boundary.py \
  test/registered/unit/managers/test_scheduler_chunked_abort_race.py \
  test/registered/unit/mem_cache/test_mamba_unittest.py -q

27 passed, 16 subtests passed. All applicable pre-commit hooks and git diff --check pass.

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:

Request latency Main Patch
Cold 0.852 s 0.807 s
Warm 1 0.828 s 0.765 s
Warm 2 0.797 s 0.764 s

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

  • Format code with pre-commit.
  • Add registered regression tests.
  • Follow the SGLang code style.
  • Provide focused correctness tests and end-to-end smoke timings, with the limits stated above.

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-test to run the tests your change touches, or copy this prompt for an agent:

Find the registered tests this PR affects and run them with /rerun-test, following https://github.com/sgl-project/sglang/blob/main/.agents/skills/rerun-test/SKILL.md

AI assistance: OpenAI Codex performed this rebase and local validation.

Leoyzen pushed a commit to Leoyzen/sglang that referenced this pull request Sep 17, 2026
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).
Leoyzen pushed a commit to Leoyzen/sglang that referenced this pull request Sep 17, 2026
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).
Leoyzen pushed a commit to Leoyzen/sglang that referenced this pull request Sep 18, 2026
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).
@yuchenwang3

Copy link
Copy Markdown
Author

@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 f273a50b44db1be65bb337dd81e2cddee1db3f10 reports a missing run-ci label; could a maintainer authorize CI if the scope looks suitable?

@yuchenwang3

yuchenwang3 commented Oct 3, 2026 •

Copy link
Copy Markdown
Author

@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 10edb05e72 at 0b8607ec03; the three focused CPU suites pass 10 tests, and changed-file pre-commit checks pass. The earlier SM90 smoke was not rerun on this revision. The minimum-base requirement is satisfied; could you authorize CI?

@yuchenwang3
yuchenwang3 force-pushed the fix/mamba-prefill-cache-boundary-20260916 branch from f273a50 to 0b8607e Compare October 3, 2026 23:05

Copy link
Copy Markdown
Author

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

@xiezhq-hermann xiezhq-hermann added the run-ci CI: run the baseline test suite on this PR label Oct 13, 2026
detain added a commit to detain/sglang that referenced this pull request Oct 19, 2026
…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)
detain added a commit to detain/sglang that referenced this pull request Oct 30, 2026
…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>
@yuchenwang3 yuchenwang3 changed the title [Fix] Publish Mamba prefill cache updates before preparing the next batch [Fix] scheduler: Publish Mamba prefill cache updates before preparing the next batch Nov 5, 2026

Copy link
Copy Markdown
Author

Merged main at c03b6b3df into 1ccea14a1 and resolved the conflict. The Mamba publication check now uses upstream's early-result path, keeping its unified-memory flush. All five cache-boundary tests pass in a CPU source-method harness. Changed-file pre-commit checks pass. Normal local collection is blocked by missing starlette; I did not run a local GPU/server test for this merge. The branch is mergeable with run-ci present. Current-head CI has passed lint, title, the small-GPU job and seven CPU shards; remaining jobs are queued/running. Review is still needed.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-ci CI: run the baseline test suite on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants