Skip to content

Fix batched prefill decode starvation - #601

Draft
krunkosaurus wants to merge 3 commits into
antirez:mainfrom
krunkosaurus:agent/fix-batched-prefill-starvation
Draft

Fix batched prefill decode starvation#601
krunkosaurus wants to merge 3 commits into
antirez:mainfrom
krunkosaurus:agent/fix-batched-prefill-starvation

Conversation

@krunkosaurus

@krunkosaurus krunkosaurus commented Jul 24, 2026

Copy link
Copy Markdown

Summary

  • reserve the next model turn for an active decoder after each bounded prefill quantum
  • establish that reservation as soon as a request registers for generation, covering the final-prefill-to-first-decode gap
  • schedule resident-session eviction saves and disk checkpoint loads through the same batched prefill arbiter
  • release the reservation after a decode batch, the final active generation, or shutdown
  • add a threaded regression test for the scheduler handoff

Root cause

The batched scheduler gives decode work priority only when decode_pending > 0. After a prefill quantum releases the model lock, an active generation may not have enqueued its next token yet. The prefill worker can therefore reacquire the lock immediately and repeat this race for every quantum, turning bounded 128-token prefills into minute-long decode stalls.

There is a similar first-token window after a request finishes prefill but before it registers and enqueues its initial decode. Reserving the handoff at generation registration closes that gap even if another prefill already acquired the executor.

A resident-session cache miss also performs an eviction save followed by a disk checkpoint lookup before the request reaches prompt prefill. Those operations previously acquired inference_mu directly, so a long prefill could repeatedly win the mutex and keep the new request invisible to the round-robin scheduler. Routing those cache swaps through the prefill arbiter makes the waiting slot visible after the current bounded quantum. Recursive checkpoint writes from an active prefill remain on the existing raw helper.

Validation

  • git diff --check
  • clean make -j4 ds4-server and make -j4 ds4_test builds
  • fresh server-only unit binary: ds4-server tests: ok
  • live Apple M5 Max test with --batched-session 3: one 12,009-token cold prefill plus two warmed short streaming requests
  • both short requests entered prefill after the first 2,048-token quantum and produced output in 13.4–14.0 seconds; the long request completed in 31.2 seconds
  • before the cache-swap scheduling change, both short requests remained blocked until roughly 32 seconds

Fixes #600

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.

Batched server prefills can starve active decoders between quanta

1 participant