Multi-GPU training (DDP)#358
Conversation
… code for loading wfd and computing SVD.
|
Thanks. What if you replace BatchNorm with LayerNorm everywhere? No syncing. |
|
Thanks Nihar! |
|
Right, I think batch norm or layer norm could both work. When computing the syncbatchnorm you sync the batch statistics across all the GPUs. This results in two slowdowns: 1). The communication between each GPU to compute the batch statistics Of course this is no different than an all reduce. However, the all reduce latency is hidden because the backward pass is occurring asynchronously with the all reduce. However, in the case of the syncbatchnorm, you can't finish the forward pass until you have batch statistics and therefore there is no async. In our case, the batches are large enough that I think we can get away with just BatchNorm. Layernorm could also work. The only thing is we haven't tested our enets using LayerNorm. What do people think, should we use LayerNorm going forward? This seems like a slightly larger change than just switching to BatchNorm. |
|
One final point is that it would be interesting to do a study to determine what is the optimal batch size for our problem. Of course we could use as many GPUs as possible but at a certain batch size, your convergence speed starts to drop off. This was explored in https://arxiv.org/abs/1812.06162. When we start launching large training runs, this would help us determine how many GPUs we should really be using. |
|
Regarding the above three points, I am fine to either A). submit my review for the current PR and then we merge into main. Then we can consider these optimizations later. B). Take into account either the point about removing the syncbatchnorm or using torch.compile and directly merging into this PR. I think the third point about the batch sizes will require more time and is outside of the scope of this PR. Just let me know what your preference/thoughts on the above optimizations are @annalena-k @stephengreen . I think either could work, but I'm leaning more towards B to keep things organized. |





Multi-GPU training with PyTorch DDP
Summary
This PR adds data-parallel multi-GPU training to Dingo using https://pytorch.org/docs/stable/notes/ddp.html. It also adds gradient accumulation and automatic mixed precision (AMP) as complementary features for memory-efficient training. Together these allow significantly faster training on multi-GPU nodes and greater flexibility in effective batch size.
What's new
Multi-GPU training (DDP)
num_gpus > 1inlocal: spawns one process per GPU viatorch.multiprocessing. Each GPU processes a non-overlapping shard of the mini-batch; gradients are averaged across GPUs via NCCL before each parameter update.BatchNormlayers are automatically converted toSyncBatchNormso statistics are computed correctly across all GPUs.DistributedSamplerensures each GPU sees a different, non-overlapping slice of data each epoch.Gradient accumulation
gradient_updates_per_optimizer_step(default: 1). Setting this tobatch_sizewithout extra GPU memory.Automatic mixed precision (AMP)
automatic_mixed_precision(default: False). When enabled, the forward pass runs in FP16 while optimizer state and parameter updates stay in FP32. This roughly halves GPU memory usage and increases throughput on GPUs with Tensor Core hardware (A100, V100, H100).dingo_train_condorrequest_gpusin the HTCondor submission file is now set automatically fromlocal.num_gpus. Users no longer need to specify it in thecondorblock.Settings file changes
New optional keys in local:
New optional keys per training stage
condorblock:num_gpusremovednum_gpusis no longer needed (or expected) insidecondor. It is injected automatically fromlocal.num_gpuswhen building the submission file:Backward compatibility
num_gpusunderlocal.condor: Existing settings files that specifynum_gpusinside thecondorblock continue to work. A DeprecationWarning is raised at runtime prompting users to move the key tolocal.num_gpus. This fallback will be removed in a future release.num_gpuskey defaults to 1 if absent.batch_sizesemantics: batch_size has always been documented as the total effective batch size. This PR enforces that interpretation — whennum_gpus > 1, each GPU receivesbatch_size / num_gpus samples. No change for single-GPU users.Requirements
torch.amp.GradScalerandtorch.amp.autocast; the deprecatedtorch.cuda.ampnamespace is no longer used)Tests
tests/core/test_multi_gpu.py— coversget_num_gpus(including deprecation warning),build_train_and_test_loaderswith and without DDP samplers,LossInfoandRuntimeLimitsin a real two-processgloogroup, DDP state-dict stripping, and utility functions (set_seed_based_on_rank,replace_BatchNorm_with_SyncBatchNorm).tests/core/test_amp.py— covers AMP imports,train_epochwithout AMP (including gradient accumulation), andtrain_epochwith AMP on CUDA (skipped if no GPU).Integration tests on an htcondor cluster with the NPE example:
dingo_trainon 1 GPU: Training works on A100 8 GPU node withnum_gpus: 1dingo_trainon 8 GPUs: Training worksdingo_train_condoron 1 GPU: Training worksdingo_train_condoron 8 GPUs: Training worksTo test the performance, I investigated how the training time per epoch scales with the batch size on an NVIDIA A100 8-GPU node with 80 GB GPU memory:


batch_size / num_gpus. However, the overhead from gradient accumulation leads to a scaling which is worse than the ideal projection given by