Model parallelism (tensor, pipeline)
When models grow too large to fit on a single GPU's memory, model parallelism splits the model itself across multiple devices, unlike data parallelism which replicates the entire model.
Connections
- Data Parallelism - complementary approach (replicate model, split data)
- Gradient Accumulation - reduces memory for batch size
- Mixed Precision Training - reduces memory per parameter
- Transformer Architecture - common target for model parallelism
- GPU Memory Hierarchy - hardware constraint driving the need
- Activation Checkpointing - trades compute for memory
Model parallelism says: "If the model doesn't fit on one device, cut the model into pieces and put each piece on a different device."
Two main strategies exist:
- Tensor Parallelism: Split individual layers (cutting matrices horizontally/vertically)
- Pipeline Parallelism: Split model depth-wise (early layers on GPU-0, later layers on GPU-1)
Tensor Parallelism
Derivation: How to Split a Matrix Multiply
Consider a simple linear layer: where:
- (batch × input dimension)
- (weight matrix)
- (output)
Why can we split this? Matrix multiplication is associative with concatenation.
Column-wise Split (most common)
Split into column slices: where each .
Derivation:
So device computes , then we concatenate: .
Key insight: Each device needs the full input but only a fraction of . Memory saved on weights, but need to broadcast to all devices.
Why this step? The column split works because matrix-vector multiplication distributes: each column block of produces an independent column block of .
What about the backward pass?
Gradients flow:
If is split column-wise, then is also split. Device computes:
Critical: To compute (needed for earlier layers):
This requires an all-reduce to sum contributions from all devices.
Why this step? The chain rule demands we sum over all output dimensions to get gradients w.r.t. inputs Since outputs are split, we must communicate to aggregate.
A Transformer MLP has two linear layers:
where (expand) and (project).
Column-parallel on , Row-parallel on :
- Device of : Store
- Compute locally:
- No communication yet! Each device has a slice of the hidden state
- Store (row slice)
- Compute:
- All-reduce:
Why this pattern? The column-parallel followed by row-parallel design minimizes communication: only one all-reduce per MLP block, not one per layer.
Memory saved: and are each split by , so roughly of the MLP parameters per device.
Why this step? The activation after is an intermediate that doesn't need to be gathered. We only gather the final output after , reducing communication overhead.
Attention has projection matrices and output projection .
Split across attention heads: If8 heads, put 2 heads on each of 4 devices.
Device computes:
where , etc., with .
Then concatenate and apply (row-parallel), followed by all-reduce.
Why this step? Attention heads are independent, so they naturally parallelize. Each device computes a subset of heads, reducing memory and compute per device.
Pipeline Parallelism
Naive Pipeline: The Bubble Problem
Setup: Model with 8 layers, 4 GPUs. GPU-0 has layers 0-1, GPU-1 has layers 2-3, etc.
Forward pass:
- GPU-0 processes batch → sends activations to GPU-1
- GPU-1 processes → sends to GPU-2
- etc.
Problem: GPU-1 sits idle while GPU-0 computes. GPU-2 idles while GPU-0 and GPU-1 work. This creates a "bubble" of unused compute time.
Utilization: With devices and micro-batches, bubble time is approximately of total time. For , that's 75% idle!
Why this happens: Sequential dependency. Later stages wait for earlier stages. This is the fundamental trade-off of pipeline parallelism.
GPipe: Micro-Batching Solution
Timeline:
GPU-0: [F₀] [F₁] [F₂] [F₃] [B₀] [B₁] [B₂] [B₃]
GPU-1: [F₀] [F₁] [F₂] [F₃] [B₀] [B₁] [B₂] [B₃]
GPU-2: [F₀] [F₁] [F₂] [F₃] [B₀] [B₁] [B₂] [B₃]
GPU-3: [F₀] [F₁] [F₂] [F₃] [B₀] [B₁] [B₂] [B₃]
= forward micro-batch , = backward micro-batch
Bubble fraction: where = number of micro-batches.
With : bubble = . Much better!
Cost: Need to store activations for all micro-batches for backward pass. Memory increases linearly with .
Why this step? More micro-batches keep later stages busy while early stages are still processing. The pipeline fills up, reducing idle time at the cost of activation memory.
Model: 48 layers, 4 GPUs (12 layers each), micro-batch size 1, total batch 32.
Scenario 1: micro-batches
- Bubble time:
- Activation memory: 8× single micro-batch
- Throughput: ~5.5 samples/sec
Scenario 2: micro-batches
- Bubble time:
- Activation memory: 32× single micro-batch
- Throughput: ~6.8 samples/sec
Trade-off: Higher improves efficiency but increases memory. GPipe uses activation checkpointing (recompute activations during backward) to reduce memory at cost of 33% more compute.
Why this example? Shows the quantitative impact of pipeline depth. Real systems must balance based on available memory and desired throughput.
PipeDream: 1F1B Schedule
Problem with GPipe: All forward passes, then all backward passes. Memory spike for activations.
PipeDream solution: Interleave forward and backward: "1 Forward, 1 Backward" schedule.
Timeline:
GPU-0: [F₀] [F₁] [F₂] [F₃] [B₀] [F₄] [B₁] [F₅] [B₂] [F₆] [B₃] ...
GPU-1: [F₀] [F₁] [F₂] [B₀] [F₃] [B₁] [F₄] [B₂] [F₅] [B₃] ...
Why this helps: Backward passes for early micro-batches happen before later forward passes. Activations are freed soner. Steady-state memory: only micro-batches' activations (# of pipeline stages), not (total micro-batches).
Complication: Earlier layers see gradients from older micro-batches (version inconsistency). PipeDream uses weight stashing: keep multiple weight versions during training to ensure each micro-batch's forward and backward use the same weights.
Why this step? By overlapping forward and backward passes, we reduce the peak memory from to , making deeper pipelines feasible without running out of memory.
Why this feels right: Pipeline seems simpler—just split layers, no complex matrix slicing.
Why it's wrong: Pipeline has unavoidable bubble time and inter-device communication between every layer partition. Tensor parallelism has communication within each layer but can process batches in parallel across all devices simultaneously (no bubble in steady state).
When each wins:
- Tensor parallelism: Better for wide models (large hidden dimensions), high inter-device bandwidth (NVLink). Communication cost is proportional to hidden size, not depth.
- Pipeline parallelism: Better for deep models with limited bandwidth. Communication happens only at partition boundaries (fewer times per forward pass).
Reality: Modern systems (Megatron-LM, DepSpeed) combine both. Use tensor parallelism within a node (fast NVLink) and pipeline parallelism across nodes (slower InfiniBand).
The fix: Profile your model and hardware. If communication time< bubble time, prefer tensor. If memory per layer is the bottleneck, prefer pipeline.
Why this feels right: Smaller batches → less activation memory → model fits on one GPU.
Why it's wrong: Model parameters and optimizer states are independent of batch size. A 175B parameter model needs 700GB for weights alone (FP32). Reducing batch size to 1 doesn't change this.
The confusion: Activation memory (forward pass intermediate results) does scale with batch size. But parameter memory dominates for large models.
The numbers:
- Parameters: 175B × 4 bytes = 700GB (fixed)
- Optimizer states (Adam): 175B × 12 bytes = 2.1TB (fixed)
- Activations: ~batch_size × seq_len × hidden_dim × num_layers (variable)
For GPT-3, even with batch size 1, parameter + optimizer memory exceds any single GPU.
The fix: Model parallelism is necessary when parameter count exceeds device memory. Activation memory reduction (gradient accumulation, checkpointing) is complementary.
Communication Patterns
Tensor Parallelism (per layer):
where = batch size, = sequence length, = hidden dimension.
Factor of 2: all-gather after forward (to broadcast activations) + reduce-scatter after backward (to aggregate gradients).
Pipeline Parallelism (per stage boundary):
Only forward activation sent to next stage and backward gradient sent to previous stage. No all-reduce within the pipeline.
Why this matters: For tensor parallelism, communication grows with hidden size. For pipeline, it's constant per partition boundary. This guides the hybrid strategy.
Goal: Train 530B parameter model (Megatron-Turing NLG).
Hardware:
- 4480 GPUs (A100 80GB)
- Nodes with 8 GPUs connected by NVLink (900 GB/s)
- Nodes connected by InfiniBand (200Gb/s)
Configuration:
- Tensor parallelism: within each node (fast NVLink makes all-reduce cheap)
- Pipeline parallelism: stages across nodes (reduces cross-node communication)
- Data paralelism: 16-way across pipeline replicas
Why this works:
- Tensor paralelism exploits fast intra-node bandwidth
- Pipeline paralelism limits slow inter-node bandwidth use
- Data parallelism scales training across the full cluster
Memory per GPU:
- Model: 530B / (8 × 35) = 1.9B parameters per GPU ≈ 7.6GB (FP32)
- With optimizer states: ~23GB per GPU
- Leaves room for activations and workspace
Why this step? This is a real-world example of how paralelism strategies compose. Each dimension of paralelism addresses a different bottleneck.
Recall Explain to a 12-year-old
Imagine you're trying to build the world's biggest LEGO castle, but it's so huge that all the pieces won't fit on your table. What do you do?
Option 1 (Tensor Parallelism): You and your friend each take half the pieces for same wall. You both build your half, then snap them together. For the next wall, you split pieces again. You work together on each part, but share the load.
Option 2 (Pipeline Parallelism): You build the first floor, then pass it to your friend who builds the second floor, while you start the first floor of a new castle. Like an assembly line—everyone has their own stage, and castles flow through.
The problem? In Option 1, you both need to see the instruction book (communication). In Option 2, your friend waits while you finish (bubble time).
Real AI systems do both! Fast splits (tensor) with friends at your table, assembly line (pipeline) between tables across the room.
TENSOR = Together Each Node Slices One Row → All devices work on the same layer, split horizontally
PIPELINE = Pass Inputs Progressively Each Layer In Next Element → Data flows through layers like a pipe
When to use: "Tight bandwidth → Tensor; Poor links → Pipeline"
Flashcards
What is the fundamental difference between data parallelism and model parallelism? :: Data parallelism replicates the entire model on each device and splits the data batch. Model parallelism splits the model itself across devices because it's too large to fit on one device.
What is tensor parallelism?
What is pipeline parallelism? :: Splitting the model depth-wise across devices, where each device holds different layers and data flows sequentially like a pipeline (inter-layer parallelism).
For a column-wise split of matrix across devices in tensor parallelism, what communication is needed in the forward pass?
For a column-wise split of in tensor parallelism, what communication is needed in the backward pass?
What is the "bubble problem" in naive pipeline parallelism?
How does GPipe reduce pipeline bubles?
What is the memory cost of GPipe's micro-batching?
What is the 1F1B schedule in PipeDream?
Why does tensor parallelism require more communication than pipeline parallelism?
When is tensor parallelism preferred over pipeline parallelism?
When is pipeline parallelism preferred over tensor parallelism?
Why do modern systems like Megatron-LM use hybrid paralelism?
Can reducing batch size eliminate the need for model parallelism?
Concept Map
Hinglish (regional understanding)
Intuition Hinglish mein samjho
Dekho beta, yahan core problem ye hai ki aaj kal ke bade models (jaise GPT-3 jismein 175 billion parameters hain) itne bade ho gaye hain ki ek single GPU ki memory mein fit hi nahi hote. Sirf parameters store karne ke liye 700GB chahiye, aur optimizer states add karo to 2 TB se upar chala jata hai! Koi bhi single GPU itni badi nahi hoti. To solution kya hai? Model parallelism kehta hai — "agar model ek device pe fit nahi hota, to model ko tukdon mein kaat do aur har tukda alag GPU pe rakh do." Ye data parallelism se alag hai, jismein poora model copy hota hai aur sirf data split hota hai.
Ab do main tarike hain isko karne ke. Pehla hai Tensor Parallelism, jismein hum ek single layer ke andar ki weight matrix ko hi kaat dete hain — jaise ek badi matrix ko column-wise slices mein tod dena. Mast baat ye hai ki matrix multiplication ki property ke wajah se, agar ko columns mein baato (), to har device apna independently compute kar sakta hai, aur baad mein sab ko jodh (concatenate) lo. Har GPU ko poora input chahiye par sirf ek chhota hissa ka — isse memory bachti hai. Dusra tarika hai Pipeline Parallelism, jismein model ko depth-wise kaat te hain — matlab early layers GPU-0 pe, later layers GPU-1 pe.
Ye concept kyun important hai? Kyunki aaj real-world mein jo bhi large language models train ya deploy hote hain, wo bina model parallelism ke possible hi nahi hai. Backward pass mein thoda communication overhead aata hai — jaise gradients calculate karne ke liye devices ke beech all-reduce karna padta hai (sum lena padta hai sab contributions ka), lekin ye trade-off worth it hai kyunki iske bina to model chal hi nahi sakta. Agar tum AI-ML field mein jaana chahte ho, especially large-scale systems pe kaam karna hai, to ye samajhna zaroori hai ki hardware constraints kaise architecture design ko drive karte hain.