Skip to content

glm cuda: the L2 prefetch's ffn-head site also reads a MoE layer's shared gate-up, after its router - #3156

Open
Bizuayeu wants to merge 1 commit into
ashhart:mainfrom
Bizuayeu:pr/glm-l2pf-shared
Open

Bizuayeu wants to merge 1 commit into
ashhart:mainfrom
Bizuayeu:pr/glm-l2pf-shared

Conversation

@Bizuayeu

@Bizuayeu Bizuayeu commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

On two GB10s at TP=2, Jammed XSFP4, one stream at a 40,960-token window, greedy, main against this branch, the mean of three launches each, run alternately:

main this branch branch / main
Decode, fibonacci-raw (tok/s) 25.01 25.26 1.0098: 1.0% faster
Decode, gpu-chat-no-think (tok/s) 24.96 25.27 1.0124: 1.2% faster
Prefill, 9,600 tokens (cold ttft_s) 6.653 6.670 1.0025: within the spread
Prefill, 38,400 tokens (cold ttft_s) 25.94 26.00 1.0022: within the spread

Decode is tokens per second, so higher is faster; prefill is seconds to the first token, so lower is faster. Decode is about 1% faster, each prompt beyond the spread of either side's three launches, with the same tokens and no new switch; prompt processing stays within that spread, as the prefetch does not run there.

What this changes

TF_GLM_L2PF's site f, before gather_a at a layer's ffn head, read a MoE layer's router alone. It now also reads the layer's shared expert gate-up after the router, its scales and then its codes, inside the same TF_GLM_L2PF_MB budget. When the prefetch was planned in #1580, site f left the shared expert out, on the guess that the routed experts' reads would evict it from L2 before it runs; at TP=2 on GB10, the table above says reading it anyway is faster. At TP=2 a rank's block FP8 shared gate-up is 8 MiB and its router 2.25 MiB, so both fit the default 12 MiB; with less room the budget cuts the gate-up's codes, as the table's test checks. On GLM-5.3-Flash at TP=2 the start-up line reads TF_GLM_L2PF: 310 rows, 1423 MiB a decode window against main's 226 rows and 1066 MiB. Replies are identical.

  • Issue: n/a: a follow-up to glm cuda: TF_GLM_L2PF reads a decode window's next weights into L2 on a side stream, on by default on a GB10 #2162's site f, too small for an issue of its own.
  • Stacked on: main at 223da7659b, one commit: the change (1dd4d08b73). Its CHANGELOG line waits for landing: main released 1.2.8 after this base and the line no longer merged into its CHANGELOG, so it can go under Unreleased when this lands.
  • atc overlap --json: complete: false (pullFilesUnavailable and its time and count limits), with no overlap for this branch (worktreeOverlaps is empty). Through gh pr list --json files, none of the 359 open pull requests changes cuda_l2pf.zig.

Checks on this head

  • Head commit: 1dd4d08b73. Every receipt is from its binaries.
  • Lean checker, python3 tools/zig/lean_check.py, on the base and on this head: 341 problems on both, 0 new. tools/pr_check.py --base origin/main --pr 3156: 0 errors and 1 warning, that no Unreleased line names this pull request, as the line waits for landing; lean 341 against 341 with 0 new.
  • Tests run, with the command and pass, fail and skip counts: zig build test-cuda-host, 6,392 of 6,422 with 30 skipped (393 of 393 steps), with zig fmt --check and zig ast-check on the changed file. With the test's change alone on main, test-cuda-host fails the table's test and nothing else (6,391 of 6,422, 30 skipped, 1 failed). CI's pytest from the root, -q -rs: 450 passed, 86 skipped, 519 subtests passed; main's CI at 1e953d8400 gives 452 passed and 84 skipped, and the two more skipped here are tools/zig/test_nfkd_tables.py's, under Python 3.12's Unicode 15.0.0. On two GB10s with this branch's tf-cuda-test on Jammed XSFP4 at TP=2: glm-lanes-cover 46 PASS, glm-lanes-concurrent 38 PASS and glm-shared-check 1 PASS on rank 0, with rank 1's 1 PASS each and both ranks exiting 0; their logs show the prefetch on with this branch's 310 rows.
  • CI: green on 1dd4d08b73 over main at 09a9eb3317: fmt, zig, zig-windows, zig-macos-dev-tools, pytest, benchmark-clients on Ubuntu and Windows, and release-paths; host-nightly skipped, as on every pull request.
  • Not run, and why: TP=3 and wider, and one GPU: the prefetch is on by default on a GB10 at two ranks or more, off elsewhere unless named. NZ5, which main does not read yet. Other CUDA chips. With nvfp4 experts cuda: decode up to 24% faster (GLM-5.3 83.8 -> 104.1 tok/s), same bits, via a per-warp cp.async ring #2266's decode kernels, not merged, which change what the routed reads leave in L2.

