November 01, 2026
nccl-tests measures collective communication between GPUs and stops there: a clean all_reduce_perf curve says nothing about how fast your data loader feeds each rank or how long the job stalls while a checkpoint is written. Validating a managed multi-node cluster therefore takes two stages. The fabric stage measures NCCL bandwidth at your planned node count. The training stage runs one frozen job at two or more node counts and measures step time, scaling efficiency, input wait, and checkpoint stall against what the same job reaches today. On GMI Cloud's Managed GPU Cluster, in early access, that job runs on a Kubernetes cluster of dedicated GPU worker nodes that GMI provisions and operates, so your team spends the test on the workload rather than on cluster setup.
For a team moving off RunPod Instant Clusters, the job's current step time, tokens per second, and checkpoint duration there become the targets. For multi-node training on H200 with the Kubernetes layer operated by the provider, GMI's Managed GPU Cluster fits that brief, and passing both stages shows it fits your job.
NVIDIA describes nccl-tests as checking both the performance and the correctness of NCCL operations. That makes it the right first gate: within the sizes and duration you test, a bad link or misconfigured transport can surface there before it is mistaken for a model problem. A training step, though, contains much more than a collective.
| Part of a training step | Exercised by nccl-tests? | How it shows up in training |
|---|---|---|
| Cross-node collectives (all-reduce, reduce-scatter, all-gather) | Yes, at the sizes you sweep | Gradient sync and sharded-parameter gathers |
| Overlap of compute and communication | No | Communication time that compute fails to hide |
| Your framework's real message sizes | Only if you sweep them | Bucket and shard sizes far from the benchmark's peak |
| Data loading from shared storage | No | GPUs idle at the start of a step |
| Host CPU and memory for loader workers | No | Uneven input wait across ranks |
| Checkpoint writes and reads | No | Periodic stalls and slow restarts |
| Hours-long stability | Collectives only, when looped for hours | Step time that drifts upward over a run |
For the fabric stage, run all_reduce_perf across the full node count, and add reduce_scatter_perf and all_gather_perf if the job uses FSDP or ZeRO-style sharding. Record bus bandwidth at the message sizes your framework actually sends, not only the peak, and log the exact command, NCCL version, and environment variables with each result.
Write a manifest and keep it identical on both platforms at each node count:
Across node counts, only the data-parallel degree and the global batch that follows from it should change. Record both at every point.
Jobs launched through Slurm on RunPod Instant Clusters move to Kubernetes on Managed GPU Cluster, so the training script and its hyperparameters stay the same while the multi-node launch is remapped: node count, processes per node, the torchrun rendezvous endpoint, GPU and network-interface assignment, and the dataset and checkpoint mounts. Use one Kubernetes launch pattern for every run, and diff the resolved environment on both sides before the first one. Run long enough to cover warmup plus two checkpoint cycles, and drop warmup steps from every average.
A single run at full size can't tell a fast cluster from a well-hidden bottleneck. Run the frozen job at two or more node counts and compare against linear scaling:
scaling efficiency at N nodes = throughput(N) / (throughput(N0) _ N / N0)
Here N0 is the smallest multi-node point you test, usually two nodes, so cross-node traffic is already present in the baseline.
A worked example with assumed inputs: assume the job reaches 100,000 tokens per second on 2 nodes and 360,000 on 8 nodes. Linear scaling would give 400,000, so efficiency at 8 nodes is 90%. Set the pass mark relative to the same job on RunPod at the same node count, GPU model, and GPUs per node, rather than as an absolute figure.
This sweep holds per-GPU work constant, so the global batch grows with the cluster. A sweep that holds global batch fixed answers a different question; keep its results in a separate column.
Two more numbers explain a low result. Exposed communication is collective time on the critical path that compute does not cover. Read it from a profiler trace, such as PyTorch Profiler or NVIDIA Nsight Systems, that shows compute and communication streams side by side, not as step time minus compute, since that remainder also holds data wait and launch gaps. A share that rises with node count puts communication, or ranks arriving late to it, on the critical path. Rank spread is the slowest rank's step time divided by the median, timed the same way over the same steps on every rank. A value well above 1.0 signals imbalance, and per-rank compute, input wait, and collective wait then show whether one node, one loader, or one link sits behind it.
On Managed GPU Cluster, the sweep can follow the cluster's own growth. Start at the smallest size, then add workers through node requests against the existing cluster to reach the next point, keeping the same worker node specification and data center throughout. When the test window is short, include the whole sweep plan, up to the largest node count, in the first cluster request.
Data input. Run the frozen job twice at full size with matched batch shapes and token counts: once with synthetic batches placed in GPU memory before timing starts, once with the real loader reading the real dataset. For dense models, the step time difference approximates what the input pipeline adds. The direct measurement is per-rank data wait: time the training loop sits blocked on the next batch, read from the profiler trace so it counts only waits the GPU actually sees. Test a cold first pass separately from a warm one, since caching can hide a slow storage path in short runs.
Checkpoints. Measure three things:
Then compute the blocking overhead of each save cycle:
checkpoint overhead = save stall / (training time between saves + save stall)
With assumed inputs of a 90-second stall after every 28.5 minutes of training, the cycle is 30 minutes and overhead is 5% of wall-clock time. For asynchronous saving, also compare throughput with checkpointing on and off to capture any slowdown from the background write.
After restoring, compare loss against an uninterrupted run at the same data position. A jump beyond the run-to-run noise you see on RunPod fails the test until it is explained: confirm that model weights, optimizer and scheduler state, the mixed-precision loss scaler, random state, and the data loader position all came back.
Put the RunPod baseline next to the candidate result in one table; the pass rules shown are examples to adjust.
| Test | Metric | RunPod baseline | Candidate cluster | Example pass rule |
|---|---|---|---|---|
| Fabric | All-reduce bus bandwidth at your bucket size, same command | Measured | Measured | Within 10% of baseline |
| Step time, full size | Median, warmup excluded | Measured | Measured | At or below baseline |
| Scaling efficiency | Throughput vs. linear from 2 nodes | Measured | Measured | No lower than baseline minus 3 points |
| Rank spread | Slowest rank / median rank | Measured | Measured | No higher than baseline plus 0.03 |
| Data wait | Share of step time | Measured | Measured | Below 5% |
| Checkpoint stall | Blocking overhead per save cycle | Measured | Measured | Below 3% |
| Restore | Seconds to first step | Measured | Measured | At or below baseline |
| Loss continuity | Loss curve vs. uninterrupted run | Measured | Measured | Within run-to-run noise |
| Soak | Step time drift over the long run | Measured | Measured | Under 2% |
Close the report with the manifest, the NCCL test command and version, node count, worker node specification, and data center, so the result can be reproduced on a later cluster.
Long enough to cover several checkpoint cycles and at least one full pass over a representative data shard, at the full node count. Read the step time trend alongside GPU clock, temperature, and throttle counters and link error counts, so slow drift from thermal behavior or a degrading link can be traced to its source.
Rule out nondeterminism before blaming the cluster. A different NCCL algorithm or a different number of ranks changes the order of floating-point reductions, and some GPU kernels are nondeterministic by default. As a first screen, compare against the spread of repeated baseline runs with the same seed over the same aligned step range, well past the first few dozen steps. A candidate inside that band clears the screen for the window you tested; outside it, diff the resolved software environment first, since an unpinned library can shift results with no change in hardware.
Request a Managed GPU Cluster with H200 worker nodes at the smallest node count of your sweep, from the console, and contact support to activate it. At activation, send the manifest, your RunPod baseline, and the job's dataset size, per-node read throughput, and checkpoint size, so storage can be planned around the job you will accept.
Colin Mo
GMI Cloud helps you architect, deploy, optimize, and scale your AI strategies
