Repository navigation
Conversation
…hile streams decode The short-prompt loop admitted and filled prompts until none was short. A reply that ends with its prompt (max_tokens 1, an embedding, a cancel) frees its lane inside the loop, so under steady arrivals of those the decoders got no round at all. After a lane count of short prompts begun, a round goes first; a burst of arrivals that fits the lanes still goes in together. Test: 40 one-token requests beside a decoder on 2 lanes; main fills all 40 with no decoder round between them, now at most 2 in a row.
…ged long one's debt The short-prompt loop asked the one fill pick: once a long prompt had waited eight choices it won, the loop ended, and with decode time owed its step was not due, so a short prompt arriving then waited out the whole debt. Short prompts are now picked among themselves (the same order: aged, foreground, least left); the long step after them picks among long prompts, and a burst past the lane count yields to a waiting long prompt as to a decoder. Tests: a decoder, a 1,000-row prompt and 30 one-token requests at share 0.01; on main one short prompt waited 63 decoder rounds behind the debt, now at most 1. With no decoder, a long prompt beside 40 one-token arrivals still goes in before the last of them.
…opped A long prompt's step stays due while no stream decodes, but the debt it owed earlier decoders stayed too, and the next decoder to start was paid it before the long prompt's next step. The debt is now cleared whenever no stream is runnable, as it already was when no prompt waits. Test: a 3-token decoder ends after a long prompt's first step at share 0.01, a second decoder arrives; on main the second ran 50 rounds before the long prompt's next step, now 1 (its first token).
Contributor
Author
|
Metal check, since
|
Owner
|
Landed on main (84e0d56..24072ec), with your three commits as author. Thank you! On a GB10 (Qwen3.8-27B CUDA) against main: tokens identical, the 1-8 stream sweep exactly flat, and prefill within 0.3% across six alternating runs. On an M3 Ultra it is flat as well, and with the rest of the batch, Flash Next MLX on CUDA is flat to +2.8%. - Synthema, Ash's AI Assistant. |
This was referenced Oct 25, 2026
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.
Follow-ups to #1188 (prompt steps beside decoding streams), found in a review of main. Each commit has a test that fails on main and passes here. The Gemma 4 fix from the same review is a separate PR.
1. A burst of short prompts could pause decoding without bound (
4c22c055b)The short-prompt loop admits and fills until no short prompt waits. If a reply ends together with its prompt (max_tokens 1, an embedding, a cancel), it frees its lane inside the loop. Under steady arrivals of those, decoders got no round. Now, after a lane count of short prompts have begun, a round goes first. A burst that fits the lanes still goes in together, so normal traffic is unchanged. Test: 40 one-token requests beside a decoder on 2 lanes. Main fills all 40 back to back; now at most 2 in a row.
2. Short prompts waited behind an aged long prompt's debt (
3dc3997d5)pick()let a long prompt that had waited 8 choices win. The short loop then ended, and with decode time owed, the long step wasn't due either, so a new short prompt waited out the whole debt. Short prompts are now picked among themselves (same order: aged, foreground, least left). The long step picks among long prompts. A burst past the lane count yields to a waiting long prompt as it does to a decoder, so aging still holds with no decoder. Tests: share 0.01, a decoder, a 1000-row prompt and 30 one-token requests. On main one short prompt waited 63 decoder rounds; now at most 1. A second test checks that with no decoder, a long prompt beside 40 one-token arrivals still goes in before the last of them.3. Debt survived its decoders (
138e7a400)If every decoder finished or paused, the debt stayed, and the next decoder was paid it before the long prompt's next step. The debt is now cleared whenever no stream is runnable. Test: a 3-token decoder ends after a long prompt's first step at share 0.01, then a second decoder arrives. On main it ran 50 rounds before the long prompt's next step; now 1 (its first token).
Gates: full
zig build test -Dnvccexits 0. Lean: 0 new problems against the merge base. The fake-backend equality tests are unchanged and pass. With the default share (1), scheduling changes only when lanes free up inside a burst (point 1).