algbw, busbw, and reading a nccl-tests result
S3·E5The 1.75x he called marketing · Customer HQ conference room, quote review, Thursday
Builds on: NCCL environment variables on a RoCE fabric, GPUDirect RDMA: proving it is actually on
Before you read: what do you already know?
3 quick questions. Wrong answers are fine and expected; trying first makes the lesson stick.
After this lesson you can
- Compute algbw and busbw from size time and rank count for AllReduce AllGather and Broadcast.
- Explain why the correction factor differs per collective and why Broadcast and Reduce have none.
- Analyse a size sweep to locate the move from latency-bound to bandwidth-bound behaviour.
- Translate a busbw figure into a claim about which piece of hardware is the bottleneck and state what would falsify it.
Episode 5 — The 1.75x he called marketing
The last meeting before sixty-four nodes are signed or slide a quarter. Their lead engineer taps the circled row on your printed nccl-tests output: algbw in one column, busbw in the next, the second 1.75 times the first. Politely, he says he does not buy numbers a tool invents: when a vendor’s benchmark reports a bandwidth larger than the one it measured, somebody is selling. The SE studies his cold coffee; the network lead says nothing, which is worse.
He is right to be suspicious and wrong about the cause; the meeting turns on saying why, fast. Algorithm bandwidth is the plain one — size divided by time — and it is useful to compute how much time any large operation would take by dividing the size by it.[1] Bus bandwidth exists because the theoretical peak algorithm bandwidth is not equal to the hardware peak bandwidth, usually depending on the number of ranks, so nccl-tests reports a second number that reflects how optimally the hardware is used.[1] For AllReduce the correction is 2*(n-1)/n[1] — at eight ranks, 1.75. Not invented; derived, and independent of whether NCCL chose a ring or a tree that day.[1]
You look at the printout again and find what you missed before sending it. It is a single row. Both -b and -e default to 32M, so a bare run is one point, not a sweep.[2]
A single-size result is a claim; a sweep is a measurement.
Segment 1 is the arguments that decide what you measured — the half you cannot defend.
1The arguments that decide what you measured
Before the arithmetic, the run. nccl-tests builds with plain make, or with paths as make CUDA_HOME=/path/to/cuda NCCL_HOME=/path/to/nccl, and with MPI as make MPI=1 MPI_HOME=/path/to/mpi, where NAME_SUFFIX= adds a suffix to the binaries.[2] The README excerpt confirms all_reduce_perf, comm_ops_perf (requires MPI) and device_api/gin (requires MPI, NCCL 2.30.7 or later, exactly two ranks); the other binaries built from src/ are the conventional ones but were not enumerated on the page fetched, so confirm the list with ls build/ after a build rather than quoting it.[2]
The defaults are where results go wrong quietly. -b, --minbytes and -e, --maxbytes both default to 32M, -i, --stepbytes to 1M, and -f, --stepfactor is disabled by default — so a bare all_reduce_perf is a single point at 32 MiB, not a sweep.[2] -n, --iters defaults to 20 timed iterations, -w, --warmup_iters to 1, -m, --agg_iters to 1.[2] -c, --check defaults to 1 correctness-check iteration; set -c 0 for clean timing runs.[2] -a, --average defaults to 1, where 0=Rank0, 1=Avg, 2=Min, 3=Max.[2] -g, --ngpus is GPUs per thread and defaults to 1, -t, --nthreads to 1, -o, --op to Sum, -d, --datatype to Float, -r, --root to 0.[2]
Reporting and control flags worth knowing: -J, --output_file writes a JSON report, -M, --memory reports memory usage, -U, --tuning reports NCCL tuning information, -I, --per_iter_timing gives CUDA-event per-iteration timings with -K, --per_iter_skip to drop leading samples, -T, --timeout in seconds is disabled by default, and -N, --run_cycles defaults to 1 with 0 meaning infinite.[2] NCCL_TESTS_SPLIT controls GPU partitioning for parallel operations, and when several operations run in parallel the reported bandwidth is per group.[2]
The canonical sweep for a fabric question is therefore explicit about all of it: all_reduce_perf -b 8 -e 8G -f 2 -g 8 -c 0.[2]
mpirun -np 2 --map-by ppr:1:node ./build/all_reduce_perf_mpi -g 8 -b 8 -e 8G -f 2 -c 0
-g follows the rank scrubber (8 GPUs per node assumed, XE9680-class). n in the busbw formula is the total rank count, not the GPUs on one node.
perftest "is a collection of tests written over uverbs intended for use as a performance micro-benchmark". Quote this when a customer treats a result as an application SLA: "The benchmarks generate a synthetic stream of operations… The benchmarks are not designed to emulate any real application traffic."
They are "usually installed to the /usr/bin/" and "may need to be ran with sudo when running from non root". A cryptic syndrome string on stderr comes from rdma-core, not from perftest.
perftest README (master)- warnNCCL_IB_TC is unset and defaults to 0, so training traffic carries ToS 0 into the default, usually lossy, queue — whatever you configured for priority 3. NCCL 2.31.2 environment variables
- warnNo NCCL_IB_HCA on a multi-node run: NCCL uses all available HCA ports, a storage NIC included. Capped at 32 HCAs. NCCL 2.31.2 environment variables
- noteNCCL_IB_ADAPTIVE_ROUTING defaults to 1 on InfiniBand but 0 on RoCE, and needs a system-defined AR-enabled SL. NCCL 2.31.2 environment variables
t = (S/B) * (2*(n-1)/n), so B = algbw * 2*(n-1)/n — "independent of the algorithm used (ring, tree, or other) as long as they use point-to-point operations".
"The bus bandwidth should reflect the speed of the hardware bottleneck: NVLink, PCI, QPI, or network."
nccl-tests doc/PERFORMANCE.md2algbw is arithmetic; busbw is a model
“NCCL tests report the average operation time in ms, and two bandwidths in GB/s: algorithm bandwidth and bus bandwidth.”[1] Algorithm bandwidth is the simple one: “Algorithm bandwidth is using the most commonly used formula for bandwidth : size (S) / time (t).”[1] Its use is equally simple — “It is useful to compute how much time any large operation would take by simply dividing the size of the operation by the algorithm bandwidth.”[1] If a customer asks how long their all-reduce will take, algbw answers it.
Bus bandwidth answers a different question: how well is the hardware being used. It exists because “the theoretical peak algorithm bandwidth is not equal to the hardware peak bandwidth, usually depending on the number of ranks”, and NCCL tests introduce it “to provide a number which reflects how optimally the hardware is used”.[1]
For AllReduce the derivation gives t = (S/B) * (2*(n-1)/n) and therefore B = algbw * (2*(n-1)/n).[1] Crucially, that derivation is “independent of the algorithm used (ring, tree, or other) as long as they use point-to-point operations (send/receive)” — so a busbw number does not depend on which algorithm NCCL chose that day.[1]
The full set of correction factors: AllReduce 2*(n-1)/n; ReduceScatter (n-1)/n where “S is the size in bytes of the total array, which for NCCL is equal to recvcount*sizeof(datatype)*n”; AllGather (n-1)/n with S = sendcount*sizeof(datatype)*n; AllToAll (n-1)/n; Broadcast and Reduce 1, because “all data has to get out of the root rank, hence the bottleneck is on the root rank”.[1]
Two consequences to state out loud in any report. A busbw figure without n and without the collective is uninterpretable, because both are inputs to the factor. And AllReduce is the only collective whose factor exceeds 1, which is why an 8-rank AllReduce busbw is 1.75× its algbw and a naive reader thinks the tool is inflating the number.
3Turning a number into a bottleneck claim
The sentence that makes busbw diagnostic: “The bus bandwidth should reflect the speed of the hardware bottleneck: NVLink, PCI, QPI, or network.”[1] That is the whole method. Compute busbw, then say which of those four it most nearly matches, then defend the choice.
The reading changes with the shape of the run. Within a single node, an 8-GPU AllReduce should approach the NVLink bandwidth, because the collective never leaves the node. Add a second node and the bottleneck moves to the per-GPU network bandwidth — so a drop at exactly two nodes is expected, and the question is whether the size of the drop matches the per-GPU link speed you were sold. Do the arithmetic openly: a 400 Gb/s rail per GPU is 50 GB/s nominal before protocol overhead, so a per-GPU busbw in that neighbourhood at large sizes is consistent with the fabric being the bottleneck and nothing being broken. Any tighter rule of thumb you have heard for the achievable fraction is field practice, not a published NVIDIA figure; label it as such.
Where you read the number also matters. “Time matters at small sizes (‘to measure the constant overhead (or latency)’), bandwidth at large sizes” — so a busbw figure taken at 8 KiB is measuring launch overhead, not hardware.[1] This is why the sweep exists and why the knee in the sweep is more informative than any single row.
Rails are the reason a two-node number can be worse than the link speed suggests. NVIDIA’s rail definition is that “NIC-0 from each DGX system is connected to the same leaf switch (L0), NIC-1s are connected to the same leaf switch (L1), and so on”, which “helps maximize all-reduce performance while minimizing network interference between flows”.[4] PXN — “PCI × NVLink” — has GPUs reach a NIC “through NVLink and then PCI” rather than through the CPU, which “couldn’t deliver full bandwidth”, and it “leverages NVIDIA NVSwitch connectivity between GPUs within the node to first move data on a GPU on the same rail as the destination, then send it to the destination without crossing rails”.[4] That is the mechanism NCCL_CROSS_NIC=0 is protecting on a rail-optimized fabric.[3]
4Cross-checking with a tool that is not NCCL
A busbw claim gets much stronger when a second, unrelated tool measures the same path. The natural cross-check is point-to-point GPU-to-GPU RDMA between the same two GPUs: NVIDIA’s own reference run is ib_write_bw --use_cuda=0 --use_cuda_dmabuf -d mlx5_0 -a -F --report_gbits -q 1 on the server, with the client adding -n 5000 and the server address.[6] Convert once and compare: perftest reports Gb/s with --report_gbits, nccl-tests reports GB/s, so divide the perftest figure by 8 before putting the two side by side.[5][1]
The logic of the comparison is what matters. If perftest between the two GPUs is healthy and NCCL busbw is not, the fabric carried full-rate RDMA on that path and the fault is in configuration or topology — marking, GID selection, cross-NIC policy, rank placement. If both are low, the fabric is implicated and you move to counters. If perftest cannot run in CUDA mode at all, you are back in the GPUDirect gates rather than in a performance question.[6]
Keep the two tools honest about their differences. perftest is a synthetic micro-benchmark whose README says the benchmarks “are not designed to emulate any real application traffic”, and its latency figures are half a round trip.[5] NCCL busbw is a modelled figure derived from a collective. They agree about a link, not about a workload, and saying so in the report is what stops a customer treating either number as a service-level guarantee.
When the pair disagrees and you need NCCL’s own account of what it did, the debug pair from the previous lesson is the evidence: NCCL_DEBUG=INFO with NCCL_DEBUG_SUBSYS=INIT,NET,GRAPH, written per rank with NCCL_DEBUG_FILE.[3] And before either tool, rping proves basic RoCE connectivity so that a slow number is not actually a broken path.[7]
Given: all_reduce_perf -b 8 -e 8G -f 2 -c 0. One node, 8 ranks: at 1 GiB, time 60.0 ms. Two nodes, 16 ranks: at 1 GiB, time 168.0 ms.
- Single node, algbw.
S/t= 1073741824 bytes / 0.060 s ≈ 17.9 GB/s.[1] - Single node, busbw. Factor for AllReduce is
2*(n-1)/n= 2 × 7/8 = 1.75, so busbw ≈ 31.3 GB/s.[1] - Name the hardware. The collective never left the node, so the bottleneck candidate is NVLink (or PCIe if the GPUs are not NVLink-connected). Check
nvidia-smi topo -mforNV#links before asserting NVLink. - Two nodes, algbw. 1073741824 / 0.168 ≈ 6.4 GB/s.[1]
- Two nodes, busbw. Factor is 2 × 15/16 = 1.875, so busbw ≈ 12.0 GB/s.[1]
- Name the hardware for the second case. The bottleneck moved off the node, so busbw is now a network statement. busbw is a per-rank figure — the derivation assumes each rank has a bandwidth
Bto the outside world andnlinks of that bandwidth — so compare it against one rail, not against the node total. 12.0 GB/s per GPU is well under the 50 GB/s nominal of a 400 Gb/s rail, so the drop is larger than the topology alone explains.[1] - Falsify or confirm before reporting. Run
ib_write_bw --use_cuda=0 --use_cuda_dmabuf -d <nic> -a -F --report_gbits -q 1between one GPU on each node and divide by 8 to compare in GB/s.[6][5] If perftest is near line rate, the finding is configuration: check the traffic class, the cross-NIC policy and the GDR level withNCCL_DEBUG=INFO NCCL_DEBUG_SUBSYS=INIT,NET,GRAPH.[3] - Confirm the run was bandwidth-bound by reading the large end of the sweep, not the row you were handed.[1]
Acceptance: two algbw values, two busbw values with their factors written out, one named bottleneck per case, and one independent measurement that would falsify the claim.
- algbw = ________ / ________.
- AllReduce factor = ________; for n = 8 that is ________ and for n = 16 it is ________.
- Single-node busbw = algbw × ________; the bottleneck candidate is ________ or ________.
- Two-node busbw = algbw × ________; the bottleneck candidate is now the ________.
- A 400 Gb/s per-GPU rail is ________ GB/s nominal, so compare per-GPU rather than aggregate.
- The independent cross-check is ________, whose output must be divided by ________ to compare with GB/s.
- If that cross-check is healthy, the fault is ________ rather than the fabric.
A customer sends: “4 nodes, 32 GPUs, all_reduce busbw 41 GB/s”. Nothing else. Write the reply that turns this into a measurement. Acceptance: you name the four facts you need before the number means anything, you show the correction factor you would apply for n = 32 and what algbw that implies, you state which hardware you would expect to be the bottleneck at that scale and why, you name the one independent run that would confirm or refute it, and you make no claim about whether 41 GB/s is good until those facts arrive.
The sweep he actually wanted
You rerun it as all_reduce_perf -b 8 -e 8G -f 2 -c 0[2], and the shape settles the argument better than the algebra did: small sizes measure overhead, large sizes flatten, and bus bandwidth should reflect the speed of the hardware bottleneck — NVLink, PCI, QPI, or network.[1] What you say to the lead engineer: busbw is not a bigger number, it is a different question — algbw tells you how long your all-reduce takes, busbw tells you which piece of hardware you buy next. The order goes in that afternoon. The network lead’s notebook closes on its last page, and a week later an envelope arrives from the night shift: one strip of label tape, RUN THE SWEEP.
Lab
Dell-lab GPU host plus a second node if available. Read-only: builds and benchmark runs only.
- Pre-flight inventory.
nvidia-smi topo -m,nvidia-smi -L,ibdev2netdev,ulimit -l, and the NCCL version viaNCCL_DEBUG=VERSION. Record which GPU pairs showNV#links; that determines what a single-node busbw is allowed to be compared against. - Build nccl-tests.
make CUDA_HOME=/usr/local/cuda NCCL_HOME=/usr(adjust paths), thenls build/. Expected: at leastall_reduce_perf. Record the actual binary list — the README excerpt confirms only three, so this is the authoritative list for your environment. - Single-node sweep.
./build/all_reduce_perf -b 8 -e 8G -f 2 -g 8 -c 0. Expected: a table with size, time, algbw and busbw columns. Record busbw at the largest size and compute the factor by hand to confirm the tool’s arithmetic:2*(n-1)/nwith n = 8. - Name the single-node bottleneck from step 1’s topology and the busbw from step 3. Write it as a claim with the evidence attached.
- Two-node run, if a second node is available: build the MPI variant with
make MPI=1 MPI_HOME=..., then run the two-node all-reduce with the env block from the previous lesson (NCCL_IB_HCA,NCCL_IB_TC, debug pair). Record busbw at the largest size and compute the implied per-GPU network bandwidth by hand. - Independent cross-check. Between one GPU on each node:
ib_write_bw --use_cuda=<n> --use_cuda_dmabuf -d <nic> -a -F --report_gbits -q 1on the server and the same plus-n 5000and the server IP on the client. Divide by 8 to compare in GB/s. - Verdict. If the two disagree by more than roughly ten percent, the fault is in the fabric or its configuration rather than in NCCL; use
NCCL_DEBUG=INFO NCCL_DEBUG_SUBSYS=INIT,NET,GRAPHto see which device, GID and transport NCCL actually chose. Record both command lines and both numbers as the artifact. - Deliverable. One page: the topology matrix, the single-node busbw with its factor written out, the two-node busbw with its factor, the perftest cross-check, and one named bottleneck per case with the evidence that would falsify it.
Pure arithmetic and interpretation. No GPUs required.
- Compute algbw for six supplied runs from
Sandt. Expected: bytes divided by seconds, reported in GB/s, with the unit written out. If your figure differs from the tool’s by a factor of 1.024 or so, you have mixed GB and GiB — record which convention you used. - Compute busbw for the same six runs: two AllReduce, two AllGather, two Broadcast. Expected: factors
2(n-1)/n,(n-1)/nand1respectively, each written out before the multiplication. If a Broadcast busbw differs from its algbw, you have applied a factor that does not exist. - For each of the six, write one sentence naming which hardware the busbw is measuring and why: NVLink, PCIe, the socket interconnect or the network. Expected: the answer depends on rank placement, not on the collective.
- Take the three supplied result tables (1 node, 2 nodes, 4 nodes) and mark two points on each: the size at which the run stops being latency-bound, and the size at which busbw flattens. Expected: the flat region at the large end is the only region that supports a hardware claim.
- Locate the node count at which the bottleneck moves from NVLink to the fabric, and justify it from the busbw values rather than from the topology diagram. Expected: a drop between the 1-node and 2-node tables that persists at 4 nodes.
- Write the command line you would have asked for in the first place, with every default you are overriding stated:
-b,-e,-f,-g,-c. Expected: you can say what each default was and why it was wrong for this question. - Rank-count sensitivity. Recompute one AllReduce busbw for n = 2, 8, 16 and 64 with the same algbw and describe the shape of the factor as n grows. Expected: it approaches 2 and never reaches it; say in one sentence what that means for comparing clusters of different sizes.
Retrieval check
10 questions from memory. Answer before looking anything up; misses become flashcards.
Explain it to a Dell SE
Explain to a Dell SE, in five sentences, why nccl-tests prints two bandwidth numbers and which one you would put in a customer report.
Sources
Facts in this lesson were checked against nccl-tests master README and doc/PERFORMANCE.md (re-fetched 2026-09-07); NCCL 2.31.2 environment-variable reference; NVIDIA all2all/PXN technical blog. Dates are when each page was fetched.
- Performance reported by NCCL tests (nccl-tests doc/PERFORMANCE.md) · fetched 2026-09-07
- NVIDIA/nccl-tests README (master) · fetched 2026-09-07
- Environment Variables — NCCL 2.31.2 documentation · fetched 2026-09-07
- Doubling all2all Performance with NVIDIA Collective Communication Library 2.12 (NVIDIA Technical Blog) · fetched 2026-09-07
- OFED Performance Tests README (linux-rdma/perftest master) · fetched 2026-09-07
- GPUDirect RDMA and GPUDirect Storage — NVIDIA GPU Operator (v26.7.0) · fetched 2026-09-07
- Networking Troubleshooting — NCCL 2.31.2 documentation · fetched 2026-09-07
The same idea elsewhere
Other lessons that cover this ground, sometimes from another course's angle.
- Scenario: "the 400G NIC only does 180 Gb/s"Elsewhere in this course · Same ground: busbw, flags and NCCL_IB_TC
- Rail-optimized fabrics on Dell hardwareElsewhere in this course · Same ground: PXN, rails and NCCL_IB_TC
- Proving the fabric: perftest and nccl-testsInfiniBand course · Same ground: flags, NCCL_IB_TC and nccl-tests