Skip to content

glm cuda: each decode window width keeps its faster expert form - #3249

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

Bizuayeu wants to merge 4 commits into
ashhart:mainfrom
Bizuayeu:pr/glm-expert-form-width

Conversation

@Bizuayeu

@Bizuayeu Bizuayeu commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

What this changes

GLM's CUDA load now races its classic and factored NVFP4 expert forms, which give the same bits, at each of its ten timed window widths and keeps the faster form at each, where it kept one form for every width. This is not a complete fix: where factored's lead at a width sits near the race's 1% margin, two launches of one build can keep different forms there. On four GB10s at TP=4 this receipt's two racing launches did so at 12 and 32 rows (factored, classic at 3, 4, 6 and 24 rows and classic, factored at 1, 2, 8 and 16 rows; across the two records, factored's time there is 1.1 and 1.2% under classic's), and two launches of a diagnostic build of the same race did at 8, 12, 16 and 32 rows. At 8 rows the two forms' decode rounds differ by about 2% in serving (the first table's 8-stream cell below), so a launch that keeps classic there gives that back. TF_GLM_EXPERT_FORM (#3151) names one form for every width when a comparison needs one.

Main's race sums the 2-, 4- and 8-row windows and one draft step on each form and runs every decode window on the winner. A sum hides the widths it loses: on TP=4, #3151's receipt timed each form on its own, and against classic, factored's rounds are 4.82% shorter at one stream, 1.67% longer at four and 2.18% shorter at eight. This head times both forms at 1, 2, 3, 4, 6, 8, 12, 16, 24 and 32 rows, keeps classic at a width unless factored is more than 1% faster there, and prices each width at its kept form's time. A window runs on the form of the narrowest timed width that holds it: rank 0 sends the forms to every rank as a bit a row count, and the price record keeps a bit a width. The record's key carries the build's hash, so no single-form build's record is read for its forms.

The race sweeps every width five times on both forms, untimed, before it times a window; then each width's timed windows take the two forms in turn, classic first on even rounds and factored first on odd ones. Timed one form after the other, the order picked the form: on TP=4, a log of every run showed classic's narrow widths up to 60% over their later time in a load's first 4 s, and a 4 to 6% drift through the race on one form. From an empty price cache, this head's two racing loads loaded in 57.9 and 58.1 s, and #3151's head's first launch in #3151's receipt, on the same ring and racing as main does, in 56.7 s; loads that read a record loaded in 43.0 to 43.8 s here. TF_GLM_EXPERT_FORM now names one form for every width: the warm-up sweeps that form ten times, the load times the windows and the draft step at it, and a load that times nothing sends it to every rank for every row count. The startup line names one form when every width shares it, else the more common form and the widths where the other runs, as in glm: windows (timed): … experts factored, classic at 4 and 6 rows.

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, two launches each:

Cell (a 4,096-token prompt) #3151's head this head this head's form there, against #3151's
1 stream 27.699, 27.809 26.407, 26.397 factored, classic
4 streams 46.898, 47.196 46.919, 46.861 classic, classic
8 streams 73.277, 73.321 71.675, 71.591 factored, classic

Here #3151's head raced to classic for every width (glm: windows (timed): … experts classic), and this head's launches read the second race's record, factored at 1, 2, 8 and 16 rows. Taking a cell as different only where both launch pairs lean the same way by more than the larger of the two builds' launch-to-launch gaps, this head's rounds are 4.87% shorter at one stream and 2.27% shorter at eight, where the forms differ, and at four streams, where both keep classic, the two can't be separated (-0.33%). In #3151's receipt main and #3151's head raced to factored, and against that draw the forms differ at four streams instead, where that receipt timed factored's rounds 1.67% longer than classic's. So the grid ran again: #3151's head with TF_GLM_EXPERT_FORM=factored, which runs factored at every width, and two more launches of this head, which read the second race's record:

Cell (a 4,096-token prompt) #3151's head, factored named this head this head's form there, against #3151's
1 stream 26.666, 26.405 26.381, 26.380 factored, factored
4 streams 47.917, 48.642 46.989, 46.954 classic, factored
8 streams 71.676, 71.774 71.761, 71.703 factored, factored

By the same rule, this head's rounds are 2.70% shorter at four streams, where it keeps classic against factored: #3151's head's two launches there sit 1.5% apart, and every rep of this head's two is under every rep of theirs. At one and eight streams, where both run factored, the two can't be separated (-0.58% and +0.01%). Every launch's replies are the same.

Tests:

  • New (glm_lane_costs_host.zig): "each width keeps the form its windows ran faster on, and classic inside the margin", "a dip is timed again at each width's own form, not the last raced one", "a slow start outlasting the old warm-up decides no width's form and sets no price", "a drift through the race falls on both forms alike", "a window run slow right after the other form decides no width's form" and "a window takes the form of the narrowest timed width that holds it"; and in cuda_lane_costs.zig, "a raced line names the more common form and the widths where the other runs".
  • Changed: "DFlash2 races the forms at every width without timing head steps or keeping prices" and "MTP keeps each width's raced time as its price, then the two-level head slope" (were "DFlash2 races expert forms without timing unused windows or head steps" and "MTP retains every timed width and the two-level head slope"); the untimed test now wants every row count's bit for the named form.
  • Red, on the tree before the rebase: one copy of that head a break, each run on test-glm-lane-costs-host (10 tests) unless named. One form for every width, the more common one, as main keeps: "each width keeps…" (expected 998, found 1023) and "a dip is timed again…" fail. The kept forms not set before the prices are smoothed and the rows sent: the same two. No sweeps: "a slow start…" (actual 36, expected 21). Classic first in every round: "a window run slow right after the other form…" (expected 1023, found 0). Each form's table timed in one block, as main times its forms: "a drift through the race…" (expected 0, found 1023). Every width priced at classic's time: the MTP price test and the three others that read prices. Only the 2-, 4- and 8-row widths raced, as main races: six, the DFlash2 and MTP race tests among them. A window on the widest timed width under it instead of the narrowest that holds it: "a window takes the form…" (expected 8, found 24). The startup line's majority counted at half the widths: only "a raced line names the more common form…" fails, in test-cuda-host (6,365 of 6,396). A copy that ignores the name in four places (as glm cuda: TF_GLM_EXPERT_FORM names the expert form, so launches compare at one form #3151's red copy did, at this head's code) fails exactly glm cuda: TF_GLM_EXPERT_FORM names the expert form, so launches compare at one form #3151's four naming tests in test-cuda-host (6,362 of 6,396) and the named-form test in test-glm-lane-costs-host (9 of 10, expected 1, found 241).

Checks on this head

  • Head commit: bc6349146d, the CHANGELOG line on top of e259495f26. It changes no code. The gate below ran on e259495f26's tree, and the lean checker and the overlap on this head; the receipt ran the change before the rebase, 4f9832ca00.
  • Lean checker, python3 tools/zig/lean_check.py, on the base (f935ab25cf) and on this head: 341 and 341, no new findings. On main 4cfd955737 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 e259495f26's 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,322 of 9,391 with 69 skipped, test-kernel-contract 238 of 238, test-cuda-host 6,454 of 6,484 with 30 skipped). pytest from the root with the CI's options plus -rs (-q -rs -p no:cacheprovider): 450 passed, 86 skipped, the same skips and reasons as glm cuda: TF_GLM_EXPERT_FORM names the expert form, so launches compare at one form #3151's gate. Main's CI pytest at 8642b1ee81, the newest main pytest that has finished, has 452 passed and 84 skipped, 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: zig, zig-windows, zig-macos-dev-tools, pytest, release-paths, both benchmark-clients jobs and fmt passed; the two nightly host jobs report skipped.
  • Not run, and why: Metal, HIP, Vulkan and XPU builds and tests, with no such machine here. Only GLM's CUDA load races the forms (price in families/glm/cuda_lane_costs.zig), and only GLM's CUDA decode windows read them (cuda_moe.zig).

Receipt

T bench_openai fibonacci-raw bench_openai gpu-chat-no-think bench_concurrent 1 stream 4 streams 8 streams
0 53.68, 53.69 → 54.14, 53.91 43.00, 43.02 → 43.48, 43.29 64.9, 65.1 → 65.3, 65.0 108.9, 109.1 → 109.1, 109.0 118.5, 118.5 → 117.0, 118.9
1 70.04, 71.16 → 71.38, 72.05 60.76, 58.02 → 62.01, 61.21 74.5, 74.7 → 77.4, 73.8 102.5, 103.3 → 103.5, 103.3 111.6, 112.7 → 113.5, 112.6

At T=0 the four launches draft the same rounds, accepted and drafted tokens. By the same pair rule, this head's bench_openai gpu-chat-no-think median is 0.87% higher; fibonacci-raw (+0.64%) and the three bench_concurrent cells can't be separated. At eight streams one launch of this head sat at 117.0, every rep of it under every rep of #3151's head's two, and the other at 118.9, over them. 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 on either build.
  • 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 forms only pick the kernels a decode window already runs, 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 forms reach only decode rows, which give the same bits on either.)
  • 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 glm cuda: TF_GLM_EXPERT_FORM names the expert form, so launches compare at one form #3151's head's.)
  • 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. (Without drafts this head's rounds are 4.87% shorter than glm cuda: TF_GLM_EXPERT_FORM names the expert form, so launches compare at one form #3151's head's at one stream and 2.27% shorter at eight, and the two can't be separated at four; against glm cuda: TF_GLM_EXPERT_FORM names the expert form, so launches compare at one form #3151's head naming factored, they are 2.70% shorter at four and can't be separated at one and eight. With drafts at T=0 one figure is 0.87% higher and the other four can't be separated; at eight streams one launch of this head sat 1.3% under glm cuda: TF_GLM_EXPERT_FORM names the expert form, so launches compare at one form #3151's head's two, every rep of it under every rep of theirs, and the other above them. The form reaches only decode rows. Where factored's lead at a width sits near the race's margin, two launches can keep different forms there, as the first paragraph says.)

…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.
GLM's load-time race summed the 2-, 4- and 8-row windows and one draft step of its classic and factored NVFP4 expert
forms, then ran every decode window on the winner. A sum hides the widths it loses: at TP=4 the factored form wins at
1 and 8 rows but loses at 4 and 6, so four streams without drafts decode about 2% slower than on classic.

Both forms give the same bits (the NVFP4 fact contract holds the factored tiles and waves to the decode form's sums),
so each window can take either. The race now times both forms at all ten widths, keeps classic at a width unless
factored is more than 1% faster there, and prices each width at its kept form's time instead of timing the table
again. A window runs on the form of the narrowest timed width that holds it: rank 0 sends the forms as a bit a row
count, and the record keeps a bit a width. Records carry the build's hash in their key, so no record of a single-form
build is read for its forms.

The race sweeps every width five times on both forms, untimed, before it times a window, then each width's timed
windows take the two forms in turn, classic first on even rounds and factored first on odd ones. Timed one form after
the other, the order picked the form: on four GB10s at TP=4 a log of every run showed classic's narrow widths up to
60% over their later time in a load's first 4 s, and a 4 to 6% drift through the race on one form, so two launches of
one build kept different forms at 3, 4, 6 and 12 rows. Five sweeps of both forms take about 5.4 s there.

TF_GLM_EXPERT_FORM now names one form for every width: the warm-up sweeps that form ten times and the load times the
windows and the draft step at it, and a load that times nothing sends it to every rank for every row count. The
startup line names one form when every width shares it, else the more common form and the widths where the other runs
(experts factored, classic at 4 and 6 rows).

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.

1 participant