Repository navigation
RoCE transport above two ranks: roce.zig:148 refuses every world but 2, so four GB10s fall back to NCCL (rebased 2..16-rank branch, gate question) #1209
Description
On main (c9fab624) the RoCE transport accepts only two ranks. At any other world, Roce.init refuses before it sets up the wire:
zig/src/fabric/tp/roce.zig:148:if (b.world != 2) return error.Unsupported;
With the default TF_TP_ROCE_FALLBACK=nccl, every rank of a three- or four-rank session drops to NCCL (rank 0 logs tp: RoCE unavailable (Unsupported); every rank uses NCCL). With TF_TP_ROCE_FALLBACK=error, the session refuses to start.
Why it matters on hardware like ours
We run four GB10s on a switched ConnectX-7 fabric. At two ranks they use RoCE for the small decode exchanges. At four ranks they can't. #1206 (DeepSeek-V4.1 at world 4) says "RoCE keeps its two-rank limit, so four ranks use NCCL", #1202 calls the limit out of scope, and #981 asks what TP=3/4 would need. This issue is about that limit only.
A rebased branch
neko-legends/TensorFold:pr/tp-roce-multi-rank (f0dc5699, three commits on c9fab624, 22 files, +1158/−450; compare). There's no PR yet. We'd like your answer to the question at the end first.
1. fabric/tp: RoCE transport for 2 to 16 ranks
roce_guard:Layout.of(world, slot)gives every rank the same region layout, with one flag and one receive slot per source rank.peerAtorders the posts starting at the next rank, so the senders don't all hit one receiver first.stripes()takes the source and destination rank.roce_verbs: one RC queue pair per HCA and peer.Recordcarriesqpn[hca][peer], andconnect()takes every rank's record and pairs HCAs by subnet at both ends. The in-flight byte cap is now checked before each peer's stripe, not once before the loop. Checked once, a world-4 post could have(world − 1) ×the payload in flight: 3 MiB against the 2 MiB cap. Checked per peer, a post adds at most one payload past the cap. The record's magic is bumped, because its layout and the registered region size both changed.roce:initaccepts2..max_world(16), every rank agrees the setup, and the host gather takes alensslice ofworldentries.mailbox.cu(tp_roce): waits for every peer's flag per HCA, copies each sender's shard to that rank's slot, and sums in rank order at any world. The device helpers move toroce_device.cuhsomailbox.custays under the file-size rule.roce_shm: one mapping for every rank's region, so the shared-memory wire runs any world on one host without RDMA. That's what the host tests use.exchangestays a two-rank route. Above two ranks it goes to NCCL (hybrid.zig).- Tests: three- and four-rank worlds over the shm wire (2,000 gathers of every size, variable lengths with empty shards, one stopped rank timing out the other three, abort), the region layout and post order, four-rank HCA pairing by subnet and the refusal of non-one-to-one pairings (
roce_verbs_test.zig), the in-process collectives at two, three and four ranks, anda world-4 post checks the in-flight cap before every peer's stripe, so 3 MiB is never posted at once(roce_post_test.zig). Without the per-peer check, that last test fails.
2. fabric/tp: the tf-tp-test harness takes any world: gpu_checks.Ctx carries the world. Above two ranks, NCCL's float sums are compared within rounding, because its ring order isn't rank order. The route's own sums stay bit-exact. gpu_bench uses nccl-tests' bus-bandwidth factors for any world (unchanged at two). roce-host and failfast transport run at the session's world.
3. One-line comments for tools/zig/lean_check.py.
Two ranks keep the same collective semantics. The exchange route and the bits of a two-rank sum don't change. The wire record and the registered region size do change, so both ranks need the same build.
The rebase, and the one conflict
The branch was first cut on d1018b31 (1.1.1). Commit 1 applies to c9fab624 without conflicts. None of the transport files changed on main in between. The only conflict was in zig/src/fabric/tp/gpu_main.zig. Since then, main has replaced the runner's args[1] string matching with gpu_cli.parse, which validates the command into a Command union before any resource opens (094a0906). It has also reworded the hca failure messages for the verbs-loader errors (9ca00ae9). Our side changed suite and failfast to pass cfg.world to checks.Ctx.init, and also added a bench subcommand. The resolution:
main'sgpu_cliparsing, usage line andswitch (command)dispatch are kept unchanged, along with itshcamessages.- The world goes into
Ctx.initinsuiteandfailfast, andsuitewarns when a requested non-NCCL backend fell back to NCCL. - Our
benchsubcommand is dropped. Adding it would have meant extendinggpu_cli'sCommand, usage and invalid-argument table, and it isn't needed for this change. The two-host shell scripts are left as they are onmaintoo.
Gates (x86_64 Linux host, Zig 0.17.0, no GPU)
| gate | clean main c9fab624 |
branch f0dc5699 |
|---|---|---|
zig fmt --check build.zig zig |
clean | clean |
zig build test-tp |
30/30 steps; 61/62 passed, 1 skipped | 30/30 steps; 72/73 passed, 1 skipped |
zig build test-dsv41-forward |
6/6 steps; 100/100 | 6/6 steps; 100/100 |
zig build test-server-cpu |
5/5 steps; 617/617 | 5/5 steps; 617/617 |
zig build test-kernel-contract |
11/11 steps; 72/72 | 11/11 steps; 72/72 |
python3 tools/zig/lean_check.py |
3,608 files, 438 problems | 3,610 files, 438 problems (none in the files this branch touches) |
The skip is the same case on both trees: the verbs test that needs TF_TP_ROCE_TEST_HCA. No gate fails on either tree. lean_check.py exits 1 on both because of the 438 problems already on main.
On four GB10s
We ran the same transport code at world 4 on four GB10s over the switched ConnectX-7 fabric. roce.zig, roce_guard.zig, roce_shm.zig, hybrid.zig, mailbox.cu and roce_device.cuh are byte-identical to this branch, and roce_verbs.zig differs only in comments. This was a DeepSeek-V4.1 world-4 boot on top of #1202's split. All four ranks came up over RoCE (tp: rank N of 4 up (roce, …) on every rank), with zero panics. A rank only logs that line after every rank passes the load-time probe in Session.openRoce. The probe checks a 4 KiB all-gather over RoCE against NCCL's, and a bf16 sum against the rank-order reference. If any rank fails it, every rank falls back to NCCL.
Not tested
- No injected failures on hardware at world 4: no
failfastkill, fatal or transport run. Only the host tests' stopped-rank and abort cases cover that. - Speed not compared against NCCL at world 4, so we make no claim that RoCE is faster than NCCL at four ranks.
- The boot didn't reach serving, for a separate reason: DeepSeek-V4.1 plans no prefill segment at world 4 (DeepSeek-V4.1 at four ranks: no prefill segment plans (BMQ 32 against 16 heads a rank; x3gm's block against the decode scratch) #1205). So no model tokens went through these collectives at world 4. The probe above and the host tests are the evidence.
- No world 3, and no world above 4, on hardware. The
tp_rocekernel isn't compiled by the gates above (this host has nonvcc). It was built and run in the hardware boot above.
The gate question
Is a transport change of this size (2 to 16 ranks, one QP per HCA and peer, a new region layout, a two-rank wire-format change) acceptable as one PR? Or would you prefer something narrower? For example, world 4 only, or the layout and guard first with the verbs and kernel changes after. If you're fine with the shape, we'll open the PR against this issue. We'll include the four-rank tf-tp-test suite and the failure runs if you want them first.
Activity
Preserve the two-rank RoCE protocol and qualify multirank gather routing
Keep multirank RoCE setup ownership when the CUDA drain fails
ashhart commentedon Oct 25, 2026
Fixed on main in 81b476d..2ff0191: @neko-legends' RoCE transport for 2 to 16 ranks, the tf-tp-test harness for any world, and two maintainer children. The children keep the two-rank wire protocol unchanged and route multi-rank gathers, and keep RoCE setup ownership when a CUDA drain fails. Thank you. At TP=2 on two GB10s, tokens are equal to main and speed is within ±0.3%. We don't have four boxes to run a world of 4 here, so if you can, please post a run on yours.
- Synthema, Ash's AI Assistant.
flashosophy commentedon Oct 25, 2026
World-4 run from the Neko Legends 4×GB10 rig, on
2ff019107.Build:
Ran
tf-tp-test suitewithTF_TP_WORLD=4, ranks 0–3 on four GB10 hosts (sm_121), once withTF_COMM_BACKEND=roceand once withnccl. The RoCE run usedTF_TP_ROCE_FALLBACK=error, so there was no silent NCCL fallback.Results (passed / failed):
rocencclRank 0 established RoCE HCA pairs to ranks 1, 2 and 3. All exact eager and graph-replay checks passed.
decode_window, 86 exchanges of 8192 B, from the same RoCE suite:RoCE per-exchange latency is 38.5% of NCCL here (2.60× faster). The separate NCCL suite measured 39.92 µs/exchange and 3432.7 µs/window.
One note for other testers: the RoCE suite still loads
libnccl.so.2for the NCCL collectives; without that runtime library it exits immediately withNcclUnavailable.