Skip to content

glm cuda: TF_GLM_EXPERT_FORM names the expert form, so launches compare at one form - #3151

Open
Bizuayeu wants to merge 3 commits into
ashhart:mainfrom
Bizuayeu:pr/glm-expert-form-name
Open

Bizuayeu wants to merge 3 commits into
ashhart:mainfrom
Bizuayeu:pr/glm-expert-form-name

Conversation

@Bizuayeu

@Bizuayeu Bizuayeu commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

What this changes

GLM's CUDA load races its classic and factored NVFP4 expert forms, which give the same bits, and decodes every window on the winner. The race's sums can land inside its 1% margin, so two launches or two builds of the same code can keep different forms, and a comparison of two builds then carries the form along with the change it measures.

TF_GLM_EXPERT_FORM=classic or factored names the form instead. Rank 0 sets it on every rank before the warm-up, skips the race, and times MTP's windows and draft step at that form. A load that times nothing (no drafts, or too little scratch context) still sends the named form to every rank and logs glm: expert form (untimed). The named form ends the price cache's identity, so its record and the race's never stand in for each other. Unset or blank keeps the race, its key and its startup line as they are. On four GB10s at TP=4 (GLM-5.3-Flash XSFP4, --context 1048576 --parallel 8), round time in ms from our own grid client ("draft": false, greedy, 256 tokens, each launch's median of six reps), lower is faster:

Cell (a 4,096-token prompt) main this head, unset this head, classic this head, factored
1 stream 26.457, 26.482 26.406, 26.572 27.842, 27.699 26.441, 26.420
4 streams 47.827, 48.092 47.999, 47.926 46.935, 47.360 47.906, 47.965
8 streams 71.605, 71.703 71.736, 71.672 73.273, 73.304 71.641, 71.740

Main and this head unset both raced to factored (glm: windows (timed): … experts factored), and their later launches read that record ((kept)); unset, this head is within 0.1% of main in every cell. A named launch times its own record under the named key, classic right after the unset ones (glm: windows (timed): 26.62 ms at 1 row, 34.72 at 4, 99.16 at 32, a draft 1.37 ms; experts classic (TF_GLM_EXPERT_FORM)), and its second launch keeps it. The two forms trade places by width: against classic, factored's rounds are 4.82% shorter at one stream, 1.67% longer at four and 2.18% shorter at eight (the mean of two launches each), with the same replies. Without drafts (--no-drafts) the named load logs glm: expert form (untimed): factored (TF_GLM_EXPERT_FORM), the unnamed one logs nothing, and both answer as main does.

Tests:

  • New: "a named expert form is set once, before the warm-up, and no forms race" (glm_lane_costs_host.zig), "the expert form takes classic or factored, leaves blank to the race, and refuses the rest by name" (cuda_env_test.zig), "a load that times nothing sends a named form to every rank once, and nothing unnamed" and "a named form's startup line ends at (TF_GLM_EXPERT_FORM), and a raced one as it did" (cuda_lane_costs.zig).
  • Changed: "the consumed key includes drafter precision and all timing knobs" also holds that a named form only ends the key, so the race's key stays as it was, and that the two names' keys differ.
  • Unchanged: the two race tests pass with only the new argument added, so an unnamed load makes main's calls in main's order.
  • Red, on the tree before the rebase: a copy of that head that ignores the name in four places (the measure races as if unnamed, the key drops its tail, untimed sends nothing, the env row reads classic as factored) fails exactly four tests in test-cuda-host, 6,355 of 6,389: the set-once test (expected 1, found 3), the env test, the untimed test (expected false, found null) and the consumed key's named tail. The startup-line test reads only the line formats, which that copy keeps, so it passes there.
  • New, after review: "a record kept at one named form is not reused at the other" (cuda_lane_costs.zig) keeps a classic record under a classic load's key in a temporary cache, then shows that a load naming factored finds nothing under its own key, and that the reuse check refuses a record kept at the other form while the race and the same form reuse it. The check moved into reuses(), which price() calls in its place.
  • Red, on this head: with the reuse check's form clause always true, only the new test fails in test-cuda-host, 6,447 of 6,478, where a classic record must be refused at factored. With the key's named tail always empty, the new test fails where the factored key must find nothing, and the consumed key's test where a named form must lengthen the key, 6,445 of 6,478; the third failure in that copy is cortheon fuzz's linear-time test, a timing test that passes in the gate below.

