Repository navigation
glm cuda: at three ranks or more, TF_GLM_ROCE_MULTI=1 sends the small all-gathers over fabric/tp's multirank RoCE - #1524
Conversation
2ceff26 to
a634d64
Compare
|
Rebased on main |
|
Thanks @Bizuayeu. GLM still uses NCCL above two ranks by default, and no GPU source changes, so this is host code only. Merged with main, the Linux server and runner compile. Two things before we run it on device:
This branch carries #1516's first five commits, so we'll land #1516 once and then this on top. |
a634d64 to
cec391f
Compare
Done in
|
|
Hi @Bizuayeu, a status update on #1524. #1516 landed tonight, so this branch can now sit on main. We read its own diff on top of #1516. The default path Could you rebase it onto current main? After that, the run still owed needs two GB10s at once: GLM at TP=2 with |
cec391f to
4716504
Compare
Rebased onto main Checks on the rebased head (Linux x86_64): If it helps, I can take the TP=2 run here on two GB10s (the setting unset, against main: token hashes and decode). |
…thers, every rank or none Group.multi is a family's wire beside NCCL (fabric/tp's multirank RoCE for GLM): allGather sends what it fits down it, check, abort and close reach it, and it is set only after every rank's probe agreed.
…ny opens the borrowed wire
…rs to fabric/tp's multirank RoCE, every rank or none At three ranks or more GLM's small gathers all go to NCCL. With TF_GLM_ROCE_MULTI=1 each rank now opens fabric/tp's bootstrap at TF_TP_MASTER (GLM's rank and world in place of TF_TP_RANK and TF_TP_WORLD), agrees the wire's settings, picks its ports, brings up roce_multi's wire and kernel (TF_TP_KERNEL_IMAGE, as DeepSeek reads it), and probes one 4 KiB in-place gather on the wire against the group's NCCL. Every step ends in a vote over the bootstrap, so a rank that fails at any of them leaves every rank on NCCL (TF_TP_ROCE_FALLBACK=error refuses instead); the probe's vote is the last thing that can fail, and only then is the wire lent to the group's `multi`. ranks.Settings refuses TF_GLM_ROCE_MULTI=1 without TF_TP_MASTER and TF_TP_KERNEL_IMAGE before the weights load. The wire's waits are fabric/tp's: bounded by time and by its abort flag, and a failed one's check names the rank and the seq it waited on. The lent wire logs its first gather once, and its account (gathers, bytes, the proxy's last post, whether it failed, each HCA's queue pairs) when it is closed or aborted.
…mismatch keeps every rank on NCCL The vote is unchanged; as its own function it runs on host threads over fabric/tp's bootstrap. Three ranks vote with rank 1's probe failing, and every rank refuses the wire, keeping NCCL; with every probe matching, every rank lends it. A vote on each rank's own probe alone leaves ranks 0 and 2 lending the wire and fails the test.
…, and a failed NCCL gather or wait refuses
4716504 to
41b725a
Compare
|
Thank you @Bizuayeu. This is in TensorFold 1.2.0, released today: https://github.com/ashhart/TensorFold/releases/tag/v1.2.0 It landed with your commits under your own name, so you're listed as a TensorFold contributor, and it's credited to you in the release notes and the changelog. |
On three GB10 in a ring (GLM-5.3-Flash XSFP4 at TP=3, one binary served with
TF_GLM_ROCE_MULTI=0and=1, two launches each, the same tokens in every launch):=0, as main)=1)--no-drafts(tok/s)--no-drafts(ttft, s)What this changes
At three ranks or more GLM's small all-gathers all go to NCCL; the RoCE gather in
cuda/roce.zigstops at two. WithTF_GLM_ROCE_MULTI=1, each rank borrows fabric/tp's multirank RoCE for the main stream's small all-gathers.tp_group.Groupgets amultiwire beside NCCL:allGathersends what fits down it, andcheck,abortandclosereach it. Each rank opens fabric/tp's bootstrap atTF_TP_MASTER(GLM's rank and world in place ofTF_TP_RANKandTF_TP_WORLD), agrees the wire's settings, picks its ports, brings up the wire and its kernel (TF_TP_KERNEL_IMAGE, as DeepSeek reads it), and probes one 4 KiB in-place gather on the wire against the group's NCCL. Every step before the probe's gathers ends in a vote over the bootstrap, so a rank that fails at any of them leaves every rank on NCCL (TF_TP_ROCE_FALLBACK=errorrefuses instead). The probe's buffer, stream and uploads are ready on every rank, by a vote, before any rank enters a gather; then every rank enters both, NCCL's even when its wire gather fails to enqueue. From there a failed NCCL gather or wait refuses whateverTF_TP_ROCE_FALLBACKsays, since a peer may be waiting in NCCL, and the ranks' verdict on the bytes is the last vote: only when every rank's probe matched is the wire lent. The settings refuseTF_GLM_ROCE_MULTI=1withoutTF_TP_MASTERandTF_TP_KERNEL_IMAGEbefore the weights load. The wire's waits are fabric/tp's: bounded by time and by its abort flag, and a failed one's check names the rank and the seq it waited on.It stays opt-in. Decode gains, but a cold long prompt runs 1.2 to 1.6% slower with the wire open, although prefill sends nothing down it. #1288's comments trace that to moving the wire's queue pairs to ready-to-receive on the NIC functions NCCL also uses, a cost that outlives the queue pairs (opening them, or running the proxy, leaves nothing); keeping the two on separate functions removes it but costs NCCL a rail, 9.75% on this ring's cold prompt (two rails, not merged; #1288). So NCCL keeps every world above two by default until a cold-prompt receipt shows no loss.
This builds on #1516, which lets
roce_multiconnect on a switchless ring. #1516 has landed, so this is now its own six commits on main581cbe65c. Part of #1288, route (b).Receipt
cec391feb(fabric/tp: multirank RoCE over a switchless ring, each peer's stripes on the HCAs that reach it #1516's six commits on maine4cc630f5, then the six here); GLM-5.3-Flash XSFP4 at TP=3, PARALLEL=4,--prompt-cache-gib 0; three GB10 cabled in a ring of three QSFP cables with no switch, each port seen as two RoCE v2 interfaces on its link's two /24s;zig build nativefor sm_121, and fabric/tp'smailbox.cubuilt as the kernel image. One binary served withTF_GLM_ROCE_MULTI=1(andTF_TP_ROCE_FALLBACK=error, so a wire that does not open refuses) and with=0, launched wire, NCCL, NCCL, wire with the drafts and again with--no-drafts.bench_openai's medians over its two prompts (256 tokens, 3 repeats, temperature 0), and every wire launch beat every NCCL launch. One stream (bench_concurrent): code +3.40% and chat +1.68% with the drafts, +1.64% and +2.83% without; four streams: code +2.54% and chat +0.39% with the drafts, +0.83% and +1.84% without (the launches overlap in chat with the drafts and in code without). After the cold 136k prompt, decode is 2.41 to 3.40% faster with the drafts and 0.48 to 1.51% without.bench_cache --require-cold) is 1.57% slower in its first phase with the drafts and 1.24% without, and 1.3 to 1.5% slower in its repeated and concurrent phases. No launch waited on the engine's heat guard (noheat_wait_son any rank).test-roce-multi-host40/40,test-tp130/131 (1 skipped: the two-rank verbs run, which needsTF_TP_ROCE_TEST_HCA),compile-config-server7/7,test-cuda-host268/268 steps (3,825 of 3,834 tests, 9 skipped),zig build test434/434 steps (1,608 of 1,624, 16 skipped),compile-cuda-tests10/10 steps (the CUDA runner, which CI does not build),zig fmt --check, andtools/zig/lean_check.pywith nothing new (426 against maine4cc630f5's 426, and 425 against7c0688951's 425 merged with it). The probe test fails with the readiness vote taken out: a failed prepare on one rank leaves another lending the wire.Checklist
"draft": false, a resumed prompt equals a fresh one, and concurrent equals solo. (The token hashes agree across the drafted and plain launches, on the wire and on NCCL. The prompt cache was off and the four-stream runs were not hashed; the wire only copies bytes, so resumed and concurrent prompts take the same sums as on NCCL.)SUMMARYline is in the receipt. (No kernel changes; the wire runs fabric/tp's mailbox kernel as it is.)