datarekha

Pipeline parallelism

When a model is too big to fit on one GPU, you split it across GPUs by layer — an assembly line of stages. The catch is the 'pipeline bubble' of idle GPUs; micro-batching, 1F1B, and DeepSeek's DualPipe are how you shrink it.

9 min read Advanced Deep Learning Lesson 18 of 28

What you'll learn

  • Why huge models need model parallelism, and how pipeline parallelism splits by layer
  • The pipeline bubble — why a naive pipeline leaves most GPUs idle
  • How micro-batching (GPipe) shrinks the bubble, and the exact bubble formula
  • 1F1B for memory, DualPipe for overlap, and where pipeline fits the parallelism zoo

Before you start

Data parallelism (DDP) puts a full copy of the model on every GPU and splits the data — which is useless the moment the model itself no longer fits on one GPU. A frontier model’s weights, gradients, and optimizer state run to terabytes; you have to split the model. Pipeline parallelism is the most intuitive way to do it: cut the network into consecutive stages and put each stage on a different GPU, like an assembly line.

GPU 0 holds the first chunk of layers, GPU 1 the next, and so on. A batch flows through stage 0, whose output is handed to stage 1, then stage 2… That works — but done naively it wastes almost everything.

The pipeline bubble

Run a single batch through the line and watch the GPUs: while stage 0 computes, stages 1–3 sit idle waiting for its output. When the work reaches stage 1, stage 0 has nothing to do. At any instant only one stage is busy — so with P stages you get 1/P utilization. Four GPUs running at 25% is a catastrophic way to spend a cluster. That idle time is the pipeline bubble.

The fix is to keep all stages busy at once by feeding the pipeline a stream of work instead of one batch. Split the batch into M micro-batches and pump them in back-to-back (this is GPipe): as micro-batch 1 advances to stage 1, micro-batch 2 enters stage 0 behind it, and soon every stage is working on a different micro-batch.

GPipe schedule — micro-batches fill the pipelineGPU 0GPU 1GPU 2GPU 3time →bubbleF1F2F3F4F1F2F3F4F1F2F3F4F1F2F3F4
Four micro-batches (F1–F4) stream diagonally through four stages. Once the pipeline is full, all four GPUs work at once; the dashed cells are the bubble — the unavoidable ramp-up and ramp-down.

How much bubble is left?

The bubble never fully vanishes — you still pay one ramp-up and one ramp-down — but its fraction shrinks as you add micro-batches. For P stages and M micro-batches the bubble fraction is (P − 1) / (M + P − 1):

def bubble_fraction(P, M):
    return (P - 1) / (M + P - 1)

P = 4  # four pipeline stages
for M in [1, 2, 4, 8, 16, 32]:
    print(f"micro-batches M={M:>2}: bubble = {bubble_fraction(P, M):.1%} idle")
micro-batches M= 1: bubble = 75.0% idle
micro-batches M= 2: bubble = 60.0% idle
micro-batches M= 4: bubble = 42.9% idle
micro-batches M= 8: bubble = 27.3% idle
micro-batches M=16: bubble = 15.8% idle
micro-batches M=32: bubble = 8.6% idle

M = 1 is the naive case — 75% of a 4-GPU cluster idle. Push 32 micro-batches through and the bubble falls under 10%. The rule of thumb: keep M several times larger than P. You can’t shrink it forever, though — more micro-batches means more in-flight activations to hold in memory, which is the next problem.

1F1B and DualPipe

1F1B (“one-forward-one-backward”, from PipeDream) tackles that memory cost. Plain GPipe runs all M forward passes, then all M backward passes — so it must store activations for every in-flight micro-batch at the peak. 1F1B instead interleaves: as soon as a micro-batch finishes its forward pass through the last stage, it starts flowing backward, freeing its activations early. Same bubble, far lower peak activation memory — which pairs naturally with activation checkpointing.

DualPipe (DeepSeek-V3) goes further: it runs the pipeline bidirectionally and deliberately overlaps the inter-stage communication with computation, so the GPU is computing one micro-batch while it ships another’s activations across the network. That overlap hides almost all of the remaining bubble — a big part of how DeepSeek-V3 trained so cheaply.

In one breath

  • When a model is too big for one GPU, data parallelism can’t help (it replicates the whole model); you must split the model, and pipeline parallelism splits it by layer into stages, one per GPU — an assembly line.
  • Naively only one stage works at a time (1/P utilization); the idle time is the pipeline bubble.
  • GPipe micro-batching streams M micro-batches so all stages stay busy; the bubble fraction is (P−1)/(M+P−1) — ~75% at M=1 but under 10% at M=32 for 4 stages.
  • 1F1B interleaves forward/backward to cut peak activation memory; DualPipe (DeepSeek-V3) overlaps communication with compute to hide almost all of the remaining bubble.
  • Pipeline is one axis of 4D parallelism — data, tensor, pipeline, and expert splits combined to train the largest models.

Quick check

Quick check

0/4
Q1Why can't data parallelism (DDP) train a model that's too big for a single GPU?
Q2What is the 'pipeline bubble'?
Q3How does GPipe shrink the bubble, and what's the trade-off?
Q4What does DualPipe (DeepSeek-V3) add over plain GPipe/1F1B?

Next

Pipeline parallelism is one piece of how frontier models are trained; see how it and the rest of the modern stack come together in the frontier LLM walkthrough, and revisit Multi-GPU training for the data-parallel half (DDP & FSDP).

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Related lessons

Explore further

Skip to content