Skip to content

glm: TF_GLM_PREFILL_REDUCE=ring, for fabrics where a rank reaches only its neighbours - #902

Merged
ashhart merged 2 commits into
ashhart:mainfrom
Bizuayeu:pr/glm-ring-split
Oct 18, 2026
Merged

ashhart merged 2 commits into
ashhart:mainfrom
Bizuayeu:pr/glm-ring-split

Conversation

@Bizuayeu

@Bizuayeu Bizuayeu commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

The second follow-up promised on #804 (comment), as two commits on main: 3b0093a3, and 98a5dc65 from review (a failed post still ends its NCCL group).
@SvangenStudios offered to run it on the ring of four (comment).

What this changes

TF_GLM_PREFILL_REDUCE=ring is split with its exchanges passed along the ring, for a fabric where a rank reaches
only its two neighbours. A piece's partials take world - 1 steps: at each, a rank sends its next neighbour the blocks
still on their way and keeps the one meant for it in the slot of its room that split's direct receive fills, so the
owner sums every rank's block in rank order, as with split: the same bytes in the same order, and the same bits. The
glued rows go back as a ring all-gather, each step's rows straight into place. Blocks in transit sit in a piece's room
past split's slots (split uses about 1/world of it), so ring takes no new memory. cuda_relay.zig holds the schedule
and the two exchanges; the fingerprint's split field is 2 for ring, so every rank must pick it. Each step's postings
go through Library.group (cuda/nccl.zig), which ends the group when a post fails, so a failed post never leaves
later NCCL calls inside it.

With #899 also in, ring should skip that probe, which tries every pair; whichever of the two lands second makes the
probe run for split only and names ring in its refusal.

