Repository navigation
Conversation
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
Full GLM-5.3 (EXLR8,
--speed-up) never serves on two M5 Ultra Mac Studios: rank 0 hangs while loading and rank 1 then needskill -9. The cause is the one confirmed in #3158:pair_post_waitincore/pair.metalwaits for the peer inside a running command buffer by polling memory the host writes. This change removes that GPU-side wait. Each exchange now retires its producer command buffer, waits for the peer on the host, and encodes the consumer into a new command buffer only after the host has published the exact 64-bit receive epoch. A silent or dead peer ends in a controlledPairLinkFailedon both ranks.exchange 2: PeerSilentand rank 0 never serves #3158. That issue reports the GLM-5.3-Flash pair, which glm metal: use shared events for paired transfers #3293 fixes. This is the same root cause on the full GLM-5.3 path (families/glm_dsa,core/pair.zig,core/pair.metal), which glm metal: use shared events for paired transfers #3293 does not touch.cf2c65bd3e562b9cfd621f56b8af6c45a71bfc3f. This pull request adds one commit,786950bc62be9e9fd32c51fb700c0c56a1d55034(25 files, +1768 −170). The four earlier commits on the branch belong to glm metal: use shared events for paired transfers #3293 and are not new scope here. The branch merges cleanly ontomainat2ba55ec7d32d0bec446bb012678550be7ef52895.atc overlap --jsonRun with atc 0.9.6 on 2026-11-14 UTC against the stack base. The repository-wide run and the selected run (--pulls) both returnedcomplete: false(timeLimit,conflictChecksUnavailable) and listed 14 open pull requests sharing files, each with conflictunknown. I then test-merged this head and its base against those 14 plus qwen27: serve the 27B pair, refuse what a pair cannot keep, and die with the peer #1434, glm metal: one arithmetic order for prompt windows, chunks and decode on M1-M4 (#743) #2128 and metal families: refuse a token id at or past the embedding table before the lookup #3045 withgit merge-tree. One new conflict appeared, with Implement GLP control vectors with a fused Metal kernel for Qwen3.8-27B #1835 inzig/tests/contract/metal_limits.zig; moving this change's branch of thatelse ifchain below the neighbouring one removed it. On the pushed head no pull request in that set has a conflict that it does not already have with glm metal: use shared events for paired transfers #3293's head. glm metal: one arithmetic order for prompt windows, chunks and decode on M1-M4 (#743) #2128 keeps its known conflict inzig/src/families/glm/prompt.zig, inherited from glm metal: use shared events for paired transfers #3293.Why not shared events inside one command buffer, as in #3293
The first build of this change did that, and it passed one start. On the second start a command buffer accumulated enough handoffs to sit in an event wait for about 5 s and the GPU watchdog killed it (
kIOGPUCommandBufferCallbackErrorTimeout). The build in this pull request has no GPU peer waiter at all, so there is nothing for the watchdog to kill.Shared code
core/pair.metalandcore/pair.zigare used in production only by the full GLM-5.3 engine and model, and both callers are converted together.core/pair_rows.*and the existingmetal paircontracts are unchanged.Checks on this head
786950bc62be9e9fd32c51fb700c0c56a1d55034.python3 tools/zig/lean_check.pywith the head's checker: 341 findings on the basecf2c65bd3, 341 on this head.tools/zig/lean_new.pyreports 0 new.zig build test-glm-dsa-pair -Dcpu=apple_m1 -j4 --summary all: 43/43 steps, 73/73 tests passed, 0 skipped.zig build test test-golden -Dcpu=apple_m1 -j4 --summary all: 851/853 steps; 13,791 of 13,902 tests passed, 109 skipped, 2 failed. The same command on the base: 13,771 of 13,882 passed, 109 skipped, the same 2 failed (core.paced_read.test.aligned spans copy the bytes asked for, at any offset and lengthandfamilies.glm.weights_fix_test.test.GLM copies and transforms join before shard eviction; errors and duplicate opens retain ownership). Both failures are inherited. This head adds 20 passing tests.zig fmt --check build.zig zigandgit diff --checkpass.python3 tools/pr_check.py --base cf2c65bd3: 25 changed files, lean 341 vs 341, 0 new; 0 errors, 1 advisory warning (the Unreleased line, which needs this pull request's number).zig build kernel-contract -Dcpu=apple_m1) exits 1 on this M4 Pro withUnrecordedRotaryCompilerbefore reaching any kernel, on the base as well as on this head. The filtered device runs below cover every changed kernel and the othermetal pairkernels.mainwas not run on the Studios. The "before" run used the glm metal: use shared events for paired transfers #3293 head build;core/pair.metal,core/pair.zig,families/glm_dsaandnative/glm_dsa_host.zigare identical between that commit andmainat2ba55ec7d.Receipt
--context 8192 --parallel 4).cf2c65bd3): rank 1 loaded in 396 s (159.6 GB) and reported serving. Rank 0 never finished loading; after 20 minutes its main thread was infamilies.glm_dsa.engine.Engine.chunk > Engine.finish > -[_MTLCommandBuffer waitUntilCompleted]. After rank 0 was stopped, rank 1 did not exit on SIGTERM within 30 s and neededkill -9.0470bd50ca3ad67d88bf04076a5e7c0362a4afcb4b8dbbae20b2cdd0fdf4b4eb), every start stopped both ranks on SIGTERM with nokill -9and no GPU watchdog message.core/pair.*.786950bc6oncf2c65bd3; Zig 0.17.0; two Mac Studio M5 Ultra, 256 GB, macOS 27.2,rdma_en3over Thunderbolt 5; checkpointkindlingai/glm-5.3-exlr8-k2-k3-k4@f459f5d0272a529403dd9b3fd32f771f4250a0d4; greedy, integrated drafts.tools/bench_concurrent.py http://127.0.0.1:18792 glm-5.3 --tokens 256 --reps 3 --temperatures 0 --levels 1,4 --alone --serial: 0 failed. One stream: 3/3 replies equal to the request alone, and equal to"draft": false. Four streams: 12/12 equal to the request alone, and equal to"draft": false. The same holds with thinking off. Separately, two prompts sent with drafts on and off, two repeats each, returned identical 128-token visible replies and token hashes, and both ranks logged equal pick counts and pick hashes on every run (for example 17,273 picks, hash08dd0396…d56a, on the 75-request run).main's path does not serve on these machines, so there is no decode number to compare with.tools/bench_openai.py … --tokens 256 --reps 3 --temperatures 0, median of three, one stream: 8.92 tok/s (fibonacci-raw), 7.85 tok/s (gpu-chat-no-think).bench_concurrent.pyat four streams: 33.9 tok/s aggregate, 8.7 per stream; 30.4 aggregate with thinking off. Round gap p50 is 314 to 324 ms at about 2.5 to 2.7 tokens per round. This is below the 21 and 31 to 32 tok/s indocs/glm-5.3-exlr8.md. I do not know how much of the gap is this change (each exchange now pays a command-buffer boundary) and how much is these machines; there is no working baseline here to separate them. Server-reported decode on other prompts ranged from 6 to 31 tok/s and was episodic within a run, with no established cause.tools/bench_cache.py … --require-cold, 2,570 prompt tokens, zero cached tokens reported:ttft_s13.21, 13.12 and 12.99 across the first and two repeated requests. No before number, for the same reason.PeerDownand returnedPairLinkFailedin 0.55 s, then stopped normally. SIGSTOP to rank 0 while rank 1 was idle: rank 1's follower returnedPairLinkFailed2 s later. A 75-request run including 60 consecutive mixed requests completed with no error.Kernel contract
pair_stageis new.pair_add_rms,pair_argmaxandpair_pickreplace their same-named versions, which called the pollingpair_post_wait; that function is removed. Each consumer now takes the host-published 64-bit epoch and refuses a slot whose epoch does not match.metal glm dsa pair; each fails as planted.zig build tf-kernel-contract -Dcpu=apple_m1, thentf-kernel-contract --only 'metal glm dsa pair'andtf-kernel-contract --only 'metal pair'.--only 'metal glm dsa pair', exit 0 eachNot done
decode.json,concurrent.json,cache.json) and the run logs are available on request; they are not attached yet.