Checks on this head

  • Head commit: 63dcb400ff, the test the owner suggested, on the CHANGELOG line f935ab25cf and the change c628011007. Its only code change moves price()'s reuse check, as it was, into reuses(). The checks below ran on this head; the receipt ran the change before the rebase, on main 6c5cef8941.
  • Lean checker, python3 tools/zig/lean_check.py, on the base (main ab78aef904) and on this head: 341 and 341, the same findings. On today's main d367f0fb7f and this head merged with it: 341 and 341, the same findings.
  • Tests run, with the command and pass, fail and skip counts: the whole gate on this tree in WSL (x86_64, Zig 0.17.0), zig build with test -Dfixed-cpu, test-kernel-contract, test-golden, test-cuda-host, compile-cuda-tests (x86_64 and aarch64), check-native-cuda-host, test-glm-lane-costs-host, test-glm-window-prices-host, test-glm-fact-contract-host, test-cuda-startup-costs-host, test-glm-startup-host and test-glm-link-transport-host: every step passed (test 9,315 of 9,385 with 70 skipped, test-kernel-contract 238 of 238, test-cuda-host 6,448 of 6,478 with 30 skipped). The new test runs and passes in both test and test-cuda-host; test's one skip more than on the head before is Nemotron's ring-read test, which skips when its first read finishes before the second is asked. pytest from the root with the CI's options plus -rs (-q -rs -p no:cacheprovider): 450 passed, 86 skipped, the same skips as on the head before. Main's CI pytest at 90b4fae62d, the newest main run that has finished, has 461 passed and 84 skipped, with the nine tests main has added since this base, and prints no skip reasons. Two of the skips here are tools/zig/test_nfkd_tables.py's, which want Unicode 16.0.0 (this Python 3.12 has 15.0.0).
  • CI: pending on this head.
  • Not run, and why: Metal, HIP, Vulkan and XPU builds and tests, with no such machine here. Only GLM's CUDA load reads TF_GLM_EXPERT_FORM (price in families/glm/cuda_lane_costs.zig).