Receipt

  • Main against branch: the table above.
  • Boxes it reaches: GLM-5.3-Flash on CUDA with TF_GLM_L2PF on, the default on a GB10 (sm_121) at two ranks or more; decode windows only, as the prefetch does not run in prompt processing. Ran on GB10 at TP=2. Rank 0 holds 83.53 GiB of model on both sides.
  • New switch? No.
  • Environment, with the TensorFold commit, Zig and backend toolchain, machine and GPU, checkpoint and revision: main 223da7659b and this branch's 1dd4d08b73 (the head), each built with an empty Zig global cache, Zig 0.17.0 and nvcc 13.3 for sm_121. Two GB10s over a direct link (driver 580.178.04), Bizuayeu/GLM-5.3-Flash-Jammed-XSFP4@cae94e69 from a local copy (each of its 132 safetensors files has the revision's sha256; the local index differs in its metadata alone), a 40,960-token window, one stream, vision off.
  • Paths and platforms the change reaches, which I ran and which I could not: cuda_l2pf.zig's sources for site f on MoE layers, read on every decode window's side stream. Ran at TP=2 on GB10; the rest is under "Not run".
  • Exactness, from tools/bench_concurrent.py --alone --serial or token_sha against "draft": false: the three decode-check prompts, 512 greedy tokens each, give the same token_sha256 drafted and with "draft": false, on main and on this branch: ae95484b0c71, 315eaea6c98b and 461c6e99e77f. Every request's output sha in rank 0's done lines is the same, in order, on both.
  • Main against this branch, same machine and checkpoint, posted before asking for a run on our machines (or why you can't): the table above.
  • Decode speed before and after, from tools/bench_openai.py: as of 0d3ad792f, its two prompts with "draft": false in each request, 256 tokens, five repetitions, one stream at a 40,960-token window, each launch's median, the mean of three launches: the table. This branch decodes 0.98% and 1.24% faster on the two prompts, and faster in each of the three alternated pairs (+1.38%, +1.13% and +0.45%; +1.31%, +1.14% and +1.27%), while each side's three launches spread by under 0.9%. Both sides' load timing picked the same expert form.
  • Prompt speed before and after: the cold first request's ttft_s from tools/bench_cache.py --require-cold as of 19d1a9af6, on the decode-check prompts' tokens repeated to 9,600 and 38,400, the mean of three launches: the table. Prompt processing is within 0.3% of main (+0.25% and +0.22%), inside the spread of each side's launches (0.5% and 0.6%); the prefetch does not run there.
  • Licence of any new weights or drafter, or "n/a": n/a.

Checklist

  • The title and description match the head commit, including the receipt and the stack. (1dd4d08b73, one commit on main at 223da7659b: every receipt is from its binaries.)
  • New tests fail before the change, pass after it, and skip cleanly without their dependency. (The table's test, changed alone on main, fails and no other test does; it needs no GPU.)
  • Comments and docstrings are one line. No measurements or history in the source. (lean: 0 new problems.)
  • No personal data, machine names, internal hosts or local paths in the diff or its commits. No AI attribution lines. (The diff and the commit's message, searched.)
  • Every token still goes through the lane rounds. No serial path, and nothing that needs drafts off. (The change adds reads to the side stream's prefetch; glm-lanes-cover, concurrent and shared pass at TP=2 with it on.)
  • Drafted output equals "draft": false, a resumed prompt equals a fresh one, and concurrent equals solo. (Drafted equals plain on both; resumed and concurrent by the lanes tests.)
  • No precision traded for speed. If the bits change, the description says which sums change and why. (No sum changes: the prefetch reads weights into L2 and computes nothing.)
  • Decode and prompt processing are no slower than main at one stream and the widest admitted; with --drafter, no slower than without it, greedy and sampled. (One stream, greedy, MTP drafts off, on Jammed XSFP4: decode 1.0% and 1.2% faster, prompts within 0.3% of main, means of three launches each. The widest admitted and --drafter were not measured.)

…ared gate-up, after its router

Site f read a MoE layer's router alone. The shared expert's gate-up runs on the same window once the routed
experts are done, and its scales and codes now follow the router in the site's rows, inside the same budget.
The table's test gives layer 1 a block FP8 shared gate-up and checks where the 12 MiB budget cuts it.
@ashhart

ashhart commented Nov 14, 2026

Copy link
Copy Markdown
Owner

Thanks for the fresh head and the careful receipt. Adding the shared gate-up to site f is a small, clear change. Reading its scales and codes after the router, inside the same budget, is easy to follow, and the table test now checks both the added rows and the cut when space is tight.

The new head applies cleanly. Format, diff check and lean are clean, with zero new lean problems, and the changed file stays well under the size limit. The prefetch computes nothing, so served tokens are unchanged by construction.

Verdict: DEVICE. The speed gain and token hashes need a device check, and we will run that on our side. The changelog line can go under Unreleased when this lands.

Must fixes: none.

  • Synthena (Ash's Agent)

@ashhart

ashhart commented Nov 14, 2026

Copy link
Copy Markdown
Owner

Our device run on two GB10s at TP=2, main at the merge base against this head (1dd4d08), GLM-5.3-Flash Jammed XSFP4, fresh launches base, new, new, base: GREEN.

  • Tokens are identical: 72 of 72 decode cells and 12 of 12 prompt cells.
  • Decode is faster, which is the gain this PR claims: chat +8.3% at 1 stream and +4.1% at 2, code +4.4% and +4.8%.
  • The 32K prompt is level (-0.8%, inside our 1% noise bar). The 2K and 8K prompt gains in the summary come mostly from main having moved since the merge base.

Thank you @Bizuayeu. It's going into the landing queue.

  • Synthena (Ash's Agent)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants