Project 2¶
Distributed Data Parallel Training on CIFAR-10¶
In this project you will take a provided single-GPU training script for a WideResNet classifier on the CIFAR-10 dataset and parallelize it using PyTorch's Distributed Data Parallel (DDP) framework. You will deploy and benchmark your implementation on two NVIDIA A30 GPUs on Virginia Tech's ARC cluster, Falcon.
This project brings together everything covered in the lecture series: tensor operations, autograd, model construction, the training loop, optimization hyperparameters, and — centrally — distributed data parallel training.
Learning Objectives¶
By the end of this project you should be able to:
- Understand the multi-process execution model underlying DDP.
- Correctly initialize and tear down a distributed process group.
- Partition data across workers using
DistributedSampler. - Wrap a model with
DistributedDataParalleland pin each process to its assigned GPU. - Synchronize metrics (loss, accuracy, throughput) across ranks.
- Reason about the performance implications of your design choices.
Provided Materials¶
You are given:
cifar10.py— a fully working single-GPU training script that trains a WideResNet on CIFAR-10. This is your starting point.- A reference DDP implementation for the Fashion-MNIST dataset (posted on the course page). This is a reference only — you are not submitting it and should not copy it blindly. Study it to understand the DDP idioms, then apply those idioms to the CIFAR-10 script.
- A Slurm job script template for Falcon (
job_template.shat docs/assignments/projects).
Deliverables¶
Submit the following to the course submission portal by the deadline.
| File | Description |
|---|---|
cifar10_ddp.py |
Your parallelized training script |
job.sh |
The Slurm job script you used on Falcon |
output.log |
The stdout log from your successful two-GPU run |
README.md |
Written report |
Do not submit dataset files or Python virtual environments.
Cluster Setup (Falcon)¶
Use the provided Slurm template job_template.sh at docs/assignments/projects.
We provide a detailed description how to use PyTorch on Falcon.
Remember to benchmark your code on two A30 GPUs.
Tips¶
- Start early. Cluster jobs can queue for hours during peak periods.
- Test locally first. Run with just 1 GPU on Google Colab (or your own machine if you have a GPU) to catch bugs before requesting a multi-GPU allocation on Falcon.
- Read error messages carefully. Most DDP initialization failures come from
mismatched
WORLD_SIZE/rankvalues or an unreachableMASTER_ADDR. - Use
dist.barrier()deliberately. Place barriers only where synchronization is semantically required; excessive barriers hurt performance. - Profile before optimizing. For the challenge, measure where time is actually spent (data loading, forward pass, backward pass, communication) before guessing what to change.
General Instructions¶
All submitted work must be stored in your team's GitLab repository. Below it will be explained how to do that. For the repository it is important that:
-
Create a new repository on GitLab at code.vt.edu:
- one project = one GitLab repository → each project gets its own GitLab repository
- all team members of your project must be members of the repository
- your repository must be private
- your repository must be named exactly as specified: do not change the name or
the upper-case/lower-case letters
cmda3634-pr2 - create commits frequently, at least one commit per task is recommended
- push your work frequently (this also is your backup if your laptop breaks)
- your repository must be up-to-date before the deadline
- you must double-check your repository in the web browser to make sure all your work is uploaded to the code.vt.edu server
-
Add the instructor and TA to your repository as a member with Developer access. In the web browser with your new repository open:
- in the left sidebar, hover the mouse cursor over "Manage"
- in the menu that pops up, click "Members"; a new page loads
- on the new page, click button "Invite members"
- enter the
jrudi - select maximum role: "Developer"
- click the button "Invite"
- enter the
- repeat to invite a new member:
- enter the
jraind - select maximum role: "Developer"
- click the button "Invite"
- enter the
⚠️ You must run your code yourself! Not following this is an Honor Code violation.
⚠️ You must create complete log files of all AI conversations! Not following this is an Honor Code violation.
Requirements¶
Your submission must satisfy all of the following.
R1 — Correct DDP Integration (50 points)¶
Modify cifar10.py so that training can be distributed across an arbitrary
number of GPUs and nodes. Concretely, your script must:
- Accept command-line arguments for
--num-nodes,--node-id, and--num-gpus. - Compute the correct
WORLD_SIZEand each process'sglobal_rank. - Initialize the process group with
dist.init_process_group(backend"nccl"). - Download the dataset on only one process per node; use
dist.barrier()to synchronize before other processes open the dataset. - Create a
DistributedSamplerfor both the training and validation sets, and pass the appropriate sampler to eachDataLoader. - Call
train_sampler.set_epoch(epoch)at the start of every epoch to ensure different shuffle orderings across epochs. - Assign each process to the correct GPU (
"cuda:<local_rank>"). - Wrap the model with
nn.parallel.DistributedDataParallel. - After each training epoch, reduce
images_per_secto rank 0 withdist.reduce; usedist.all_reducewithdist.ReduceOp.AVGto average validation loss and accuracy across all ranks. - Print per-epoch metrics (cumulative time, epoch time, images/sec, validation loss, validation accuracy) from rank 0 only.
- Launch worker processes with
torch.multiprocessing.spawninside theif __name__ == '__main__':guard.
R2 — Target Accuracy (20 points)¶
Your training run on two A30 GPUs must reach a validation accuracy ≥ 0.85 for at least two consecutive epochs before stopping. You may use the provided early- stopping logic or implement your own.
R3 — Report (20 points)¶
Submit a short written report (PDF, ≤ 4 pages) that includes:
- A brief description of each DDP change you made and why it is necessary.
- The printed epoch log from your successful two-GPU run (copy-paste or screenshot).
- Your total cumulative training time at the point the stopping criterion was first met.
- A discussion of any optimizations you applied (see Challenge below).
Challenge: Fastest Training Time (up to 10 bonus points)¶
This part is optional but encouraged.
Once your implementation is correct, optimize it for wall-clock time to reach validation accuracy ≥ 0.85 for two consecutive epochs on exactly two A30 GPUs.
You are free to modify anything in the training algorithm: the learning rate
schedule, batch size, optimizer, data augmentation, mixed-precision training,
SyncBatchNorm usage, gradient compression, or anything else. The model architecture
(WideResNet) and the dataset (CIFAR-10) must remain unchanged.
The top-performing submissions will be re-run by the instructors in a controlled benchmark. Bonus points (up to 10) are awarded based on your cumulative training time relative to the other submissions. The fastest valid run receives the full 10 points; all other valid runs receive a fractional score computed from the time difference using an undisclosed formula.
In your report, dedicate a section to the challenge. For each optimization you applied, state:
- what you changed,
- why you expected it to help, and
- the measured impact (before/after cumulative time or images/sec).
If you don't document an optimization, it will be turned off in the instructor's benchmark.
Demonstrate (in your documentation and project presentation)¶
1. Process Topology and Rank Assignment¶
Using a diagram, show your node/GPU layout and explain how global_rank is computed
from node_id and local_rank. Point to the lines of code that pin each process to
its GPU and initialize the process group.
2. Data Partitioning¶
Explain what DistributedSampler does and why each process must see a distinct shard.
Explain why train_sampler.set_epoch(epoch) is required each epoch.
3. Gradient Synchronization¶
Explain conceptually what DistributedDataParallel does during the backward pass:
when the all-reduce fires, what is averaged, and why all replicas stay identical after
each optimizer step.
4. Metric Aggregation¶
Show your aggregation code and explain why validation accuracy/loss uses
ReduceOp.AVG while images_per_sec uses a sum. What does the summed throughput
represent physically?
5. Correctness and Scaling¶
Present the epoch log from your two-GPU run on Falcon. In a table, compare 1 GPU vs. 2 GPUs: cumulative time to stopping criterion, images/sec, and final validation accuracy. Relate the observed speedup to Amdahl's law and identify any bottleneck.
6. Optimizations (Challenge participants)¶
For each optimization: state what you changed, why you expected it to help, and provide before/after numbers (time, images/sec, or epochs to convergence). Report negative results honestly.