Repository navigation
Flash Next: a lone request on the engine's GPU-side rounds, handed to the shared rounds when a second arrives - #11
Conversation
… the shared rounds when a second arrives One stream through the shared rounds decodes slower than the single-reply driver: a host-driven round each window. The batch host now gives LaneHost a lone driver (api.Lone, as the 27B's host does): - The lone stream's prompt pass is the shared sessions' own, in the engine's session (session 0, whose buffers the engine's rounds use), so the states the prompt cache keeps are the shared path's. - It then decodes on the engine's GPU-side rounds: generateFrom's decode, split out as Engine.decode, two rounds in flight. - When LaneHost's yield hook asks (a request waits, or a cancel), the loop stops after a landed round; the round ahead has already started as rounds do (the kept DeltaNet states, the n-gram tail, the head's absorb of the last window), so the session's host fields are set to match (position, history, head position, pooled counts clamped) and the lane core adopts the stream: its next window starts at the cache's end with nothing held. - FLASHNEXT_NO_LONE=1 serves every request through the shared rounds. The lone driver takes greedy streams only (api.Lone.sampled stays false): sampled streams, and those with a thinking budget, stay on the shared rounds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cwschroeder
left a comment
There was a problem hiding this comment.
Thanks, @sethforprivacy. One fix before merge:
families/flashnext/backend.zig:194,201,231:promptaliasesstream.context. Committing the first token can reallocate that array, anddecodethen reads the stale slice into GPU history (engine.zig:687). Please takestream.prompt()again after the commit (or reserve capacity first), and add a test with a prompt length that forces the reallocation.
When this lands after #7: sampled requests should stay on the shared rounds (.sampled = false), since the lone handoff does not sample. After #10: keep session 0 for lone decode and smallest fit for shared admission. The lone driver is on by default, so the description should say that unset changes execution.
bff378b to
a3085be
Compare
|
Posted by Claude (Anthropic's AI assistant) on behalf of @sethforprivacy, as part of our local-inference tuning work on a Mac Studio M5 Ultra. @cwschroeder thanks for the reviews. First, the rebase you asked for: onto the branch's new tip
Checks on the M5 Ultra (macOS 27.0, Zig 0.17.0,
I haven't rerun it live, so the served receipts in the description are from the pre-rebase head. Each branch still sits directly on the batching branch, so they can be reviewed and merged in any order. Your review is next: |
…t, which can move the context The prompt slice was taken before the first token's commit and handed to Engine.decode after it; a commit that grows the context frees the old buffer, so decode copied freed memory into the rounds' history. - Backend.commitFirst commits the first token and returns stream.prompt() from the context as it is then. decode copies the prompt to the rounds' history before any round commits, so later commits leave it alone. - A test fills the context to capacity, refuses growth in place (FailingAllocator resize_fail_index 0), and checks the returned prompt is the moved context's, with the prompt's tokens. - backend.zig joins the core test root (tensorfold.zig), so this test runs.
|
Posted by Claude (Anthropic's AI assistant) on behalf of @sethforprivacy, as part of our local-inference tuning work on a Mac Studio M5 Ultra. @cwschroeder fixed in
M5 Ultra, Zig 0.17.0: |
|
@sethforprivacy thanks, |
cwschroeder
left a comment
There was a problem hiding this comment.
Rechecked 09c72626, including an independent Astra review. The stale prompt fix is correct: commitFirst takes the prompt from the context after the commit, and decode copies that history before subsequent commits. The regression test forces relocation and checks both pointer ownership and token contents.
Local checks on an M5 Max with Zig 0.17.0 and -Dcpu=apple_m1: zig fmt --check passes; zig build native -j4 passes 45/45 steps; zig build test -j4 reports 1514/1522 tests passed, 8 skipped. All 283 core tests pass, including the new relocation test, but the test step still exits nonzero because the split-checkpoint test logs the previously reported text.weight error. This does not replace a served model receipt.
No new correctness blocker found in this head. The misplaced doc comment identified above remains. Please include that small move in the rebase after #9; the combined head will need another check, especially session 0 and admission when #10 is also included. #10 now has a separate follow-up review with two remaining issues.
…atch-lone Conflicts: backend.zig keeps both the think import and batch_costs, and both the lone handoff counter and ashhart#9's serial-drafts and costs fields; flashnext_batch_host.zig's startup line names both the drafts mode and the lone driver; tensorfold.zig's test root takes both backend.zig and batch_costs.zig.
…rst had taken it), doc comments on one line
…rge left shadowing the lone driver (Backend.lone)
|
Posted by Claude (Anthropic's AI assistant) on behalf of @sethforprivacy, as part of our local-inference tuning work on a Mac Studio M5 Ultra. @cwschroeder up to date with the tip after #9 (
M5 Ultra, Zig 0.17.0, This head doesn't include #10, so the combined check you mention (session 0 and admission with #10) still waits for #10. |
b7ca88a
into
cwschroeder:feat/flashnext-continuous-batching
Posted by Claude (Anthropic's AI assistant) on behalf of @sethforprivacy, as part of our local-inference tuning work on a Mac Studio M5 Ultra.
As asked on ashhart#606: the lone driver and its handoff, alone against
feat/flashnext-continuous-batching. It was rebased onto04c8571(1.0.8 with #7 and #8), with the review fix as a second commit. It needs neither of the other two PRs. The receipts below are from77ebc3f, before the rebase. With the sized sessions, a handed-over stream would also move to the smallest free session that holds it, keeping the engine's long session free. That step would follow whichever of the two lands second.What it does
LaneHostone (api.Lone, as the 27B's host does). A lone request runs the shared sessions' own prompt pass into the engine's session (session 0, whose buffers the engine's rounds use), so the states the prompt cache keeps are the shared path's.generateFrom's decode split out asEngine.decode, with two rounds in flight.LaneHost's yield hook asks (a request waits, or a cancel), the loop stops after a landed round.FLASHNEXT_NO_LONE=1serves every request through the shared rounds.api.Lone.sampledstays false), as do requests with a thinking budget or the loop guard (lane_host_lone.fits). A sampled reply would need one more step at handoff, the round start run by hand since nothing runs ahead of it, which our combined build has.Receipts on this branch. Mac Studio M5 Ultra, 256 GB, macOS 27.0,
TensorFold/Qwen3.8-Flash-Next-MLX-6bit-MTP@45032a1, greedy,--context 32768 --parallel 2(base and branch alike).77ebc3f, shared roundsbb176f18, prosea49a7c9f, edit3d5a75d5).handoff_exact.py): each first request is sent alone, and the second follows once the first has streamed ~400 characters. All three pairs equal their solo replies, both requests.ca7e464a7a501db9).On our combined build (
add3da3onsethforprivacy:flashnext-lane-parity):zig fmt --checkpasses on the changed files.zig build testat09c7262passes 1514/1522 (8 skipped), including a test that the prompt is read after the first token's commit moves the context. One step fails:core.checkpoint_metal's split-checkpoint test, which fails the same way on04c8571here.🤖 Generated with Claude Code