Repository navigation
Conversation
|
Thanks @atakann, this is a careful piece of work. The profile came first, the keys hold every host value a window's launches take (stricter than the gate asked), and at TP=2 greedy, sampled, four-at-a-time and drafted replies all equal eager on one binary, across the 256-token, 512-token and 8,192-pool seams. Off by default with the eager path making the same launches is what we need. On TP=1: none of the GLM-5.3-Flash checkpoints we serve fits one GB10, so that half of the gate can't be reached there. We'll take TP=2 as the gate for this PR, and note that on #1490. A few things before it can queue:
|
|
Thanks @ashhart. Items 1 and 2 are in b1c436a, rebased on 34ea8cb:
With the local Item 3, the device test, is next. I'll post its shape here before the code. |
|
For item 3, the shape I'll take: |
|
Item 3 is in 5a039df, rebased on 882ca0a: Three graphed streams and their eager twins get the same prompts (500, 2,330 and 3,990 tokens). Each round runs one twin's window through the graph cache and the other's with the caches off, from the same state, and compares the logits' bytes, the drawn tokens and the drafts; both then keep the same rows. 48 lone rounds a stream with one key each (they cross the 512- and 4,096-key seams), 48 shared rounds of all three (one crosses a score grid's), then 90 mixed rounds of 1 to 4 rows, lone and 2 or 3 streams shared. At the end the twins' whole states are compared, and each cache must have captured, updated and replayed. On a GB10 at TP=1 with the FP4-Local checkpoint and A planted control: with the 512-key chunks left out of the key, it fails at round 4, the first lone window past 512 keys ( |
|
One note on what TP=2 covers: rank 0 turns its caches off for the twins' windows, and rank 1 keeps its own caches on for both, so on rank 1 the twins' windows run through graphs too. Rank 0's comparison therefore holds rank 0's graphs against its eager launches, with rank 1 graphed in both arms. |
|
Thanks @atakann, all three are in:
Running it at TP=1 on the cut-down checkpoint also covers the TP=1 half of #1490's gate for the window code. Two small things for the landing. We'll do both ourselves when we prepare it, unless you'd rather push them:
Before it lands, we'll run main against this branch at TP=2 on two GB10s with the switch off, to confirm the default path is unchanged. Optional, not blocking: the TP=2 check compares rank 0's graphs against eager while rank 1 replays in both arms. One run with the roles swapped would compare rank 1's graphs directly too. |
|
Our GLM-5.3-Flash NVFP4 run at TP=2 on two GB10s, main against this branch with
We've queued a prefill-only repeat at 2K and 8K, six runs in a balanced order, to tell the mode from the change. This lands if that's level. |
What this changes
Part of #1490, after the profile posted there.
TF_GLM_GRAPHS=1runs GLM's verify windows on CUDA as CUDA graphs on each rank: one stream's windows (Engine.verify) and several streams' windows in one pass (Engine.verifyShared). It is off by default, and with it unset every pass runs eagerly as on main.Forward.runis nowstage(each rank's state check, the rings' rebase when due, the ids upload) and thenkernels(the embedding to the head). The eager path calls both, so it makes the same launches in the same order as before.cuda/keyed_graph.zigis the cache fromflashnext/cuda_window_graph.zigas a shared helper. A key'sshapepicks the held graph, andmovingholds every other host value. The same key replays. New values record the pass again and update the held graph in place (cuGraphExecUpdate), and other launches get a new executable. A new slot runs eagerly once before its capture. Flash Next keeps its own copy in this PR; it can move onto the shared one in a follow-up.glm/cuda_window_graph.zigbuilds the keys. A key holds every host value the window's launches take: the rows, each stream's slot and buffers, its KDA layers' state halves, and its length's host geometry (the 512-key chunks, each latent layer's dense and sparse branches, the selection's pool bucket and score grid). So a held graph moves in place at most once every 256 tokens, and the rounds between replay it.TF_GLM_PROF, image rows or DFlash2's taps, prompt chunks, the MTP head's draft steps and the sampling, which reads the logits on the host.Receipt
Environment: two GB10s (driver 580.173.02), TP=2 over RoCE,
nvidia/GLM-5.3-Flash-FP4-Local@76348dd,--context 32768 --parallel 4, default drafts (MTP + copies),zig build native -Dsm=121(ReleaseFast), Zig 0.17.0, nvcc 13.3. Measured on main6ec5fa4cplus this change. Rebased on09e2f7fa: one commit touches these files (36a87df7, the test list incuda/root.zig), and the tests below ran on the rebased tree.Exactness, one binary,
TF_GLM_GRAPHSunset against=1: 14 requests (7 prompts of 93 to 7,808 tokens, each with drafts and with"draft": false), 700 tokens each withignore_eos. The replies cross the 256- and 512-token seams, the dense limit and the 8,192-token pool bucket."draft": falsereply (7 of 7).Decode speed, the same binary, N concurrent greedy requests of 256 tokens, the mean of two runs:
TF_GLM_GRAPHS=1(tok/s)This matches the idle time in the GLM-5.3-Flash on CUDA: capture decode and verify rounds as CUDA graphs (one rank and TP=2) #1490 profile (3.6% to 5.3%), most of it the GPU's own gap between queued launches.
Prompt speed: prompt chunks run eagerly, and
runmakes the same launches as before. First-token times in the load runs are the same in both arms.Memory: the lowest MemAvailable during the graph run was 19.1 GiB on each rank, about 1.4 GiB below the run without graphs.
Tests run, with the local
.zig-cachecleared, so no result came from the cache:zig build test --test-timeout 10m: 547/547 steps, 7143/7202 tests passed, 59 skipped, 0 failed.test-kernel-contract182/182,test-server-security test-pull-security29/29 (server-security 24 passed),fuzz-cortheon20/20.test-golden: 322/335 and 55/55 equal to server goldens, 0 unexpected differences.zig fmt --check build.zig zig,check-release-paths, a plainzig build,git diff --checkand the two Python unittests from the No GPU workflow are clean. Lean: 425 problems, 425 on the base, 0 new.cuda/keyed_graph_test.zig(Flash Next's cache test on the shared cache: replays, updates in place, a new executable on new launches; intest-runtime-host, 285/285) andglm/cuda_window_graph_test.zig(test-glm-graphs-host: the next tokens replay; a 512-key chunk, a score grid or the next request in a slot moves the graph in place; a KDA half, the sparse branch, other rows or another slot take another graph; a shared key follows its segments' order). Each fails by name when an expected count or key relation is changed.Not run, and why: TP=1 on one GB10, because the FP4-Local pack does not fit one GB10 (asked on GLM-5.3-Flash on CUDA: capture decode and verify rounds as CUDA graphs (one rank and TP=2) #1490 which checkpoint to use). Other GPUs and Windows hosts. Graphs under memory pressure: this cache has no floor yet (at most 128 graphs, cleared when full), so it stays opt-in.
Overlap: this merges cleanly with #2257 (32-row windows) and #2142. #2162 (
TF_GLM_L2PF) touches the same lines ofcuda_engine.zig(an import andverifyShared's forward), and its prefetch runs on a side stream during a window. If it lands first, I'll rebase and keep windows eager underTF_GLM_L2PFuntil the two are measured together.zig build -Dgpu=sparkfails on main (the-Dgpupath returns beforeprofile.wire), so these builds usedzig build native -Dsm=121. That fix is a separate PR.Checklist
"draft": false, a resumed prompt equals a fresh one, and concurrent equals solo. (resumed prompts not tested apart: this change does not touch prompt passes or restores)SUMMARYline is in the receipt. (no kernel change)