GPU acceleration and device management
Overview
GPU acceleration moves tensor computations from the CPU to specialized parallel processors (GPUs) that can perform thousands of operations simultaneously. Device management is the practice of explicitly controlling where tensors and models live (CPU vs GPU) and how data moves between devices.

Core Concepts
The Physics of Why GPUs Win
Derivation: Speedup Factor
The time to compute a matrix multiplication where , :
where is clock frequency (Hz), is cores (~16).
For GPU with cores (~10,000):
Speedup:
Pluging in typical values: GHz, , GHz, :
But: This assumes . If you transfer data every operation, speedup colapses.
PyTorch Device Management: From First Principles
How Tensors Know Their Location
Every PyTorch tensor has a .device property. Under the hood, it's a pointer to memory in either:
- CPU DRAM (accessible by CPU cores)
- GPU VRAM (accessible by GPU cores, isolated from CPU)
The Movement Tax
Training Loop: Device-Aware Pattern
The Standard Recipe
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# 1. Move model to device (once)
model = MyModel().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())
# 2. In training loop, move batches to device
for epoch in range(num_epochs):
for batch_idx, (data, target) in enumerate(train_loader):
# Move batch to GPU
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data) # Computed on GPU
loss = criterion(output, target) # Loss on GPU
loss.backward() # Gradients computed on GPU
optimizer.step() # Parameter update on GPU
# Only move to CPU for logging
if batch_idx % 100 == 0:
print(f'Loss: {loss.item():.4f}') # .item() copies scalar to CPUWhy each step?
.to(device)on model moves all parameters/buffers to GPU memory (one-time cost)- Moving batches to GPU: unavoidable (data loader reads from disk → CPU RAM → GPU VRAM)
.item()for logging: scalars are tiny (~4 bytes), transfer is negligible- Everything else stays on GPU: no unnecessary round-trips
Multi-GPU: Data Parallelism Derivation
The Problem
One GPU has limited memory (~24 GB). Large models or batches don't fit.
The Solution: Split the Batch
Common Mistakes
Mixed Precision Training: The 2× Speedup Trick
Why FP16?
Modern GPUs (Tensor Cores) can do FP16 matrix multiplies at 2× the FLOPS of FP32.
The problem: FP16 range is . Gradients often need FP32 precision (tiny values).
The solution: Automatic Mixed Precision (AMP)
Memory Management: The Hidden Enemy
GPU Memory Lifecycle
Allocate → Use → Free (maybe)
PyTorch uses a caching allocator:
- First allocation:
cudaMalloc(slow, 1ms) - Subsequent: reuse cached blocks (fast, 1µs)
.empty_cache()returns memory to CUDA but doesn't free to OS
Gradient Accumulation for Large Batches
When batch size 512 doesn't fit, use gradient accumulation:
accumulation_steps = 4 # Effective batch size = 128× 4 = 512
for i, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
output = model(data)
loss = criterion(output, target) / accumulation_steps # Scale loss
loss.backward() # Accumulate gradients
if (i + 1) % accumulation_steps == 0:
optimizer.step()
optimizer.zero_grad()Derivation:
- Normal:
- Accumulated: ✓
Why divide loss? .backward() sums gradients. Without /accumulation_steps, final gradients would be 4× too large.
Recall Explain to a 12-Year-Old
Imagine you have math homework:1000 multiplication problems. CPU (your brain): You solve them one-by-one. You're smart and fast, but it takes time because you do one thing at a time.
GPU (a classroom of1000 kids): Each kid solves ONE problem. They're not geniuses, but together they finish in the time it takes one kid to solve one problem!
Neural networks are like this homework—millions of simple multiplications. GPUs have thousands of "kids" (cores) working in parallel.
The catch: Moving the homework from your desk (CPU memory) to the classroom (GPU memory) takes time. So we move all problems at once (batches), not one-by-one.
Mixed precision: Some problems can be solved with simpler math (FP16 = using fewer decimal places). It's faster but sometimes makes mistakes, so we keep a "master copy" of the answer in high precision (FP32).
Connections
- 3.3.05-Computational-graphsand-autograd: GPU accelerates both forward and backward passes through the computational graph
- 3.2.03-Backpropagation: Gradient computation is embarrassingly parallel, perfect for GPUs
- 3.4.02-Batch-normalization: Requires all-reduce in multi-GPU (sync mean/variance)
- 4.1.01-CNN-architectures: Convolutions are GPU bottleneck—optimizing kernels is critical
- 3.3.07-Distributed-training-strategies: Multi-node extends device management across machines
#flashcards/ai-ml
Why do GPUs outperform CPUs for deep learning? :: GPUs have thousands of cores optimized for parallel arithmetic (low clock speed but massive parallelism), while CPUs have ~16 complex cores for sequential tasks. Neural network operations (matrix multiplies) are embarrassingly parallel—each element computed independently—matching GPU architecture perfectly.
What is the GPU memory transfer bottleneck?
What does torch.cuda.synchronize() do and why is it needed?
synchronize() blocks until all GPU kernels finish. Needed for accurate timing and when CPU must read GPU results. Without it, timing measures queue time, not compute time.Explain data parallelism's all-reduce step :: Each GPU computes gradients on its batch partition independently. All-reduce synchronizes gradients: each GPU gets the average gradient across all GPUs. Formula: . Implemented efficiently via ring all-reduce (NCCL library), bandwidth cost where is parameter count, is GPUs.
Why does mixed precision training use loss scaling?
What is gradient accumulation and when to use it?
Why does appending GPU tensors to a list cause OOM?
.item() for scalars (copies to CPU as Python float), or .detach().cpu() for larger tensors, severing GPU reference.What's the difference between memory_allocated and memory_reserved? :: memory_allocated(): currently live tensor memory. memory_reserved(): memory PyTorch holds from CUDA (includes cache). Gap = cached free blocks. After del tensor, memory_allocated drops but reserved stays high (cache). .empty_cache() returns reserved memory to CUDA but not to OS (CUDA driver still holds it).
Concept Map
Hinglish (regional understanding)
Intuition Hinglish mein samjho
Hinglish (regional understanding)
Intuition Hinglish mein samjho
Dekho, yahan core baat samajhne ki ye hai ki GPU aur CPU dono alag-alag tarah ke workers hain. CPU ek master chef ki tarah hai — bahut fast, par ek time pe thode se hi kaam kar sakta hai kyunki uske paas sirf 8-16 powerful cores hote hain. GPU thousands of simple cooks ki tarah hai — har ek chhota kaam simultaneously karta hai. Ab neural networks mein jo matrix multiplications hote hain, wo millions of independent multiply-add operations hote hain — matlab "embarrassingly parallel" kaam. Isiliye GPU pe ye kaam 100x se 300x tak fast ho jaata hai, kyunki saare operations ek saath parallel mein chal sakte hain.
Lekin ek catch hai jo bahut zaroori hai — data ko CPU (RAM) se GPU (VRAM) tak move karna padta hai, aur ye transfer SLOW hota hai (PCIe bottleneck ~16 GB/s), jabki GPU ki apni memory bandwidth ~900 GB/s hoti hai. To agar tum har chhoti operation ke liye baar-baar data idhar-udhar bhejte raho, to tumhara saara speedup advantage khatam ho jaata hai. Isiliye "Golden Rule" ye hai: data ko GPU pe hi rakho, transfers minimize karo, aur operations ko batch karke ek saath bhejo. PyTorch mein har tensor ka ek .device property hota hai jo batata hai wo CPU pe hai ya GPU pe, aur .to(device) se tum use move kar sakte ho.
Ye baat kyun matter karti hai? Kyunki jab tum real projects mein deep learning models train karoge, to sahi device management ke bina tumhara training bahut slow rahega ya crash bhi ho sakta hai. Bahut se beginners galti karte hain ki wo model GPU pe daal dete hain par data CPU pe chhod dete hain, ya baar-baar transfer karte hain — aur phir socha hua speed nahi milta. Agar tum ye intuition samajh loge — GPU ki power parallelism mein hai, aur transfer ek "tax" hai jise avoid karna hai — to tum efficient aur fast ML systems bana paoge, jo industry mein ek bahut valuable skill hai.