Why pipeline parallelism scales poorly, and why the bubble, not the code, is usually the problem
Pipeline parallel training throughput does not scale with additional stages, GPU utilization drops sharply as pipeline depth increases
Also appears as
- Adding more pipeline parallel stages makes each training step slower per GPU, not faster
- GPipe or PipeDream-style pipeline shows large idle gaps between microbatches in the profiler
Short answer
Pipeline parallelism scales poorly when the number of microbatches per training step is too small relative to the number of pipeline stages, because the unavoidable fill and drain bubble at the start and end of each step is proportional to the stage count minus one divided by the number of microbatches. With too few microbatches, GPUs spend a large fraction of every step idle waiting for the pipeline to fill or drain, and adding more stages without also adding more microbatches makes this worse, not better.
Affects: DeepSpeed pipeline parallelism, Megatron-style pipeline parallel training, and any GPipe or PipeDream-style implementation, most visible above 4 pipeline stages
Increase microbatches relative to pipeline stages before changing anything else
- 1Calculate your current bubble fraction as (pipeline_stages minus 1) divided by num_microbatches; anything above roughly 25 percent means the bubble is likely dominating your step time.
- 2Increase gradient_accumulation_steps or your framework's num_microbatches setting so it is at least 4 times your pipeline stage count, adjusting global batch size accordingly.
- 3If activation memory is the reason microbatches are limited, add or increase activation checkpointing to shrink per-microbatch memory footprint, freeing room for more, smaller microbatches.
- 4Check that layers are partitioned roughly evenly across stages, since one heavy stage sets the pace for the whole pipeline regardless of microbatch count.
- 5Profile with a pipeline trace or torch.profiler to confirm the idle gaps actually shrink after increasing microbatches, rather than assuming it worked.
How to confirm this is your problem
- GPU utilization graphs show clear idle gaps at the start and end of every training step, worse with more pipeline stages
- Throughput per GPU decreases as you increase pipeline parallel degree while holding total GPU count and batch size fixed
- Increasing pipeline stages was expected to speed up training but instead made each step slower
- The profiler trace shows large blocks of GPU idle time between microbatch forward and backward passes, especially on the first and last stages
Root causes and fixes
Too few microbatches relative to the number of pipeline stages, so the fill and drain bubble dominates total step time
Pipeline parallelism only overlaps stages once the pipeline is full: at the start of every step, stage 2 must wait for stage 1 to finish its first microbatch, stage 3 waits for stage 2, and so on, with a mirrored drain at the end. The fraction of step time spent in this unavoidable bubble is roughly the stage count minus one divided by the number of microbatches, so with only as many microbatches as stages, more than half the step can be idle bubble time, and adding stages without adding microbatches makes the ratio worse.
Fix: Increase the number of microbatches, via gradient accumulation or your framework's num_microbatches setting, to several times your pipeline stage count; a common rule of thumb is 4x or more, so the bubble becomes a small fraction of total step time.
Layers are partitioned unevenly across pipeline stages, so one heavy stage sets the pace for every other stage
Pipeline parallel frameworks typically split a model into stages by layer count, not actual compute cost per layer. Embedding and output layers can be disproportionately expensive, especially with large vocabularies, or disproportionately cheap compared to a typical transformer block, so an even layer count split can still produce a badly imbalanced compute split, and every stage's throughput is capped by the slowest one.
Fix: Manually rebalance the partition points based on measured per-layer compute time rather than layer count alone, giving the embedding and output stages fewer transformer blocks to compensate for their extra cost.
Activation memory limits per-microbatch size, which caps how many microbatches can run without exceeding VRAM, directly worsening the bubble
Smaller microbatches use less activation memory per stage but require more of them to reach a target global batch size; if activation memory is already tight, you may be forced into fewer, larger microbatches purely to fit in VRAM, which is exactly the configuration that maximizes bubble overhead.
Fix: Add or increase activation checkpointing, recomputing activations during backward instead of storing them, to reduce per-microbatch memory footprint and free headroom for more, smaller microbatches.
Pipeline parallelism is combined with tensor or data parallelism without accounting for the communication interaction between them
In 3D-parallel setups, tensor-parallel all-reduce operations happen within each pipeline stage while pipeline stages themselves communicate activations forward and gradients backward between GPUs; if these are not scheduled to overlap properly, tensor-parallel communication can serialize with pipeline handoffs, adding latency on top of the pipeline bubble rather than hiding it.
Fix: Use a framework with built-in support for combined 3D parallelism scheduling rather than hand-rolling the combination, since correct overlap of tensor-parallel and pipeline-parallel communication is a nontrivial scheduling problem.
Slow interconnect between pipeline stages adds latency to every stage handoff
Every microbatch's activations and gradients must physically move between the GPUs assigned to consecutive pipeline stages; if that link is a slow PCIe path or an oversubscribed network rather than NVLink or InfiniBand, the transfer time itself adds to the bubble on top of the theoretical fill and drain overhead, and this effect is invisible in bubble-fraction math that assumes instant handoffs.
Fix: Place adjacent pipeline stages on GPUs with the fastest available interconnect between them, same node or NVLink-connected where possible, and reserve cross-node links for the outermost or least-frequent communication boundaries.
Diagnostic commands
Calculate the theoretical bubble fraction for your current config
python -c "stages=8; microbatches=8; print((stages-1)/microbatches)"
A result above roughly 0.25 to 0.3 means a large fraction of every step is unavoidable bubble at your current microbatch count; increasing microbatches directly lowers this number.
Profile per-GPU utilization across a training step
nvidia-smi dmon -s u -d 1
Look for GPUs on the first and last pipeline stages showing long idle stretches at the start and end of each step; this visually confirms bubble overhead versus a genuine compute or communication bottleneck elsewhere.
Check per-stage compute time balance
python -m torch.utils.bottleneck train.py
Compare wall-clock time spent in each pipeline stage; a significantly slower stage, often the embedding or output layer stage, indicates a partitioning imbalance rather than, or in addition to, a bubble problem.
Stopping it from happening again
- Set microbatch count relative to pipeline depth as a deliberate ratio (4x or higher) in your training configuration, not an afterthought left at the default.
- Rebalance pipeline stage boundaries based on measured per-layer timing whenever you change model architecture or vocabulary size, not just layer count.
- Budget activation checkpointing into your memory plan from the start so it does not become a forced tradeoff against microbatch count later.
- When combining pipeline parallelism with tensor or data parallelism, use a framework with tested 3D-parallel scheduling rather than a custom implementation.
When this becomes an architecture problem
If you have already maximized microbatch count within memory limits and rebalanced stages, and the bubble is still large because your model is too small to justify the pipeline depth you are using, the right fix is usually reducing pipeline parallel degree and relying more on data or tensor parallelism, which is a parallelism-strategy decision worth an architecture review rather than continued pipeline tuning.
Frequently asked questions
How many microbatches do I actually need for good pipeline parallel efficiency?
As a rule of thumb, aim for at least 4 times your pipeline stage count, though the exact number depends on how much bubble overhead you can tolerate. The bubble fraction is the stage count minus one divided by microbatches, so more microbatches always reduces it, subject to the memory and compute cost of managing more, smaller units of work.
Does pipeline parallelism ever make sense for a small number of GPUs?
It can, but the bubble overhead is proportionally worse with few microbatches relative to stages, so pipeline parallelism tends to pay off more at larger scale where you can afford enough microbatches to amortize the bubble. For a small GPU count, tensor or data parallelism alone often scales better with less tuning complexity.
Why does GPU utilization look fine on average but training is still slow?
Average utilization over a whole run can hide short, repeated idle bursts at the start and end of every pipeline step; look at utilization on a per-step or sub-second timescale, and specifically on the first and last pipeline stages, rather than a coarse average, to see the bubble pattern clearly.
Size it properly next time
Free calculators that prevent this class of failure before you provision hardware.
Multi-GPU Tensor Parallelism Calculator
Model how tensor-parallel throughput actually scales across multiple GPUs, accounting for interconnect overhead that keeps scaling sub-linear.
Free ToolFine-Tuning GPU-Hours Estimator
Estimate GPU-hours for LoRA, QLoRA, and full fine-tuning on the same model size and dataset, so you can compare method tradeoffs before choosing.
Free ToolGPU Cluster Buildout Cost Calculator
Turn GPU count and class into a full cluster budget covering server chassis, networking fabric, power and cooling capex, and install, with a true cost per GPU.
Related problems
Tensor parallelism fails because the model does not split evenly across GPUs
Tensor parallelism fails when the chosen degree does not evenly divide the model's attention heads, and often its key/value heads and hidden size, so the framework cannot split the projection weights across ranks. It also fails in practice, without an assertion, when the GPUs assigned to the TP group differ in VRAM or compute, since the even weight split then fits some ranks and not others.
NCCL collective operation timeout during distributed training
An NCCL timeout means one or more ranks did not reach a collective operation (all-reduce, broadcast, all-gather) within the configured window, almost always because a straggler rank is slow or stuck, not because NCCL is malfunctioning. Raising NCCL_TIMEOUT can mask the symptom, but the durable fix is finding and removing the straggler: a data loading stall, an OOM-crashed rank, or a checkpoint write blocking one process.
DeepSpeed ZeRO configuration errors at training startup
DeepSpeed refuses to start when its config's batch-size fields do not agree with each other, since train_batch_size must equal train_micro_batch_size_per_gpu times gradient_accumulation_steps times world_size exactly. A second common failure comes from enabling ZeRO stage 3 with CPU or NVMe offload on hardware that lacks enough system RAM or fast enough storage, which surfaces as tensor or contiguity errors rather than a clear resource message.
GPU utilization stays low during LLM inference even under load
Low GPU utilization during inference almost always means the GPU is waiting on something else: request concurrency is too low for the batching scheduler to fill, the client code is calling the server synchronously one request at a time, tokenization or network I/O is serialized in front of the GPU call, or max-num-seqs is set too low to admit enough concurrent sequences. Raising effective concurrency, either by fixing the client or the server's admission limits, is almost always the fix, not more GPU compute.
GuideMulti-Node LLM Training Infrastructure: Networking and Storage
Multi-node LLM training infrastructure explained: InfiniBand vs RoCE tradeoffs, storage throughput needs, and cluster topology for enterprise fine-tuning.
GuideMulti-GPU LLM Serving: Tensor vs Pipeline Parallelism
Multi-GPU LLM serving explained: tensor parallelism vs pipeline parallelism, NCCL interconnect requirements, and when to split a model across GPUs.
GuideOn-Prem GPU Cluster Design: Node Sizing, Networking, and Storage
Design an on-prem GPU cluster: node sizing for H100/H200/B200, InfiniBand vs RoCE networking, storage throughput, and rack power for enterprise AI workloads.
Still stuck, or tired of fighting your own infrastructure?
Netray deploys and operates on-prem AI for regulated manufacturers and defense suppliers. We have debugged this stack in production, on air-gapped networks, at scale.