Receipt

  • Environment: main at 46a08c18 (TensorFold 1.0.12) plus this change; three DGX Spark (GB10, sm_121, CUDA 13.3)
    over RoCE, where at TP=3 every pair of ranks is linked; nvidia/GLM-5.3-Flash-NVFP4 at revision 423acf37.
  • Exactness: at TP=3 the concurrent check ran with split and then with ring (each rank's log naming its setting),
    and every solo run's token ids, greedy and sampled, hashed the same under both, byte for byte (a test-only print, not
    in this branch), and the same as before this branch moved onto main. The shared check passes with ring. Split, the
    default, is unchanged.
  • Decode speed before and after: n/a, decode windows take the whole exchange, not the pieces ring changes.
  • Prompt speed before and after: split, the default, is unchanged; ring was not timed (on our hosts every pair is
    linked, so it only adds hops).
  • Tests run, at 98a5dc65: on one host zig build test-cuda-host 855 passed (852 at main), zig build test 1269
    passed and 7 skipped, with three new tests (the relay simulated for 2 to 6 ranks: every owner gets every rank's
    block in split's slot with the transits inside the room, and the glued rows reach every rank; and both exchanges
    against a fake NCCL failing each call in turn, every rank of worlds 2 to 6: the error returns with the groups
    balanced and nothing posted after it), zig fmt --check clean, glm-ref and the thin and synthetic lanes checks;
    at TP=3 the concurrent check with split and with ring (41 PASS each) and the shared check with ring; at TP=2 the
    concurrent check with split and with ring (39 PASS each, the same ids: at two ranks ring posts what split posts);
    each rank's log naming its setting; 0 failed. lean_check: 516, as main.
  • Not run, and why: a ring of four, where some pairs are not linked (@SvangenStudios offered to run it, against
    gather on the same build); a peer lost during an exchange (ending the other ranks then is fabric: tensor parallelism across CUDA processes (bootstrap, NCCL, mailbox and RoCE collectives, plan link, fail-fast) (DeepSeek-V4.1 stack 3/14) #636's fate channel).

Checklist

  • Every token still goes through the lane rounds. No serial path, and nothing that needs drafts off.
  • Drafted output equals "draft": false, a resumed prompt equals a fresh one, and concurrent equals solo (the lanes checks above, with ring at TP=3).
  • No precision traded for speed. If the bits change, the description says which sums change and why (no sum changes: the owner adds the same blocks in the same order).
  • Prompt processing is no slower than the last release (split, the default, is unchanged; ring is opt-in).
  • The description says which platforms I ran, Metal M1 to M5 and CUDA, and which I could not (CUDA on GB10; the family is CUDA's).
  • New tests fail before the change, pass after it, and skip cleanly without their dependency (the relay's three host tests; the fake NCCL one fails with the first commit's relay).
  • Comments and docstrings are one line. No measurements or history in the source.
  • No personal data, machine names, internal hosts or local paths. No AI attribution lines.

@ashhart

ashhart commented Oct 16, 2026

Copy link
Copy Markdown
Owner

Thank you, @Bizuayeu, for relaying the original rank blocks into their existing slots, so the ring keeps split's reduction order.

At 691cfd7, the routing and room offsets look consistent, including unequal row shares. One error path remains: families/glm/cuda_relay.zig:58 opens an NCCL group, but a failed send or receive returns before GroupEnd. The same pattern occurs in the glued-row exchange.

Please add shared group cleanup and rank-wide failure handling, with a failure injected at each posting. We can fold that small fix in at landing. The relay planner should also move into shared core when DeepSeek uses it, preserving GLM's existing glue arithmetic.

We'd compare ring against split on real two-rank and larger advertised configurations, with uneven pieces and prompt cuts, then check resumed/fresh and concurrent/solo tokens and a peer failing during an exchange.

Posted by Ash's AI Assistant Synthena

@Bizuayeu

Copy link
Copy Markdown
Contributor Author

@ashhart, thank you. Pushed ea26c0be on top of 691cfd7d:

Re-run at ea26c0be on our GB10s: at TP=3 the concurrent check with split and with ring (41 PASS each, the solo runs' ids the same bytes, and the same as at 691cfd7d) and the shared check with ring; at TP=2 the same pair (39 PASS each, the same ids); each rank's log names its setting. On one host test-cuda-host 514 passed and zig build test 600 passed and 5 skipped; lean_check as #804. The description says so.

…ng, for a fabric where a rank reaches only its neighbours
…ay out of it; the ring relay posts through it, and a fake NCCL fails each posting in turn
@Bizuayeu
Bizuayeu marked this pull request as ready for review October 16, 2026 13:09
@Bizuayeu

Copy link
Copy Markdown
Contributor Author

Rebased onto main as 3b0093a3 and 98a5dc65: the fingerprint's of and one test's call to it took main's new mtp argument; the relay and Library.group are unchanged. At TP=3 and TP=2 ring's ids again equal split's, and both are the same as before the rebase. The receipt above is updated. Ready for review.

@ashhart ashhart mentioned this pull request Oct 17, 2026
@ashhart
ashhart merged commit 08c2e69 into ashhart:main Oct 18, 2026
4 checks passed
ashhart added a commit that referenced this pull request Oct 18, 2026
…ing start on a neighbours-only fabric loads

Settings.read sets split and ring for TF_GLM_PREFILL_REDUCE=ring, and the engine's start guarded #899's probe with split
alone, so a ring start still exchanged a value with every rank before loading: a ring of four without a switch, whose
ranks reach only their neighbours, could not start in the mode #902 built for it (two ranks cannot tell the two apart).
probeStart probes for split without ring, and the refusal now names ring or gather. Host tests over a fake fabric: a
direct split start is refused at every rank of a four-rank ring and starts behind a switch and on two ranks; ring and
gather starts on the four-rank ring post nothing before loading.

Co-authored-by: Bizuayeu <30091157+Bizuayeu@users.noreply.github.com>
ashhart added a commit that referenced this pull request Oct 18, 2026
…ollow-ups named as ours

After review of the fourteen lines that overstated: #710's fresh-quantization gain is one measured case; #902's stop
and group cleanup hold where the watch lines are open and through the shared helper; #887 says its chunks change the
attention bits below 8,192, on M5 too, with no larger error against float64; #899 records the ring start and the
wider refusal; #802 keeps the author's B70 receipts apart from our host-checked lifetime fixes; #836's equalities are
the GB10's and its Windows build the author's; #888 says a failed restore's pinned memory is kept, never freed; #734's
tree rounds are checked against the host's, not float64; the Living Weights lines (#876, #877, #878, #891) say which
runs their numbers come from, the ten-fact costs, and the M3 Ultra's four-fact result; #946 and #910 read plainer.
@ashhart

ashhart commented Oct 18, 2026

Copy link
Copy Markdown
Owner

Thank you, @Bizuayeu, for the ring relay and per-posting failure controls.

We merged the work for 1.1.0 in 08c2e69. Our follow-up shares the relay plan, routes split/scatter/broadcast postings through one group helper and connects NCCL errors to the rank watch. Active watch lines notify peers before a local fatal exit; returning failures still end their group.

The relay retains the rank-slot sum order, and ring startup skips direct split's all-peer probe.

Rank-watch controls and two-GB10 EXL3 serving passed; four-rank relay and live killed-peer comparisons are not recorded.

Posted by Ash's AI Assistant Synthena

@Bizuayeu

Copy link
Copy Markdown
Contributor Author

Rank-watch controls and two-GB10 EXL3 serving passed; four-rank relay and live killed-peer comparisons are not recorded.

(#902 (comment))

The live killed-peer comparison with TF_GLM_PREFILL_REDUCE=ring beside split, on 1.1.0 at two GB10s, is in #899: #899 (comment). Each survivor stopped on the closed watch line within 1.2 to 1.4 s, idle or with a prefill or a decode in flight. The four-rank relay is not covered there either: our three hosts could not run 1.1.0 beyond TP=2.

BlivionIaG pushed a commit to BlivionIaG/TensorFold that referenced this pull request Oct 20, 2026
…s along the ring, each step's postings in one group that a failed post still ends
BlivionIaG pushed a commit to BlivionIaG/TensorFold that referenced this pull request Oct 20, 2026
…ing start on a neighbours-only fabric loads

Settings.read sets split and ring for TF_GLM_PREFILL_REDUCE=ring, and the engine's start guarded ashhart#899's probe with split
alone, so a ring start still exchanged a value with every rank before loading: a ring of four without a switch, whose
ranks reach only their neighbours, could not start in the mode ashhart#902 built for it (two ranks cannot tell the two apart).
probeStart probes for split without ring, and the refusal now names ring or gather. Host tests over a fake fabric: a
direct split start is refused at every rank of a four-rank ring and starts behind a switch and on two ranks; ring and
gather starts on the four-rank ring post nothing before loading.

Co-authored-by: Bizuayeu <30091157+Bizuayeu@users.noreply.github.com>
BlivionIaG pushed a commit to BlivionIaG/TensorFold that referenced this pull request Oct 20, 2026
…ollow-ups named as ours

After review of the fourteen lines that overstated: ashhart#710's fresh-quantization gain is one measured case; ashhart#902's stop
and group cleanup hold where the watch lines are open and through the shared helper; ashhart#887 says its chunks change the
attention bits below 8,192, on M5 too, with no larger error against float64; ashhart#899 records the ring start and the
wider refusal; ashhart#802 keeps the author's B70 receipts apart from our host-checked lifetime fixes; ashhart#836's equalities are
the GB10's and its Windows build the author's; ashhart#888 says a failed restore's pinned memory is kept, never freed; ashhart#734's
tree rounds are checked against the host's, not float64; the Living Weights lines (ashhart#876, ashhart#877, ashhart#878, ashhart#891) say which
runs their numbers come from, the ten-fact costs, and the M3 Ultra's four-fact result; ashhart#946 and ashhart#910 read plainer.
@Bizuayeu
Bizuayeu deleted the pr/glm-ring-split branch October 25, 2026 07:27
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