PyTorch on GPU
Making PyTorch models actually use the hardware you paid for.
This page covers GPU-specific optimization for PyTorch. For install/tensors/dataloaders/checkpoint basics, see PyTorch basics.
Expect a slow first iteration (or first per new input shape) while it traces and compiles; variable/dynamic input shapes without dynamic=True can trigger a recompile every time, silently erasing the speedup.
PyTorch's CUDA/HIP caching allocator holds onto freed memory instead of returning it to the driver, so repeated alloc/free of similarly-sized tensors is fast after warmup — but it means nvidia-smi/rocm-smi memory usage reflects the allocator's reserved pool, not just what's "live."
If GPU utilization (check via nvidia-smi dmon or rocm-smi) sits well below 100% during training, the data pipeline — not the model — is usually the bottleneck.
DDP scales well because the only cross-GPU communication is gradient all-reduce, which is bandwidth-bound and overlappable — contrast with naive data-parallel approaches that gather full outputs to one GPU and serialize.
Two separate costs stack on top of raw kernel time: Python/dispatcher overhead (every op call walks through Python, autograd, and dispatch machinery before a single GPU instruction issues) and kernel launch overhead (each of those tiny ops is a separate launch, and a model with thousands of small ops can be launch-bound rather than compute-bound). Fusion (kernel fusion, torch.compile) attacks exactly this by turning many small launches into fewer, larger ones.