Repository navigation
flashnext: pool lanes keep their states and draft as pieces on whole pages, no run (#2444 P3, F2) - #2898
flashnext: pool lanes keep their states and draft as pieces on whole pages, no run (#2444 P3, F2)#2898jschmied wants to merge 5 commits into
Conversation
|
Thanks @jschmied. F2 reads well. The packer is small and deterministic and refuses a bad map by name, We checked 1ed15fd: CI is green, lean has 0 new problems, no file passes 600 lines (
As agreed on #2444, this stays a draft and lands with #2992 in one train. #2992 already carries Q3's
|
1ed15fd to
d432157
Compare
|
@ashhart both done, on both stacks:
Heads: #2898 d432157, #2992 25108af, both on main 3885dce. Host |
kv_pieces packs a family's state pieces first-fit decreasing on whole pages (each piece inside one page, 256-byte aligned) and checks a map for overlap. Lease.state holds those pages beside the token table; takeState takes them from the promise and finish gives them back, so no consecutive run is needed. Layout.pieces counts them wherever a run counted (need, plan, holds), and Layout.outside names the buffers wider than a page a stream keeps in its fixed part. The load report shows the pieces, their pages and pad, and that fixed part.
…aft as pieces Under TF_FLASHNEXT_KV_PAGES, TF_FLASHNEXT_STATE_PAGES=1 maps each lane's DeltaNet states, conv tails, n-gram tail and its MTP head's spares, saved rows, small buffers and pending-row log onto pieces of whole pages from its lease (no claimRun). The head's streams and window scores, wider than a page, are a block of the lane's own outside the pool, charged in its fixed part. The saved n-gram tail and rows get their own pointers, which the rollback key hashes. Unset, the run and every buffer stay as before.
… 0 or 1 Read through environment.value, so the CUDA backend refuses any other text by name at startup, before a model loads; words, 2 and padded text are refused.
…allest page that holds it TF_FLASHNEXT_STATE_PAGES's PieceOverPage said pages need 128 rows or more, but the floor is lower and depends on the head: the head's attention layer widens a page, so 104 rows hold a 3 MiB DeltaNet state with the head and 112 without. The load now logs the page's bytes, the largest piece and the smallest TF_FLASHNEXT_KV_PAGES that holds it (smallestRows, from the same piece sizes the packer places), and the refusal points at that line, as Qwen's does.
…ith the other Flash Next settings As a .cuda row a malformed value stopped every CUDA start, not only Flash Next's. In main's flashnext scope it is checked where the other 29 are, before either Flash Next load path does device or checkpoint work; the count test and docs/environment.md move to 30 settings, 21 switches.
d432157 to
59ba8b4
Compare
|
@ashhart both stacks are restacked on main 6c5cef8, with #2939's updated core commit fac7b74 as their first commit. It has the same patch-id as #2939's (02a90f2d91d9), so #2898, #2992 and #2939 still carry the same commit. Its only change from the previous copy is #2939's point 4: the TooMany text in |
|
Thanks @jschmied. The restack reads right:
We have no more code asks on either PR. Before the two land together, we'll run one cell on a GB10 on #2992's head against main #2992 removes the per-lane block that this body's line describes, so this is the CHANGELOG line we'd use for the pair:
Tell us if you'd like it worded differently.
|
What this changes
#2444 P3 step F2, built as ashhart answered the two open questions (comment 6764448869): whole-layer pieces first, and
the draft's two buffers wider than a page as a lane's own block, charged as the lane's fixed bytes and shown in the
load report.
Today a Flash Next pool lane (TF_FLASHNEXT_KV_PAGES) claims one consecutive run of pages for its recurrent states and
its MTP head's buffers (48 pages of 256 rows with the head and pending rows, 16 without the head). A pool with no free
run that long refuses the lane even when it has the pages. With
TF_FLASHNEXT_STATE_PAGES=1(only withTF_FLASHNEXT_KV_PAGES; refused by name otherwise) the lane takes any free pages from its promise instead and places
each buffer as one piece inside one page:
row, the draft head's scores, best values, and with pending rows (the default) the log's control word and 36 layer
logs (150 pieces).
The head's
streams(31.5 MB at 1536-row chunks) and windowlogits(15.9 MB) are single matrices wider than a page.They are one block a lane, allocated at load outside the pool. The block is in the layout's
fixed_bytes, so theload plan charges it to every lane, and the load report names it.
No kernel changes. Every kernel already takes these buffers by pointer, one layer at a time. The only host sites that
reached into the head's
savedblock by offset (5, in cuda_mtp_ops/cuda_mtp_roll) now read their own pointerssaved_tail/saved_nrow, and the rollback graph key hashes them.Unset, nothing changes: the run, the layout and every buffer are as on main.
Core vs family:
checkrefuses overlap / a piece past its pagecore/kv_pieces.zig(new)Lease.state(pages beside the token table),Pool.takeState,finishgives them backcore/kv_pool.zigLayout.pieces/statePages()in need, plan, holds, the window cap;Layout.outside(of fixed_bytes, wider than a page)core/kv_pool.zig,native/cuda_kv_pool.zig(2 lines)core/kv_groups.zigkv_pieces_test.zig,kv_pool_state_test.ziglayoutPieces, lane open/close/fits, states mapped from the mapflashnext/cuda_kv_lanes.zigDraft.pieceSizes/wideBytes/initPieces,saved_tail/saved_nrowflashnext/cuda_mtp_draft.zig(+ 5 host sites)cuda_lanes_load.zig,cuda_lanes_explain.zig;core/profile.zig,docs/profiles.mdQ3 (Qwen 3.5/3.6, PLAN in #2444) is meant to reuse the core parts as is, giving its own piece list. Metal has no Flash
Next pool, so nothing there to share.
What each lane pads to (256-row pages with the head, 7.52 MiB a page):
head and the two land together.
patch-id), as asked in 6825628750, so flashnext: pool lanes keep their states and draft as pieces on whole pages, no run (#2444 P3, F2) #2898, flashnext: the MTP head's streams by row through the lease's pages, no per-lane block outside the pool (#2444 P3, F2 part c) #2992 and qwen3_5 cuda: a pool lane's state as pieces on any whole pages of its lease, no run (#2444 P3, Q3) #2939 carry the same commit. Against this PR's old first commit it
adds the reworded
kv_pieces_test.zigheader, Q3'stakeStatefix (highest free ids, the free stack's order) andqwen3_5 cuda: a pool lane's state as pieces on any whole pages of its lease, no run (#2444 P3, Q3) #2939's spare-lane line in
native/cuda_kv_pool.zigserved(off for Flash Next, which sets neitherall_on_pagesnorengine_paged). The two Flash Next commits have the same added and removed lines as on 1ed15fd; a fourth names thesmallest page in the PieceOverPage refusal, and a fifth moves the switch's environment row to main's
flashnextscope.main; flashnext: refuse a malformed TF_FLASHNEXT_* setting by name before device work #2585 (Flash Next draft/window graph) and prompt radix: checkpoint parts per cache group, one hold a page, page-only stores (#2444 P2, C3) #2875 (P2.1) merge clean with this head.
Checks on this head
qwen3_5 cuda: a pool lane's state as pieces on any whole pages of its lease, no run (#2444 P3, Q3) #2939's updated core commit fac7b74 (same patch-id 02a90f2d91d9): its only change from 7e2957d is the TooMany text
in
native/cuda_kv_pool.zig, "state pages" for "state runs", and its test.kv_pool.zig,kv_pool_state_test.zigandcuda_kv_pool.zigare byte-equal to qwen3_5 cuda: a pool lane's state as pieces on any whole pages of its lease, no run (#2444 P3, Q3) #2939's head 250994a.zig build -Dfixed-cpu test test-golden test-kernel-contract test-server-security test-pull-security fuzz-cortheon check-release-paths: 724/724 steps.zig build check-native-cuda-host compile-cuda-tests: 16/16.python3 tools/pr_check.py: 0 errors, lean 345 vs 345 (0 new); its one warning is the CHANGELOG line, which is in this body forashhart to place.
zig fmt --check,git diff --check: clean.zig build test-kv-pool test-cuda-host: 396/396 steps.kv_pieces_test.zig(Flash Next counts 186/37 and 223/45 at 256-row pages, determinism, a piece widerthan a page refused by name, overlap/past-page maps refused);
kv_pool_state_test.zig(need/plan/holds equal to a run ofequal pages, fixed part counted at admission, takeState/finish, fragmentation: every other page held,
claimRunrefuses,takeStateadmits; LEAK: failed state allocation, cancel after open, failed table write: census back to load);cuda_kv_pieces_test.zig(fake device: a fragmented pool opens a pieces lane and refuses a run lane, two lanes with thehead place every piece inside their own state pages with no overlap, failed upload/fill leave the pool as at load, a
layout from another packing refused); the switch is exact 0 or 1, refused by name when Flash Next loads, before device work: a
flashnextscope row, so amalformed value no longer stops other families' CUDA starts (
environment_test; the scope's count test anddocs/environment.md move to 30 settings, 21 switches).
kv_pieces.zig:64, 3 tests red); conv tailsmapped onto the state pieces (
cuda_kv_lanes.zigmapStates, 2 tests red).core/kv_pool.zigandkv_pool_state_test.zigare byte-equal to qwen3_5 cuda: a pool lane's state as pieces on any whole pages of its lease, no run (#2444 P3, Q3) #2939's head f4a0bdf (cmp),takeStatefix included.cuda_lanes_explain.zig): the load logs the page's bytes, the largest piece and the smallestTF_FLASHNEXT_KV_PAGES that holds it, computed from the same piece sizes the packer places (
smallestRowsincuda_kv_lanes.zig): 104 rows with the head, 112 without. The refusal points at that line, as Qwen's does. Host test:both values, the smallest page loads, one step less refuses. Planted red: smallestRows one step short (
cuda_kv_pieces_testred by name, "expected 104, found 100", 1 failed).Served gates (GB10, Qwen3.8-Flash-Next-FP4, --context 16384 --parallel 4)
The rows in this section are from the old head 1ed15fd against main 33ada77. The pair lands as one, so the served rows
for the rebased code are on #2992's head, which contains this one: its kit and A-B-B-A were rerun after the rebase. The
"don't land yet" below is about this PR alone, and #2992's (c) answers it: there main and pieces admit the same streams at
76-79 GB.
Conformance kit (
tools/conformance.py, flashnext-cuda profile with cancel/evict blocks), on this head, page cachedropped before every server: pages off,
TF_FLASHNEXT_KV_PAGES=256, and 256 +TF_FLASHNEXT_STATE_PAGES=1. All 7 gatespass in each: drafted == plain 10/10 (448 of 724 MTP drafts accepted), repeat 10/10, concurrent == solo 76/76 at 1/2/4/8
(drafted and plain, greedy and seeded), resumed == fresh 6/6, refusals 4/4, cancel 4/4 (censuses back to idle), evict 3/3.
Every kit row's token sha against main (33ada77) in the same paging: 126 rows, 114 keys, 0 differ, for off, 256, and
256 with pieces (on == off == main).
Load line with pieces (256):
state, in the pool as pieces: 223 pieces, 312.22 MiB on 45 pages a lane, 25.98 MiB padding (7.7 %); a lane's fixed part, outside the pool and counted at admission: 45.16 MiB, 45.16 MiB of it buffers wider than a page. Main:state run, in the pool: 357.39 MiB on 48 pages. A lane: 383.4 MiB vs 360.8 MiB (+22.6 MiB, +6.3 %).Speed, A-B-B-A twice per paging, fresh server each arm (f2 ab-table):
Admitted streams at a memory limit (
TENSORFOLD_CUDA_MEMORY_LIMIT_GB, --parallel auto): lanes equal at 76-79 GB, but thepool is 36-42 pages smaller (every lane's 45 MiB block, idle lanes too) and a stream saves only 3 pages. Concurrent
16k-window streams: 5 vs 4 at 77 GB (equal at the other five limits); 4k-token streams: 6/7/8 vs 5/6/7 at 76/76.5/77 GB.
Don't land yet: the idle wide block costs a stream in a measured cell, which is the trigger for (c) (streams and window
scores read by row, so they fit pages too) or for a block made only while a lane is busy.
Receipt
tensorfold-nativemd5 5c6f97bb, 1ed15fd md5 85868fe8 (zig build native -Dsm=121).TF_FLASHNEXT_STATE_PAGES(exact 0 or 1; 1 needsTF_FLASHNEXT_KV_PAGES), off by default.Proposed CHANGELOG line (Unreleased, for the maintainer to place):
- Flash Next: TF_FLASHNEXT_STATE_PAGES=1 keeps a pool lane's states and MTP buffers as pieces on any pages of its lease, so a fragmented pool still admits it; the head's two wide buffers are a lane's fixed bytes (#2444).