Receipt

  • Main against branch: the table above and the drafted figures below.
  • Boxes it reaches: every CUDA box that serves GLM. This receipt: four GB10s at TP=4, a ring of four QSFP cables.
  • New switch? TF_GLM_EXPERT_FORM. Unset by default, which races the forms as main does; classic or factored names one; blank is unset, and any other text stops the load by name. Its row is in platform/environment.zig and docs/environment.md, and the GLM CUDA recipe says when to name it.
  • Environment: TensorFold 1f30d6c365 (the change before the rebase; the rebased c628011007 differs only beside glm cuda: TF_GLM_CONTEXT_CEILING serves windows past 1,048,576 tokens, read at GLM's open #2765's env lines, and this head's 63dcb400ff only moves the reuse check into reuses() and adds a test) and main 6c5cef8941, each built from an empty Zig global cache with Zig 0.17.0 and zig build native -Dnvcc=<its nvcc> -Dsm=121 in a CUDA 13.3.1 build container; GLM-5.3-Flash XSFP4 at 88736f91 with its MTP head drafting; --context 1048576 --parallel 8 --mtp-confidence 0.5, TF_KV_SHARE=32, a 14 GiB reserve, TF_GLM_PREFILL_REDUCE=gather and NCCL_CUMEM_ENABLE=1 (the grid adds TF_GLM_PROMPT_TAIL=1), the GPU clock capped at 2,200 MHz. A first launch of this head, not counted, put its race's record in the price cache, as a build's first load does; then the grid ran main, unset, classic, factored, factored, classic, unset and main, the drafted launches main, unset, unset and main, and the two loads without drafts factored and unset.
  • Paths and platforms the change reaches, which I ran and which I could not: GLM's CUDA load, where price reads the name; unnamed, it makes main's calls. Ran on TP=4 GB10s: unnamed with drafted and "draft": false requests, each named form with "draft": false requests, and both without drafts. Not run: drafted requests at a named form. A named load prices its windows at that form (factored named: 34.84 and 97.97 ms at 4 and 32 rows, against the race's 37.18 and 102.10), so its drafted speed can differ from the raced load's at the same form. Also not run: TP=2 and TP=3, where the name is read and sent the same way and unnamed is main's path.
  • Exactness: every grid launch's replies equal main's first launch's in all three cells, named either way and unset, and the two loads without drafts answer the same. On the four drafted launches, tools/bench_concurrent.py --alone --serial finds every concurrent reply equal to the same request alone (78 checks a launch) and to the serial "draft": false replies (48 a launch), none failed, and every bench_openai and bench_concurrent reply hash equals main's first launch's (12 and 78).
  • Main against this branch, same machine and checkpoint: this post.
  • Decode speed before and after, in tok/s, main's two drafted launches → this head's two (unset): tools/bench_openai.py (the median of three 256-token runs) and tools/bench_concurrent.py (a cell's aggregate, the median of three reps; request i sends tools/bench_prompts.json's item i, 256 tokens):
T bench_openai fibonacci-raw bench_openai gpu-chat-no-think bench_concurrent 1 stream 4 streams 8 streams
0 54.22, 52.27 → 53.80, 53.99 43.51, 42.22 → 43.22, 43.25 65.3, 65.1 → 65.2, 65.1 109.7, 109.5 → 109.6, 109.5 118.2, 118.4 → 118.4, 118.5
1 70.79, 64.27 → 70.95, 71.43 62.24, 59.01 → 60.98, 61.10 72.6, 76.5 → 72.6, 75.4 104.3, 103.6 → 104.0, 103.9 113.2, 113.6 → 113.3, 113.2

At T=0 the four launches draft the same rounds, accepted and drafted tokens. This head's bench_openai medians sit 0.4 to 0.8% below main's first launch, every run below every run of it, and above main's second, which ran both cases 3 to 4% below its first with no heat wait in any launch; the pairs lean opposite ways, so two launches a build can't separate them. Its bench_concurrent cells sit with main's two, the higher 0.1% above them at most. At T=1 the drafting differs from launch to launch, so those figures are for the record.

  • Prompt speed before and after: n/a. The form reaches only decode rows (cuda_moe.zig sets it only when the pass is not a prompt's), so prompt passes run the same kernels under either name.
  • Licence of any new weights or drafter: n/a.

Checklist

  • The title and description match the head commit, including the receipt and the stack.
  • New tests fail before the change, pass after it, and skip cleanly without their dependency. (The red copies above. None needs a GPU, a checkpoint or the process environment, so none skips.)
  • Comments and docstrings are one line. No measurements or history in the source.
  • No personal data, machine names, internal hosts or local paths in the diff or its commits. No AI attribution lines.
  • Every token still goes through the lane rounds. No serial path, and nothing that needs drafts off. (The name only picks the form the decode windows already use, with drafts or without.)
  • Drafted output equals "draft": false, a resumed prompt equals a fresh one, and concurrent equals solo. (bench_concurrent --alone --serial on the four drafted launches above; the name doesn't reach the prompt cache.)
  • No precision traded for speed. If the bits change, the description says which sums change and why. (No bits change: every launch's replies equal main's, named either way and unset.)
  • 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. (Unset, this head races to and keeps the same form as main: its round times are within 0.1% of main's, and its T=0 drafted figures can't be separated from main's two launches, as above. A named form is the caller's choice; the first table shows each form's round time by width, and a named form's drafted speed wasn't measured. The form reaches only decode rows.)

Bizuayeu added a commit to Bizuayeu/TensorFold that referenced this pull request Nov 13, 2026
…re at one form

GLM races its classic and factored NVFP4 expert forms when a build first loads and keeps the winner under a key that
carries the build's hash. The race's window sums can land inside its 1% margin, so the same code built twice can serve
different forms, and a comparison of two builds then carries the form along with the change it measures.

TF_GLM_EXPERT_FORM=classic or factored names the form: rank 0 sets it on every rank before the warm-up, skips the
race, and times the windows and the draft step at that form. A load that times nothing (no drafts, or too little
scratch context) still sends the named form to every rank and logs `glm: expert form (untimed)`; unnamed, such a load
stays on classic as before. The named form ends the cache identity, so its record and the race's never stand in for
each other; unset or blank keeps the race and its key as they were. The startup line keeps its words and puts
(TF_GLM_EXPERT_FORM) right after the form's word, which ends MTP's line. Any other text stops the load by name, as
GLM's other rows do.
@ashhart

ashhart commented Nov 14, 2026

Copy link
Copy Markdown
Owner

Thanks @Bizuayeu, this is a useful diagnostic switch, and it's kept tidy. A named TF_GLM_EXPERT_FORM is set once before the warm-up, the race is skipped, and the form is part of the price cache's identity, so a kept record is only reused at its own form. A load that times nothing still sends the form to every rank. A malformed value is refused by name at startup.

It's queued to land. It's safe because with the variable unset the cache keys, the timing calls and the startup lines are all exactly main's, and the form only picks which expert kernels run.

Optional: a test showing that a record kept at one form isn't reused at the other would check the behaviour directly, not just the startup line's text.

  • Synthena (Ash's Agent)

…sed at the other

The reuse check in price() moves into reuses(), which price() calls in its
place. The new test keeps a classic record under a classic load's key in a
temporary cache, then shows that a load naming factored finds nothing under
its own key, and that reuses() refuses a record kept at the other named form
while the race and the same form reuse it.
@Bizuayeu

Copy link
Copy Markdown
Contributor Author

Optional: a test showing that a record kept at one form isn't reused at the other would check the behaviour directly, not just the startup line's text.

#3151 (comment)

Added in 63dcb400ff: "a record kept at one named form is not reused at the other" keeps a classic record under a classic load's key in a temporary cache, then shows that a load naming factored finds nothing under its own key, and that the reuse check refuses a record kept at the other form while the race and the same form reuse it. The check moved into reuses(), which price() calls in its place. With either half removed, the test fails. The description has this head's gate.

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