Hello, I'm Shrijith Venkatramana, and I'm building LiveReview — a blast-radius aware AI code review built for your business-critical systems. Star us to help devs discover the project, give it a try, and share your feedback to help improve the product.
Imagine you are training an LLM on 4,096 GPUs.
At the end of a training step, 4,095 GPUs have finished.
One GPU is still working.
So what happens?
Nothing.
The other 4,095 GPUs wait.
That is the straggler problem in synchronous distributed training: the throughput of the entire job is often determined not by the average worker, but by the slowest worker at each synchronization point.
This sounds like a minor systems nuisance. At LLM scale, it becomes an economic problem.
A training run using thousands of GPUs can spend substantial fractions of its wall-clock time waiting for work that, somewhere in the cluster, has already finished. And the culprit is often not a dying GPU or a broken network link. Recent measurements from a real LLM training cluster found that workload imbalance, sequence-length imbalance, and garbage-collection pauses can all create stragglers.
The important idea is simple:
In synchronous parallelism, variability is multiplied by synchronization.
This article works from that intuition down into the mechanics, the math, the economics, and what an engineer can actually do about it.
1. First, forget LLMs: the restaurant problem
Suppose you have 8 cooks preparing 8 dishes in parallel.
Seven finish in 10 minutes.
One takes 14 minutes.
If every dish must be plated at exactly the same time, the restaurant is waiting 4 minutes.
The average cooking time was:
(10 + 10 + 10 + 10 + 10 + 10 + 10 + 14) / 8
= 10.5 minutes
But the batch time was:
max(10, 10, 10, 10, 10, 10, 10, 14)
= 14 minutes
That distinction is the entire problem.
For independent work, average worker speed matters.
For a synchronized batch, the maximum worker time matters.
LLM data-parallel training has exactly this structure.
A simplified training step looks like:
GPU 0: forward -> backward -> \
GPU 1: forward -> backward -> \
GPU 2: forward -> backward -> --> all-reduce --> next step
GPU 3: forward -> backward -> /
...
GPU N: forward -> backward -> /
The all-reduce is a synchronization barrier. Conceptually, the next iteration cannot proceed until the required participants have reached the synchronization point.
So the time for one step is roughly:
T_step ~= max(T_0, T_1, ..., T_N) + T_sync
not:
average(T_0, T_1, ..., T_N) + T_sync
This is why a 5% slowdown on one worker can produce a much larger than 5% effect on system utilization.
The phenomenon predates modern LLMs by more than a decade. Google engineers including Jeff Dean studied large-scale neural-network training with thousands of machines in the early 2010s. Their DistBelief system explored both asynchronous training and mechanisms for dealing with uneven worker progress. A few years later, Jianmin Chen, Xinghao Pan, Rajat Monga, Samy Bengio and Rafal Jozefowicz explicitly revisited synchronous SGD and showed that you could keep synchronous optimization while reducing the impact of the slowest workers.
The problem did not disappear as hardware got faster. The opposite happened: synchronization became more important because the jobs became much larger.
2. Why LLM training makes the problem worse
The key scaling equation is almost embarrassingly simple.
Suppose each worker takes:
T_i = 100 ms + noise_i
With 8 workers, perhaps the slowest one occasionally takes 110 ms.
With 4,096 workers, you have 4,096 opportunities for somebody to be slow.
You do not need every worker to become unreliable. You only need the maximum of a large set of random variables to drift upward.
That is the same statistical phenomenon that makes tail latency such a persistent distributed-systems problem.
Suppose, purely as a toy model, that each worker has a 1% chance of taking more than 120 ms on a particular step.
With 100 workers:
P(at least one slow worker)
= 1 - 0.99^100
~= 63.4%
With 4,096 workers:
1 - 0.99^4096
~= ~100%
So the question at scale is no longer:
"Does an individual GPU have occasional slow steps?"
The answer is yes.
The real question is:
"How much slow-step behavior reaches the critical path of the whole job?"
This is why ordinary percentile thinking can be misleading.
Suppose a worker's step-time distribution has:
p50 = 100 ms
p95 = 105 ms
p99 = 110 ms
Those numbers look excellent.
But if thousands of workers participate in every synchronized step, the job repeatedly samples the extreme tail.
The effective quantity is much closer to:
E[max(T_1, T_2, ..., T_N)]
As N grows, the expected maximum generally grows even if the distribution of each individual worker stays unchanged.
This produces a nasty scaling property:
Adding more workers can increase compute capacity while simultaneously increasing exposure to tail events.
That is one reason "we doubled the GPU count, so training should be twice as fast" eventually becomes a bad model of system behavior.
3. The math of wasted compute
Here is the back-of-the-envelope calculation that makes the problem tangible.
Suppose:
N = 4096 GPUs
normal step time = 100 ms
straggler step time = 120 ms
During a straggler step, the fast GPUs finish at 100 ms and wait until 120 ms.
The waiting time per fast GPU is:
20 ms
Across 4,095 fast GPUs:
4095 * 20 ms
= 81.9 GPU-seconds
of aggregate GPU time wasted on that single step.
The job only advanced 20 ms, but the cluster burned almost 82 GPU-seconds of waiting.
Another way to see it is utilization.
Ignoring communication overhead, the fast GPUs are doing useful computation for:
100 / 120 = 83.3%
of the elapsed time.
So roughly:
16.7%
of their time is idle.
Now imagine that this happens regularly.
If a job runs for 10 days on 4,096 GPUs, total allocated GPU time is:
4096 * 10 * 24
= 983,040 GPU-hours
If straggling contributes an effective 10% reduction in useful compute:
~98,304 GPU-hours
are effectively lost.
At an illustrative infrastructure cost of just $2 per GPU-hour:
98,304 * $2
= $196,608
At $5/GPU-hour:
= $491,520
Those are not exotic failure scenarios. They are just the economics of a large amount of idle accelerator time.
And there is a second-order cost.
If a 10-day training run becomes an 11-day run, the extra day may delay:
evaluation
checkpointing
fine-tuning
research iteration
deployment
the next experiment
For research teams, that opportunity cost can be more important than the raw GPU bill.
This is why stragglers belong partly in the domain of operations and economics, not just distributed systems.
4. In real LLM systems, the "slow GPU" is often not the GPU
This is one of the most useful lessons from recent measurements.
A common mental model is:
straggler = broken GPU
That is often wrong.
A 2025 OSDI study by researchers from NYU, ByteDance and Zhejiang University analyzed five months of production LLM-training traces from a ByteDance cluster. They examined 3,079 jobs using at least 128 GPUs, including jobs with thousands of GPUs.
Their main result was stark:
42.5% of jobs were at least 10% slower because of stragglers.
At the tail, some jobs wasted as much as 45% of their allocated resources.
Even more interesting was what they found when investigating root causes.
Hardware or software faults in individual servers were not a major explanation.
Instead, several mundane-looking sources mattered:
Pipeline imbalance
Suppose a pipeline has four stages:
stage 0: 8.0 ms
stage 1: 8.1 ms
stage 2: 11.0 ms
stage 3: 8.0 ms
Stage 2 is the slowest.
The other stages cannot simply run at their ideal rate forever. Eventually they wait for stage 2.
A three-millisecond imbalance in one stage can therefore propagate through a much larger system.
This is particularly relevant to pipeline parallelism, where the model itself is partitioned across GPUs.
Sequence-length imbalance
Transformer compute is highly dependent on sequence length.
Imagine two microbatches:
worker A: 2,000 tokens
worker B: 2,000 tokens
worker C: 2,000 tokens
worker D: 7,000 tokens
Worker D is not "broken."
It simply has more work.
But synchronous training does not care why the worker is slow. Everyone still waits.
This is an important systems principle:
Synchronous parallelism converts workload imbalance into global idle time.
Garbage collection
This one sounds almost absurd at LLM scale.
Thousands of GPUs are sitting there, each costing real money.
And a CPU-side garbage collector can pause one process long enough to become the slow participant in a synchronization cycle.
The ByteDance study found GC-induced pauses among the causes of observed straggling.
That is a useful reminder that accelerator performance is not determined solely by accelerator performance.
The critical path can cross:
GPU kernels
CPU runtime
memory management
network
collectives
scheduler behavior
data loading
pipeline scheduling
Any one of them can become the straggler.
5. The topology of modern LLM training makes propagation complicated
LLMs are rarely trained with "just data parallelism."
A modern large-model job commonly combines several forms of parallelism:
data parallelism
pipeline parallelism
tensor parallelism
possibly context / expert parallelism
That creates a hierarchy of synchronization dependencies.
A useful simplified picture is:
DATA PARALLEL
+---------+---------+
| |
pipeline pipeline
group A group B
| | | | | |
TP TP TP TP TP TP
Now imagine one tensor-parallel worker becomes slow.
It can delay a pipeline stage.
That delayed pipeline stage can delay its data-parallel replica.
That can delay gradient synchronization.
So a local 10 ms delay can propagate through multiple layers of the execution graph.
The 2021 Megatron-LM work by Deepak Narayanan, Mohammad Shoeybi and colleagues described exactly the underlying challenge: large-model training requires composing tensor, pipeline and data parallelism, while minimizing the waiting introduced by communication and synchronization.
At this scale, synchronization is not one barrier.
It is a network of barriers.
That distinction matters when debugging.
A trace that simply says:
step 18392 = slow
is not enough.
You want to know:
which worker?
which collective?
which parallelism group?
which microbatch?
which pipeline stage?
compute or communication?
persistent or transient?
The difference between those questions is the difference between fixing a root cause and restarting the job.
6. How engineers actually fight stragglers
There is no universal solution because every mitigation trades something else away.
A. Make the work more balanced
The first solution is also the most boring:
Do not create uneven work in the first place.
That means better:
pipeline partitioning
sequence-length bucketing
microbatch construction
GPU placement
load balancing
For example, if one pipeline stage contains substantially more expensive layers, moving layers between stages can reduce the deterministic component of straggling.
This is often better than inventing a complicated runtime workaround.
B. Use backup workers
One of the classic ideas is simple.
Suppose there are:
N = 100 workers
but instead of waiting for all 100, the system proceeds after receiving enough results from the fastest workers.
For example:
wait for 94
ignore the six slowest
Those six are effectively backup workers.
The attraction is obvious:
synchronous semantics
+
less waiting for the slowest workers
The catch is equally obvious.
You are now paying for workers whose results may not be used.
And that trade-off is particularly uncomfortable for modern LLM training because the synchronization happens frequently and the GPUs are expensive.
Chen and colleagues studied this exact idea in distributed synchronous SGD and showed that backup workers can significantly reduce the damage from stragglers while retaining the basic synchronous optimization behavior.
C. Drop or delay work
A more aggressive solution is to simply not wait for slow workers.
That improves system throughput but changes the optimization algorithm.
You are effectively saying:
the global model update does not need
every worker's contribution
This moves you toward asynchronous or partially asynchronous training.
The engineering advantage is that the machine is busier.
The algorithmic disadvantage is that your gradient updates are no longer the same as fully synchronous SGD.
For some workloads that trade is worthwhile.
For others, reproducibility, convergence behavior, optimizer dynamics, or model quality make it less attractive.
D. Detect stragglers as a first-class operational signal
The worst system is one where the operator sees:
GPU utilization: 68%
and has no idea why.
A useful monitoring system should surface something closer to:
job utilization: 68%
dominant cause: synchronization waiting
largest straggler:
global rank 1842
scope:
TP group 12
PP stage 7
DP replica 31
excess step time:
+17.4 ms
persistence:
82% of recent steps
probable cause:
sequence-length imbalance
This is operationally much more actionable.
The ByteDance study is notable for exactly this reason: the authors built trace-analysis tooling into a monitoring system called SMon and deployed parts of it to the training cluster's on-call workflow.
The lesson is broader than LLMs:
Distributed systems need observability at the level of the synchronization dependency, not just the individual machine.
7. The deeper lesson: synchronous scale changes what "fast" means
Engineers often reason about performance locally:
GPU A is 10% faster.
network B has 20% more bandwidth.
kernel C is 5% faster.
But synchronous distributed training is a global system.
The relevant performance quantity is not:
average worker throughput
but something closer to:
throughput
~ 1 / E[max(worker_time)]
with communication and scheduling layered on top.
That has an uncomfortable implication.
At small scale, optimization often means making the average case faster.
At very large scale, optimization increasingly means controlling the tail.
This is the same conceptual shift that happened in large distributed services: once a request fans out over enough machines, rare slow events stop being rare at the system level.
LLM training is essentially doing the same thing with numerical computation.
And that leads to a useful design rule:
When a computation synchronizes thousands of workers repeatedly, variance is a performance feature you have to engineer away.
Not because variance is philosophically bad, but because the synchronization barrier turns it directly into idle accelerator time.
For LLM infrastructure engineers, that suggests a practical priority order:
1. Balance the work.
2. Identify where synchronization is actually waiting.
3. Separate compute stragglers from communication stragglers.
4. Determine whether the cause is persistent or transient.
5. Only then choose between rebalancing, backup workers,
scheduling changes, or relaxing synchronization.
And perhaps the most important economic lesson is this:
A 4,096-GPU cluster does not really give you "4,096 GPUs of compute" when the critical path is controlled by a small subset of workers.
You are buying the entire cluster.
Your throughput is governed by the tail.
That is the straggler problem.
What has been the strangest straggler you've encountered in a distributed system — a bad machine, a network issue, skewed data, garbage collection, or something much more mundane?
Your team's attention is limited, and the deluge of AI-generated code is making it harder to keep production reliable and secure without slowing you down.
I'm building LiveReview, a blast-radius aware AI code review built for your business-critical systems.
Instead of presenting every diff with equal emphasis, LiveReview scores each change by blast radius — how far its impact reaches through your call graph — so you can focus attention where it actually matters.
Spend code review effort where business risk is highest — not spread evenly across every diff.
⭐ Star it on GitHub:
HexmosTech
/
LiveReview
Blast-Radius Aware AI Code Review for Business-Critical Systems
LiveReview: Blast-Radius Aware AI Code Review for Business-Critical Systems
LiveReview is an AI code reviewer that scores every hunk of a diff by blast radius: how far a change reaches through your call graph, how much persistent state it touches, and how well-tested it is. A 3-line change to a shared auth check can outrank a 300-line UI tweak. Your team's attention goes to the highest-risk code first, not spread evenly across every diff.
blast-radius-demo.mp4
LiveReview's Blast Radius & Review Priority scoring, live in the diff viewer.
Here's the goal:
- A 3-line fix in a function used by 40 other files, that also writes to a database, should score high.
- A 300-line UI change in one file, fully covered by…
Click below to try LiveReview with your codebase:





