Repository navigation
Conversation
5158fc8 to
6a13226
Compare
6a13226 to
1b9fc3c
Compare
|
Thanks @swbiggart. Plain decode on the one-host pair going from 28.9 to 73.3 tok/s with the same tokens is a big result. Keeping two hosts on main's nonblocking path, after measuring what the split cost there, was the right call. The fake-NCCL tests for split order, deadline and abort are thorough. Thanks for the rebase.
Once 1 is in, a rerun of
|
|
Thanks for the review.
|
|
Thanks @swbiggart.
Two hosts keep main's nonblocking path, with one extra byte each way at start-up. Your plan for 2 to 4 works. #2976 lands first and #2977 after it. Once #2977 is on main, please:
After that, we'll run start-up and a short serve on two GB10s, one rank a host, to check that the two-host path still matches main. #2982 carries this PR at
|
…ed and that a 4 s stall stays bounded The sum mode issues one raw all-reduce that must return ncclSuccess, as on a blocking communicator, and reports init time and device memory. The timeout mode's peer outlives rank 0's 3 s bound, so a collective that waits on the peer inside NCCL fails it. An optional MASTER address lets the two ranks run on two hosts.
…mmunicator Every collective on a nonblocking communicator ran its launch on a thread of NCCL's and returned in progress, so the engine polled it with a 50 us sleep, about 105 us a collective and 192 a decode pass. The parent stays nonblocking and bounds start-up under the control link and a deadline as before; a blocking communicator split from it, sharing its connections, carries every collective and returns once the kernel is enqueued. NCCL_RUNTIME_CONNECT=0, set when unset, makes NCCL connect inside the parent's init and the split's job, which the parent's poll bounds, so no collective waits on its peer for a connection. Abort ends the parent, which stops a split in flight, then the split.
…ECT and the measured decode The two-rank recipe says how start-up stays bounded and what NCCL_RUNTIME_CONNECT=1 would give up; the CUDA recipe's transport report names Flash Next with GLM; the changelog and the ledger's tensor-parallel row carry the measurements.
…communicator; one host keeps the blocking split
…icator across hosts, and the GB10 decode
c790783 to
b4a758b
Compare
|
Rebased onto main
|
|
Thanks @swbiggart. Both ranks now agree on This now waits on one startup and short-serve run that we'll schedule on two GB10s, one rank per host, checking the nonblocking path and token equality against main. No action needed from you.
|
What this changes
At
--tp 2, Flash Next on CUDA runs every collective on a nonblocking NCCL communicator: each returns in progress, runs on NCCL's thread, and is polled with 50 µs sleeps, four a layer. On one host (two RTX PRO 6000) that wait is the bottleneck.Each rank now learns from the control link's two addresses whether both share a host (
Link.oneHost). On one host, every collective runs on a blocking communicator split from the nonblocking one (ncclCommSplit,split_share) and returns once enqueued. The split has its own deadline, so start-up is bounded by twice the rank timeout.NCCL_RUNTIME_CONNECT=0, set there only when unset, makes NCCL connect inside the bounded init and split, not in a collective. On two hosts, where the split cost two GB10s 0.8-1.3% of plain decode, the collectives stay on main's nonblocking communicator, with no split or setenv. A stalled or departed peer still ends a collective within the bound. Same collectives, order and sums: replies are token-identical.Two RTX PRO 6000 Blackwell Max-Q go from 28.9 to 73.3 tok/s plain and 112.6 to 210.8 drafted (code); two GB10s are level with main.
fabric/tp.Collectiveremains.4cfd95573).de20debb5: rank-test checks (enqueued return, bounded 4 s stall) on flashnext-rank: the failure modes start once both communicators are up, and timeout bounds only the collective #2976's start-up fix.24a913da0: the split, host tests, pins;e5cafb123: docs.b40053cde:Link.oneHost, nonblocking across hosts, host tests, the enqueued check on one host only, pins;a82050e8b: its docs.b4a758b2f: both ranks exchange their one-host answer before NCCL starts, host test, pins.atc overlap --json(ATC 0.9.6, on5da839cce, base93a2e93ec, before the rebase):complete: false, 12 changed files, 250 overlaps: 5conflicts, 5 clean, 240unknown.conflicts(GLM on CUDA: the latent split's per-rank page book and owner (TF_GLM_KV_SPLIT, #1431 part 1; stacked on #1411) #2415, qwen27: slot 0 keeps its rewind point through the warm pass #2723, llama on Metal: Qwen2 checkpoints, Bimo-7B-CyberAgent's bf16 release with its q, k and v biases and YaRN at HF's rounding points #2890, xpu: a sampled Qwen3.8-27B row draws from a top-k kept on the card #2895, core: Admit and Ledger move to engine_admission.zig, unchanged #2896) areCHANGELOG.md's re-opened## Unreleasedheading.git merge-treetrial merges of open pull requests touching a changed file: only the EXL3 Flash Next stack (EXL3 Flash Next on CUDA: one rank, offloaded experts, sm_120 gate (1/8) #1441-EXL3 Flash Next: draft lane guard for offloaded experts (8/8) #1446, EXL3 Flash Next: host-resident K/V on the tier (6/8) #1522) adds conflicts (tests/cuda/main.zig;cuda_collective.zigfor EXL3 Flash Next: CUDA graphs and the device planner (7/8) #1445, EXL3 Flash Next: draft lane guard for offloaded experts (8/8) #1446).cuda_rank_wire.zig, its pin and docs lines, so the heads conflict there.atc overlap --jsonat 2026-11-12 14:48 UTC on headc7907837d; everyconflictsentry is named in this section, or adds no conflict beyond those it already has with main (5), or conflicts only inCHANGELOG.md's## Unreleasedlines, as this section describes (1); each checked withgit merge-tree.Checks on this head
b4a758b2f9ad2be537f4ed507f0c6708260d48a7. Receipts ran on the named builds; the rebases since, ontof0d8c8d50(beforef4a2caadc) and4f8317dac,000b534a2and3885dceb2, moved onlyCHANGELOG.mdlines, and ontoeab305db0only thesameHosttest's addresses (and their pin row) and the changelog's drafted figure (flashnext: refuse a malformed TF_FLASHNEXT_* setting by name before device work #2585 there changes only howTF_FLASHNEXT_*settings are read), so they stand. Ontoab78aef90and4cfd95573(flashnext-rank: the failure modes start once both communicators are up, and timeout bounds only the collective #2976 and flashnext cuda: at --tp 2 a prompt pass reduce-scatters its routed experts' slots by rows and gathers their sums #2977 landed): the first commit keeps flashnext-rank: the failure modes start once both communicators are up, and timeout bounds only the collective #2976's rank-test fix, the source scan allowsncclReduceScatter(11 calls), and the pins come from the merged files; theflashnext-rankmodes ran again onea13840eb, this code onab78aef90.zig fmt --check build.zig zig,git diff --checkpass;pr_check --base upstream/main0 errors, 3 advisory warnings (no--pr; thesameHosttest's RFC 5737 192.0.2.x addresses).python3 tools/zig/lean_check.py: base4cfd95573341, this head 341, the same findings, 0 new.zig build test-cuda-host: this head exits 0 on Linux x86_64 (CUDA 13.3.1 image), as0fc22f003, this code before flashnext-rank: the failure modes start once both communicators are up, and timeout bounds only the collective #2976 and flashnext cuda: at --tp 2 a prompt pass reduce-scatters its routed experts' slots by rows and gathers their sums #2977, did on macOS (run without--summary); uncached on7e20ed85c: "379/379 steps succeeded; 6139/6178 tests passed (39 skipped)". Fake-NCCL tests: split and abort order, split failures and deadline, a scan allowing only the needed NCCL calls, no split across hosts,sameHostcases. Seven of eight planted mutations fail a test; the eighth (error path aborting the child first) is unobservable.zig build test test-golden -Dcpu=apple_m1(Apple M5 Max) on20387dd00, this code oneab305db0before the changelog-only rebases: "Build Summary: 837/839 steps succeeded (1 failed); 7595/7669 tests passed (73 skipped, 1 failed)". The failure,dashboard_test, is inherited: maineab305db0fails it on the same Mac ("Build Summary: 837/839 steps succeeded (1 failed); 13041/13152 tests passed (110 skipped, 1 failed)").zig build test-gradient-serving: pass on this head.Fail before, pass after,
tf-cuda-test flashnext-rank, one rank per GPU: main + the test isd41b7001e; this change isda506edfc,2567f4211(RTX) andf4a2caadc(both pairs):sum(a collective returnsncclSuccess; fromf4a2caadcon one host only)ncclInProgress), bothf4a2caadcGB10: "two hosts: collectives on the nonblocking communicator"timeout(4 s stall, 3 s bound),eofmissing(one rank alone)da506edfc; RTX also2567f4211)timeout, negative controlNCCL_COMM_BLOCKING=1: hangs (killed at 120 s), bothNCCL_RUNTIME_CONNECT=1: hangs (killed at 120 s), bothOn
ea13840eb, this code onab78aef90, RTX pair:sum,timeout,eof,eof-scatter,timeout-scatterandexchangePASS on both ranks;sumreports "one host: a collective returns enqueued".timeoutandeofmodes.--tp 1: no prompt state touched; one rank opens no NCCL.Receipt
Main against branch: cells and numbers, or why they can't be run:
2 x RTX PRO 6000 Blackwell Max-Q (PCIe Gen4, one host), main
93a2e93ecthen2567f4211, one start each. Decode tok/s at 256 tokens, greedy, median of 3; streams:tools/bench_concurrent.py --alone --serialaggregate tok/s; cold prompt tok/s, two requests:2 x GB10 (200G RoCE, one rank a host), starts alternated main, forced nonblocking, blocking, blocking, forced nonblocking, main, then
f4a2caadconce. "Blocking" is7e20ed85c(a split on every host), "forced nonblocking" the same withNCCL_COMM_BLOCKING=0. Decode tok/s, mean of each mode's starts:f4a2caadcThe head is within 0.4% of main; main's two starts differ by up to 0.6%.
Start-up: RTX init 0.50 → 0.61 s, 48.1 GiB left for streams (main 48.2), 8 streams each; GB10 rank-test init 0.30-0.34 s, main 0.30-0.31 s.
Boxes it reaches: Flash Next on CUDA at
--tp 2, both ranks on one host.New switch? Default, accepted values, profile row: none. On one host it sets
NCCL_RUNTIME_CONNECT=0when unset, as GLM's CUDA path does.Environment, with the TensorFold commit, Zig and backend toolchain, machine and GPU, checkpoint and revision: commits per table; Zig 0.17.0, CUDA 13.3.1,
-Doptimize=fast, NCCL 2.32.3. RTX: sm_120, driver 595.99.02, P2P. GB10: sm_121, 121.7 GiB, Ubuntu 24.04.4, drivers 580.178.04 / 580.159.03, NET/IB.nvidia/Qwen3.8-Flash-Next-NVFP4revisionfc694b54fb0174e0913e6adf86691ef85a4ead47;--tp 2 --context 65536 --prompt-cache-gib 0, default MTP drafts.Paths and platforms the change reaches, which I ran and which I could not: sm_120 on one host and sm_121 on two hosts, Linux; not other SMs, NVLink or HIP.
Exactness, from
tools/bench_concurrent.py --alone --serialortoken_shaagainst"draft": false: every decode request in every start, drafted and plain, has the reference hashes (chat764da77fbd3f, code4e759dbd859c). RTX cold prompts match across builds (cached_tokens0); every RTXbench_concurrentrow, greedy and sampled:aloneandserialequal, 0 failed.Main against this branch, same machine and checkpoint, posted before asking for a run on our machines (or why you can't): above. Raw results: https://gist.github.com/swbiggart/12edb97d751417e5093bcaacbf32bc22
Decode speed before and after, from
tools/bench_openai.py:tp2_bench.py, above.Prompt speed before and after:
tp2_bench.py's cold prefill, above; 64k exceeds the context.Licence of any new weights or drafter, or "n/a": n/a.
Checklist
The title and description match the head commit, including the receipt and the stack.
New tests fail before the change, pass after it, and skip cleanly without their dependency.
Comments and docstrings are one line. No measurements or history in the source.
No personal data, machine names, internal hosts or local paths in the diff or its commits. No AI attribution lines.
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. Resumed prompts not checked; drafted equals plain on both pairs, concurrent equals solo on RTX.No precision traded for speed. If the bits change, the description says which sums change and why. (Same collectives, same order.)
Decode and prompt processing are no slower than main at one stream and the widest admitted; with
--drafter, no slower than without it, greedy and sampled. RTX: faster at every level; cold prompts no slower. GB10: one stream level; more streams and prompts run main's path.