Repository navigation
Conversation
62a5890 to
03e7762
Compare
|
Follow-up check on the borrowed ring and state lifetime: From reading the code, the lane cannot recycle its run while MTP work is outstanding, on any of the four paths:
The fallback context ( The check did find a real bug, and it sits next to this code rather than in it. The server fills prompts one step at a time (Drafted.prefillStep), and prefillStep opened the drafter before the target's first step. At that point the stream had no lane yet, so ringOf found no cache, and both MTP and DFlash2 took an owned context for every stream. The pool lane's ring went unused, and every stream past the first allocated an unplanned ring at admission. Whole-prompt prefill already opened the drafter after the target, which is why the PR's byte counts and gates looked right. On the server with TF_QWEN_KV_PAGES_ALL, the original stack opened 40 owned drafter contexts in one gate run (35B + MTP); with the fix it opens 0 (27B + DFlash2: 40 → 0 as well). Fix, as a new commit on this PR: "lanes: a stepped prompt's drafter opens after the target's first step" (core/lanes/drafted.zig). The drafter now opens after the first step and before that step's rows are taken. A failed step releases only the target, since no drafter is open yet; a failed open releases both. The existing stepped-prefill host test now also asserts that each drafter open sees the target's lane: it failed before the fix (0 of 2) and passes after (2 of 2). Gates (35B-A3B + MTP and 27B + DFlash2, bf16 KV, --parallel 8 at 16384): outputs match the original stack's with every stream on pages (37/37 each); drafted == plain (37/37); concurrent == solo (15/15); flags off == main (37/37); paging == main paging on solo and resumed requests (22/22). zig build test passes, and lean shows 0 new problems. |
|
Tested as part of the stack at |
03e7762 to
d0822e4
Compare
|
Added the host test: It drives the real
Mutation check: a This branch now sits on #1355's new head (the launch-recording test commit). |
…e's run qwen3_5_moe's MTP drafter kept a ring of the window's keys and values and the newest row's state for every admitted stream, outside the pool (64 MiB a stream at 32768). With TF_QWEN_KV_PAGES they are now the end of the stream's lane run, as DFlash2's ring is: the drafter borrows them for a stream on a pool lane (zeroed on the engine's stream when the lane opens, returned with the run after the stream drains) and keeps its own contexts only for the first stream. MTP reports no target taps among its bytes, so nothing else moves out of the fixed part. GB10, Qwen3.6-35B-A3B MLX-4bit with its MTP layer, TF_QWEN_KV_PAGES=256, --parallel 4 at 32768: 64 -> 0 MiB fixed a lane (bf16 and fp8). Qwen3.8-27B + DFlash2: unchanged, 23 MiB. Gates (--parallel 8, 16384, bf16 and fp8 KV, both models): tokens equal main's contiguous and paged runs, drafted == plain, concurrent == solo, resumed == fresh.
Drafted.prefillStep opened the drafter before the target's first step, so the stream had no target lane yet: Lanes.ringOf found no cache, and DFlash2 and MTP took an owned context for every stream instead of borrowing the pool lane's run. With TF_QWEN_KV_PAGES that is an unplanned ring a stream past the first (`reserve` keeps one owned context once ring_of is set), allocated at admission, while the run's ring sat unused. The server fills prompts a step at a time, so it never borrowed; whole-prompt prefill already opened after the target. The drafter now opens after the first step, before that step's rows are taken. A failed step releases the target alone (no drafter is open yet); a failed open releases both, as before.
…en and given back on release cuda_mtp_area_test.zig drives the drafter's open and release without a device, a stream on a pool lane beside one on a reserved window, at 1, 3, 4096 and 32768 rows: the borrowed ring starts at the run's area, the state follows at the next 256 bytes and ends at areaBytes; a second open is refused; release frees the borrowed context (the testing allocator's leak check) and leaves the run alone, while the reserved window's owned context goes back to the pool; reserve(8) keeps one context of the drafter's own.
d0822e4 to
0d9734e
Compare
|
Rebased on main |
|
Thanks for the host test. It covers the area layout, the borrow and release paths and the reserve count we asked Your device gates were run on the stack with #1355, before #1373 changed the paged attention. We'll rerun them on |
…t's drafter after the target's first step (#1356)
|
Landed on main with your authorship (469d16b, f3eb483, 816965d). Our GB10 run of the 27B with DFlash2 on the default path: 0 of 20 prompt rows differ from main and the stream sweep is within ±0.4%, with every stream equal to its run alone. Thanks @jschmied. #1357 follows once the share's lane count is fixed, as noted there. |
|
This is in TensorFold 1.1.8, released today. Thank you, @jschmied! |
Part of #1156 (was
ac90d6b8there). Rebased on main3f144ccf8; #1355 is closed, and its commits are gone.An MTP stream's ring and its state now sit in its pool lane's run instead of a fixed per-lane allocation. At
--parallel 4/ 32768 the 35B with its MTP layer goes from 64 to 0 MiB fixed a lane, bf16 and fp8. The 27B with DFlash2 stays at 23 MiB. The ring is zeroed on the engine's stream and returned only after the drain, as the maintainer fixes on main expect.Gates on GB10 (Qwen3.6-35B-A3B MLX 4-bit with
--drafterfor its MTP layer, Qwen3.8-27B MLX 4-bit with DFlash2; bf16 and fp8 KV,--parallel 8, 16384,TF_QWEN_KV_PAGES=256, 37 greedy outputs per arm): flags unset == main 37/37, paging == main paging 22/22, drafted == plain, concurrent == solo at 1/2/4/8, and no lane pages left at idle, on all four configurations.zig build test -Dnvcc=…exits 0, lean shows 0 new.