Top comments (2)
Framing this around the distribution of the sample maximum rather than worker-level percentiles gets the mathematical mechanics right. In synchronous distributed training with thousands of ranks, every step samples the extreme tail of the duration distribution. Even if individual step latency follows a well-behaved distribution with a tight interquartile range, the expected maximum of four thousand independent draws gets pushed far out into the Gumbel domain of attraction on every single forward-backward pass.
The economic consequence is that large clusters are structurally short variance. When an engineering team invests in squeezing another five percent peak FLOPs out of a custom kernel, that gain can be erased by minor jitter in host-to-device memory transfers or CPU runtime pauses on a single node. At frontier scale, variance compression across the fleet generates a higher return on capital than raw compute acceleration.
Nice walk from the restaurant intuition down to the math and the economics. "Variability is multiplied by synchronization" should be pinned above every distributed systems whiteboard.
The practical takeaway from our side of the fence: in production the sneaky stragglers were never dying hardware, it was variance — GC pauses and batch imbalance were invisible until we started measuring per-worker step times instead of aggregate. Averages looked healthy while the tail ate the run.
Did you consider covering async or partially-synchronous alternatives, or is the piece deliberately scoped to the sync case? Because once you accept T_step = max(...) + sync, the natural follow-up is exactly where you can afford to break the barrier.