Repository navigation
gemma4 CUDA: the global layers' caches in pages of the core pool, every stream leasing; sliding layers keep their rings - #1619
Conversation
|
Thanks @jschmied. We ran this on a GB10 against main with no drafter, and it's clean. Gemma 4 31B 4-bit with cold prompts at 2k, 8k and 32k, and Gemma 4 12B 4-bit at 1, 2, 4 and 8 streams, both GREEN: tokens equal to main and no cell slower beyond noise. One thing stops us landing it yet. Paging is on by default for every Gemma 4 CUDA server, and the drafted paths go through the new paged kernels too: the 12B assistant reads the global layers through the Could you post, on your GB10, paged (the default) against
If paged is no slower within noise, this lands. If it is slower on a drafted path, keeping linear caches while a drafter is attached (or until the paged decode catches up) would let the rest land now. #1620 waits on this one. |
|
Thanks @ashhart. Measured on the GB10 with c460a2a, one binary for both arms, paged (default) against
Every reply is byte-identical between the two arms and to its Six of the eight means are lower on the paged side, by 0.8 to 1.7 %, each inside the run-to-run spread. Paged wins round 2 on all four 31B cells, so I read it as noise rather than a gap. Paged is no slower within noise on the drafted paths. |
|
/atc exchange 7d514b9d11922e9c3a53bbd6293e53f6373fdc00710d42e937e2f21317d9c293 |
|
I pushed d4bafe4 here: the paged decode kernels from #1618 (tree attention |
|
Thanks @jschmied, and thanks for the drafted cells. Byte-identical everywhere is what we needed. On the 31B + DFlash single-stream code cell that's still 0.7 to 2.4% under main: yes, take the cheap way out. Keep the global layers' caches linear while a drafter is attached, and paged otherwise. A default can't get slower on any cell, and paging only pays when streams share the pool. A drafter-attached server can move to pages later, in its own PR, once that cell is level. Push that and we'll re-run the 12B + assistant and the 31B + DFlash cells, at 1 and 4 streams, code and chat, paged against |
|
Done in f419bed: Gemma 4's global layers lease pages only when no drafter is attached. With
Your rerun list then covers 12B + assistant and 31B + DFlash, at 1 and 4 streams, code and chat, paged against |
|
Thanks @jschmied, that's the fix we wanted. The 31B + DFlash single-stream numbers now sit inside main's own spread. We've rebased the series onto current main (94b90c9). One conflict: #1386 added Gemma 4's grammar-masked greedy pick ( Together they take |
|
Moved in 2084804 on Your rebased copy isn't anywhere I can reach (before this push the PR head was still f419bed, and I found no branch on ashhart/TensorFold), so I did the move on our branch. To check it applies after your rebase, I rebased f419bed onto 94b90c9 myself, resolved the conflict as you did (
|
|
Thanks @jschmied. The move applies cleanly on our rebased copy: |
|
Gemma 4 31B on a GB10 without a drafter, @jschmied: your series with the split, rebased on main, against main, alternating fresh servers. Tokens equal (0 of 20 rows differ), prefill and decode level (-0.1%), sweep level at 1, 2, 4 and 8 streams (0.0 to +0.2%). GREEN. The 12B run is going now, and it lands with that. Then #1620's Kolibri cells after your merge fix. |
|
The 12B is back, @jschmied, and it's a hold for now. Gemma 4 12B 4-bit on a GB10, no drafter, main against this, order main, this, this, main. Tokens equal, prefill level, sweep level (-0.2 to +0.1%). But decode after long prompts is lower in both runs of this PR, in every cell:
That's about -0.5 to -2%, growing with context, and the shape is #1653's before your paged decode merge fix (7c0a80b). The 31B didn't show it, maybe because its global layers are a smaller share of its decode. If Gemma's global layers decode through the same |
|
Thanks @ashhart. Gemma 4's global layers do decode through
I haven't timed it here. Could you re-run the 12B long-context cells and the 31B? |
b426e52 to
32d1f87
Compare
…ry stream leasing; sliding layers keep their rings The window_pages kernels already read keys and values through a slot's page table at head dim 512, so this changes only where the global caches live and who writes them; no kernel changes. - A page is 256 positions (window_attention.page_unit: a decode chunk, whole prompt tiles) of every global layer's K and V; the arena holds each layer's K region, then its V region, page id p at rows [256 p, ...). - Loaded.layout with engine_paged, as lfm2_moe's and Dory's: open makes one slot's rings, the host plans the pool and calls attach (the arena, the slots' rings and tables). A stream is promised its prompt, reply and one verify window at its prompt pass, takes pages as its rows reach them and gives them back when it ends; fits is the host's admission. The idle server logs the pool's census. - Writes: the rows kernel writes no global cache when paged; tf_wrows_put copies its k and v rows (the same bits it would have stored) into the pages. - The decode and prompt windows of the global layers carry pg, in the target forward and in the assistant, which reads the target's pages through the same table. Kept states stay copies: a global layer's rows are walked through the slot's pages. - TENSORFOLD_GEMMA4_KV_PAGES=0 (or Options.kv_pages = false) keeps the linear caches made at open. tf-cuda-test gemma4-pages MODEL TEXT N [DRAFTER] runs the same prompts on linear and paged caches (pages leased in a shuffled order, a pool the ended streams' pages must be reused from) and wants the same replies and last-row logits, solo, sampled, two streams in a round and resumed from a kept state.
…est) before the passes
…ey (tree attention and head dim 512 _pg kernels), SASS of the others unchanged
…linear, paged only without one
…to cuda_pages.zig, the code unchanged
… a noinline helper, so its fast path builds no page lookup
|
Thank you @jschmied. This is merged into main with your commits under your own name, so you're listed as a TensorFold contributor. It ships in the next release, credited to you in the changelog and the release notes. |
|
Thanks @jschmied! This is in TensorFold 1.2.2, out now: https://github.com/ashhart/TensorFold/releases/tag/v1.2.2 |
…and Nemotron's Metal paging The switch registry grows by four rows (ashhart#1660's next group, all measured identical): TENSORFOLD_DORY_KV_PAGES (opt-in, ashhart#1284), TENSORFOLD_GEMMA4_KV_PAGES and TENSORFOLD_SPARK25_KV_PAGES (default-on, exact 0 opts out, ashhart#1619, ashhart#1653), and TF_NEMOTRON_METAL_KV_PAGES, the first metal-backend row (exact 1, ashhart#1537). A new .not_zero syntax covers Dory's on-unless-0 reader; the 0-opt-out rows reuse report_values. The four readers go through the registry's environment-first helper, so an unset variable and every set value behave byte for byte as before.
…and Nemotron's Metal paging The switch registry grows by four rows (ashhart#1660's next group, all measured identical): TENSORFOLD_DORY_KV_PAGES (opt-in, ashhart#1284), TENSORFOLD_GEMMA4_KV_PAGES and TENSORFOLD_SPARK25_KV_PAGES (default-on, exact 0 opts out, ashhart#1619, ashhart#1653), and TF_NEMOTRON_METAL_KV_PAGES, the first metal-backend row (exact 1, ashhart#1537). A new .not_zero syntax covers Dory's on-unless-0 reader; the 0-opt-out rows reuse report_values. The four readers go through the registry's environment-first helper, so an unset variable and every set value behave byte for byte as before.
…and Nemotron's Metal paging The switch registry grows by four rows (ashhart#1660's next group, all measured identical): TENSORFOLD_DORY_KV_PAGES (opt-in, ashhart#1284), TENSORFOLD_GEMMA4_KV_PAGES and TENSORFOLD_SPARK25_KV_PAGES (default-on, exact 0 opts out, ashhart#1619, ashhart#1653), and TF_NEMOTRON_METAL_KV_PAGES, the first metal-backend row (exact 1, ashhart#1537). A new .not_zero syntax covers Dory's on-unless-0 reader; the 0-opt-out rows reuse report_values. The four readers go through the registry's environment-first helper, so an unset variable and every set value behave byte for byte as before.
Stacked on #1618 (only the last three commits are new). Gemma 4's global layers take their K/V from pages of the core pool, every stream leasing as it grows; sliding layers keep their rings.
TENSORFOLD_GEMMA4_KV_PAGES=0keeps the linear caches.pis rows[256p, 256(p+1))of each.Loaded.layoutwithengine_paged,attach/fitsexported as lfm2_moe and Dory do; a stream's fixed bytes are its sliding rings and table row (about 480 MiB on the 12B, 1.17 GiB on the 31B).pagesFor(prompt, max_new)at the prompt pass, pages taken as rows reach them, returned atdropLane.LaneMemoryandfitshold back what can't be promised;forwardrefuses rows past a slot's pages (PastPages).kr/vrscratch as before, andtf_wrows_putcopies those rows into the pages (one launch per global layer per forward), so the cache bits are the linear cache's._pgkernels). The assistant and DFlash read the target's pages the same way.snap.eachwalks a paged layer through the slot's pages.attach, sobuildgives slot 0 a first page; the assistant's captured graphs are dropped whenattachreplaces the caches (graph_gen).Checks (GB10, on main d33dcfc)
tf-cuda-test gemma4-pages MODEL TEXT 64 [DRAFTER]: linear vs paged at context 8192, two lanes, the pool exactly the two widest requests' pages and shuffled, so later streams lease pages earlier ones returned. Replies and last-row logits byte-equal: three prompts (200, 1,300, 700), a sampled reply, two streams in shared rounds, a state kept at 1,000 and resumed.gemma4-statesnow attaches the pool when paged. Its two "drafter context MISSING" checks fail the same way with linear caches (TENSORFOLD_GEMMA4_KV_PAGES=0).Speed, paged vs
TENSORFOLD_GEMMA4_KV_PAGES=0:tensorfold-native serve --context 16384 --parallel 4, no drafter, fresh server per arm, three rounds with the order alternating, 256 greedy tokens, two prompts per case; min / median / max:The arms overlap in every cell; the extra
putlaunch does not show. Not measured: vision prompts over pages.zig build test test-kernel-contract test-golden test-server-security test-pull-security fuzz-cortheon,git diff --check: pass; lean 0 new.