Repository navigation
fabric: tensor parallelism across CUDA processes (bootstrap, NCCL, mailbox and RoCE collectives, plan link, fail-fast) (DeepSeek-V4.1 stack 3/14) - #636
Conversation
|
Two-host receipt for this layer, which hadn't had a GPU run: two DGX Sparks (GB10, sm_121, CUDA 13.3, driver 580.173.02, NCCL 2.30.7 from the NGC PyTorch 26.07 container), joined by direct ConnectX-7 links,
A few latencies from the suite (rank 0, µs, eager / graph): RoCE all-reduce 4 KiB 8.7 / 8.1, 64 KiB 14.5 / 14.0, 4 MiB 304 / 360; all-gather 4 KiB 8.6 / 8.1, 4 MiB 256 / 290. NCCL on the same link: all-reduce 4 KiB 18.3 / 20.7, 64 KiB 58.4 / 45.6. Three things I hit getting there, all setup rather than the collectives:
|
|
@jayleaton, a follow-up, since GitHub now shows this as conflicting with On the three things my receipt ran into, all of them setup rather than the layer:
Exported variables already reach |
db3bf32 to
151972b
Compare
|
Thank you for the two-host receipt; it is the first GPU run this layer has had. Thanks too for pinning down the setup problems. Rebased onto current main (e7c523e). The HCA auto-pick: a real bug, fixed in a763a41. The pick took the first N active RoCE ports in device-list order, and nothing checked that the peer could reach them.
Your other two points, taken as you suggested in 151972b:
I left Docs: there is a new Gates on the new head, host only (Linux x86_64, Zig 0.17.0): A re-run of your two-host receipt on this head, with |
151972b to
06af238
Compare
|
Added the production RoCE hardening as five commits ( |
|
Fixed the flaky smaps test lookup in 41be68a: it now finds the mapping containing the address; the old test fails and the fixed TP suite passes 20 consecutive runs. |
|
Independent receipt for 41be68a on two ThinkStation PGX (GB10), one GPU per machine, aarch64 Linux, sm_121, over a direct ConnectX-7 link. Zig 0.17.0, driver 580.178.04, CUDA 13.0.2 (nvcc 13.0.88), NCCL 2.28.9+cuda13.0; built with
TensorFold auto-pick selects All 16 fatal/kill cases across the two configurations pass. Fatal victims exit 70, killed victims 137, and every survivor exits 70 after 1.158 to 1.191 s from injection, rounded to milliseconds. Rank-0 microbenchmark latency in microseconds per call, explicit single HCA, eager / graph, measured with CUDA events:
The RoCE route uses NCCL for fixed-length collectives above its slot size. These microbenchmarks do not establish model throughput or GPUDirect RDMA performance. The variable-length chunked-gather GPU path remains untested; the same-host/two-GPU mailbox is not applicable to this topology. No PR code changes. |
|
Re-ran the two-host receipt on
The auto-pick fix works here. With What is left in the both-unset case is NCCL's own pick. |
… TF_TP_ROCE_HCA nor NCCL_IB_HCA is set - `tf-tp-test hca` (host only, no GPU): both ranks run the RoCE pick over the bootstrap, the same rule the wire uses, and each prints its own devices as `NCCL_IB_HCA=name,...` (no GIDs). It exits 1 with the reason when the host has no usable port or no port on a /24 the peer shares, so no rank falls back to its first ports for NCCL. - run_hosts.sh runs it when neither variable is set, exports the names for every step and logs each rank's pick; a failed or malformed `hca` stops the runner (exit 3) with the reason; `hca` is capped at TF_TP_PLAN_TIMEOUT + 31 s, so its own reason (no peer joined in time, port in use, the peer closed, a bootstrap error) comes first. An explicit NCCL_IB_HCA, an explicit TF_TP_ROCE_HCA (NCCL_IB_HCA derived from it) or both keep their behaviour; the many-devices warning is gone. Both hosts use the same mode: with one naming its ports and the other not, the latter's `hca` finds no peer and stops after TF_TP_PLAN_TIMEOUT with that reason (README). - roce_pick: `select` / `selectFrom` return how the devices were chosen (`resolve` is unchanged for the wire), `joinNames` and `hcaLine` give the NCCL list. Host tests: the port scan over a stub verbs table (state, Ethernet, the IPv4 RoCE v2 GID index), then both ranks through a real bootstrap on the two hosts ashhart#636's receipt reports (modelled, documentation addresses), an explicit rank, and a host without ports.
41be68a to
989ca7e
Compare
|
Thanks, @jayleaton. I can contribute another two-host PGX/GB10 receipt against My earlier The new head's hardware transport is still unqualified on this pair. Failure-injection checks need a separate idle-link window. |
|
@jayleaton: the direct This was an integration build of your Linux aarch64, Zig 0.17.0, CUDA compiler 13.0.88, NVIDIA driver 595.91.07. The direct check used a separate free bootstrap port, a 10-second plan timeout and a 25-second outer limit. Both test processes exited and released the listener. This receipt covers hardware port enumeration and peer selection only. The GPU/RDMA transport suites, runner handoff to NCCL and failure injection still need an idle window on the connected pair. |
|
Thanks for the precise receipt. This adds hardware evidence for the both-unset port enumeration and peer-selection path on the integration build, including matching binaries and clean process exits. I will keep its scope separate from GPU/RDMA transport, the runner handoff to NCCL, and failure injection. The stack is being reconciled onto |
|
@jayleaton, two overlaps with #642 (NCCL send/recv and an exact reduce-scatter in rank order), so neither PR surprises the other.
For the GLM-5.3-Flash family (#548), we plan to move onto #635's graph cache and this PR's bootstrap and fail-fast once they land, rather than keep our own. |
|
Hello @jayleaton, Thank you for the CUDA tensor-parallelism host, the RoCE bounds checks, and the two-host receipts. zig build test on a Mac stops in this module. zig/build/tp.zig:46 attaches the host tests to the default test step on every host. On macOS, std.c.pipe2 is an empty value, so fate.zig:56 does not compile. sock.zig:29 names posix.TCP.KEEPIDLE, and Zig 0.17's Darwin TCP struct has KEEPALIVE and no KEEPIDLE. Please keep those tests off the Mac build, or give them a path that compiles there. Nemotron Metal, GLM, Flash Next, and the 27B all run zig build test, and they stop in fate.zig before their own tests. A push that compiles on macOS is the update we need. Then it goes through the gate described on #635. Thank you again. |
…alls them. graph_cache.Cache captures one executable graph per caller-named key and replays it when the key comes back; ranks agree on every capture, eviction and fallback through an Agree hook, and a memory floor turns misses eager. arena.Arena sub-allocates DeviceBuffer.alloc from a few large chunks (VMM or plain cuMemAlloc) through memory.setHook; borrowed spans and pointers it does not own are freed as before.
…; CUresult names fall back to a table when the driver cannot answer. cuFuncGetName is optional (CUDA 12.3+), so an older driver still opens. During the exit's teardown cuGetErrorName fails too, which printed CUDA_ERROR_DEINITIALIZED as "CUDA_ERROR_UNKNOWN_CODE (4)"; code 4 now says the process is exiting.
Use CUDA device attribute 110 for GPUDirect RDMA with VMM support. Attribute 126 reports the memory sync domain count and can incorrectly enable the RDMA allocation flag. Add a regression for the external ABI contract.
…r stays under the max, a max of 0 is eager - A capture whose agreement throws, or whose insert runs out of memory, no longer leaks its executable: the map's slot is reserved before the capture, and the executable is owned here until the insert, which cannot fail. - A host map that cannot grow counts as no room in the existing room vote, so every rank declines that miss together. - `min_keep` is clamped to `max`, so under the memory floor the cap never rises above the configured max. A max of 0 runs every step eagerly, with no collective. - A cap lowered to 0 under the floor goes back to max once every rank has room, instead of staying eager for good. - Hits make no collective: with `Agree` set, keys, settings and fingerprint changes must already match on every rank. The docs now say so: the fingerprint changes at the same step on every rank, and `fingerprintOf` is for one rank. Co-authored-by: Jonathan Regan-Beasley <197533392+jregan-beasley-smc@users.noreply.github.com>
The arena's `live` (small ranges) and `large` (own chunks) maps drop an entry on every free and gain one on every allocation for the life of the process. std's tombstones never clear, so in time they take every free slot, and each allocation, free and miss then probes the whole map. Each map now counts its removals and is rehashed in place once they reach a tenth of its capacity. Test: 20,000 random allocations and frees (small ranges and large chunks) keep a tenth of each map free at every step; without the rehash it fails.
…amily that installs it, without a rebuild Cache.init applies the operator's value through Settings.operator, so a deployment that measures graphs slower can turn them off at serve time. The value is read once a cache, and is set alike on every rank like the settings themselves: no agreement is added, and hits still make no collective.
…HCA verbs-error naming) into stack/08c-kernels Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…HCA verbs-error naming) into stack/08e-block Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…HCA verbs-error naming) into stack/08r-runtime Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…HCA verbs-error naming) into stack/09a-kv Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…HCA verbs-error naming) into stack/09b-forward Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…HCA verbs-error naming) into stack/10-prefill Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…HCA verbs-error naming) into stack/11a-sampling Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…HCA verbs-error naming) into stack/11b-draft Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…HCA verbs-error naming) into stack/11c-device-drafts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…HCA verbs-error naming) into stack/11d-batched-rows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…HCA verbs-error naming) into stack/12a-serving-host Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…HCA verbs-error naming) into stack/12b-serving-engine Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…HCA verbs-error naming) into stack/13a-performance-host Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…HCA verbs-error naming) into stack/13b-performance-device Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts, both sides kept: - zig/src/cuda/memory.zig: main's allocation limit (reserveAllocation/finishAllocation) now wraps the arena hook path. A hooked allocation reserves, tries the hook, falls back to cuMemAlloc and is counted by finishAllocation like any other. - zig/src/cuda/root.zig: main's memory_receipt, setAllocationLimit and new kernel modules beside graph_cache and arena. - RUNBOOK.md: main's GLM heat-band paragraph, then the TF_GRAPH_CACHE paragraph. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r.check does launched() named the failing kernel for every non-success code, so a launch that main's tests answer with CUDA_ERROR_NOT_READY (qwen_cuda_test, experts3_host_test) reached errorName on a fake driver and crashed. It now returns error.NotReady first, like Driver.check. The ranks test reads the monotonic clock through std.Io, so it also compiles on Windows hosts (zig-windows). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…3-tp-fabric Conflicts, both sides kept: - THIRD_PARTY_NOTICES.md: two b12x RoCEnante entries, each naming its own files. Main's two-rank CUDA RoCE gather (zig/src/cuda/roce.zig, zig/kernels/cuda/roce_gather.cu), then the TP fabric's RoCE path (zig/src/fabric/tp/mailbox.cu, roce.zig, roce_verbs.zig). - build.zig: main's sys and checkpoint_config wiring beside tp_build; tp_build.add runs before both walks, so the TP modules (which compile zig/src/cuda/root.zig, now importing sys) get the sys import. Auto-merge fix: - zig/src/cuda/nccl.zig: main added ncclCommAbort and ncclCommGetAsyncError to Api too; one copy kept (main's). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the merged head you integrate is up, we can run on three GB10 hosts in a switchless ring, where each rank's address is a /32 routed over the links:
Tell us the head and the gate commands, and we will post the receipt here. |
|
Thanks @jayleaton. 1.1.1 ships the shared CUDA fabric, bootstrap, NCCL/mailbox/RoCE collectives, settings agreement and peer-failure handling. The later 88418d8/4c69b0d4b command-preflight work and 4df28ce/0a80da001 verbs/HCA diagnostics remain to carry into 1.1.2 with your credit, followed by the integrated failure controls. Keeping this PR open for those follow-ups; no TP3/4 model-support claim follows from the infrastructure landing. Posted by Ash's AI Assistant Synthena |
…le, and NCCL_COMM_ID then names only the ranks' lines (local, not for upstream: TP=3 on the ring's /32 until ashhart#636) 1.1.0's tp_watch reads NCCL_COMM_ID for the ranks' lines, and NCCL's bootstrap needs an interface in that address's subnet, which a /32 ring has only on rank 0. With TF_GLM_NCCL_ID_FILE set, each rank logs the NCCL_COMM_ID it takes out of its environment after the lines are up and before NCCL's first call; rank 0 writes its unique id to the file as hex and the others read it there (5b6aa23's form), waiting up to 10 minutes.
|
Thank you for this. We compared #636, and #637 on top of it, against the 1.1.2 line by their net change against 1.1.0. Most of the transport, cache and arena code and the rank paths are already there, often byte for byte, and carrying the whole PR would duplicate build wiring and CUDA declarations the line already has. So we're closing this one as superseded. Three pieces are worth having on their own, and we'd take them as small PRs on top of 1.1.2, credited to you:
- Synthema, Ash's AI Assistant. |
…ts with the CUDA RoCE gather's The fabric under zig/src/fabric/tp serves every tensor-parallel family, so its b12x RoCEnante credit moves out of the DeepSeek-V4.1 CUDA sources section, naming its own files and terms as #636 carried them.
…le, and NCCL_COMM_ID then names only the ranks' lines (local, not for upstream: TP=3 on the ring's /32 until ashhart#636) 1.1.0's tp_watch reads NCCL_COMM_ID for the ranks' lines, and NCCL's bootstrap needs an interface in that address's subnet, which a /32 ring has only on rank 0. With TF_GLM_NCCL_ID_FILE set, each rank logs the NCCL_COMM_ID it takes out of its environment after the lines are up and before NCCL's first call; rank 0 writes its unique id to the file as hex and the others read it there (5b6aa23's form), waiting up to 10 minutes.
What this changes
fabric: tensor parallelism across CUDA processes (bootstrap, NCCL, mailbox and RoCE collectives, plan link, fail-fast). Depends on #635. This cumulative PR targets
main; the incremental range isc58d83ca..989ca7e6.HCA selection accepts the documented device forms and rejects mixed modes. The host runner is executable. The final 48-case receipt passed against a fresh binary; this is modeled host coverage, with no hardware transport run.
Commits in this layer:
25c02d38Tensor parallelism across CUDA processes: bootstrap, NCCL, mailbox and RoCE collectives, plan link, fail-fast.6b5404dcTP tests: a drain on rank 0 then its clean stop ends rank 1's loop at the stop word with no fail-fast exit; a rank killed mid-drain still fails the other fast.06c4e75fTP RoCE: with no TF_TP_ROCE_HCA, each rank picks the ports on an IPv4 /24 the peer also has062af8b9TP README: when -Dtp-nvcc, TF_TP_ROCE_HCA, NCCL_IB_HCA, NCCL_SOCKET_IFNAME and TF_TP_MASTER are needed, with a two-host exampled7510ef8TP test runner: a binary built without the mailbox kernel refuses the kernel backends up front; run_hosts.sh passes HCA pickse16e331aTP RoCE: every RDMA post is bounds-checked before the verb, and peers with another region layout are refused649e7880TP RoCE: no RDMA write past 1 MiB, and boot refuses any setting that would ask for one664a8e2dTP RoCE: TF_TP_ROCE_CHUNKED_KB gathers strides past the slot in slot-sized ops, at most 2 MiB in flight4ad50299TP RoCE: a failed transport halts its QPs, drains them and keeps its region; the region is DONTFORK and NOHUGEPAGE9bbc21acTP RoCE: tf-tp-test failfast V transport checks the failed-transport path on real HCAs, only on an idle linkcf19042fFix RoCE test smaps lookup for merged mappings989ca7e6TP RoCE: run_hosts.sh gives NCCL the RoCE picker's ports when neither TF_TP_ROCE_HCA nor NCCL_IB_HCA is setThe combined correction removes personal MIT headers while retaining third-party notices, relocates development Python tools, and preserves stored fixtures with native consumers. Expanded provenance covers the EXL3 kernel stack; no copied text was found by the performed scan, and maintainer copy scanning remains authoritative.
Overlap with main
Main has a two-rank CUDA RoCE all-gather in
zig/src/cuda/roce.zig(from6636e696, after b12x's RoCEnante): its ownibv_*loader at lines 90–125,Errorat :147 andGatherat :212. This PR's equivalents arezig/src/fabric/tp/roce.zig,roce_verbs.zig,mailbox.cuandroce_pick.zig. Its bootstrap, fate channel, planlink,NCCL and hybrid transport have no counterpart in main. The two are kept side by side, and
THIRD_PARTY_NOTICES.mdcarries one b12x credit for each, naming its own files. Consolidation onto one loader is left to the maintainers'
common transport integration.
Receipt
Before: public head
41be68af. After: exact corrected head989ca7e6578309da169d94f55b9ffd17541b1bf1, gated on 2026-10-12, Linux x86_64 and Zig 0.17.0.Executed gate output:
Lean uses pinned main
e7c523eb: 534 inherited findings, zero new or removed identities. The mode gate covers all 57 introducing pins with zero executable-mode losses. Family suites and CLI receipts run where introduced. Host checks do not qualify checkpoint inference, GPU execution, concurrent/resumed serving parity, prompt/decode speed, or macOS/Metal behavior on this rewritten tree.The family documentation pins development tools at recipe commit c1cb9184.
Exactness and prompt/decode speed: not run on this host, which has no GPU or checkpoint weights. No new performance claim. MLX/PyTorch/checkpoint revision: not applicable to the host gate.
Checklist
"draft": false, a resumed prompt equals a fresh one, and concurrent equals solo.Merge Danger
Door: two-way. Reverting restores the parent without a data migration.
Blast Radius: engine. The incremental commits define the affected runtime, build and test surfaces. The production branch is unchanged.