← Plasmient Labs
Code Walk F · layer 03deep dive · distributed training

Scaling
one model across a thousand GPUs

Layer 03 of the Frontier Stack, gone deep. A frontier model doesn’t fit on one GPU — not the weights, not the activations, not the batch. Training it means splitting it four different ways at once across a cluster, and every split buys capacity by paying a communication tax. This walk is the four axes of parallelism, the collective traffic each one adds, and the pipeline bubble — the purest example of a GPU that’s pinned at 100% while doing nothing. The number that survives all of it is MFU, and it’s usually under half. Two interactive pieces.

  • the problemone model, thousands of GPUs
  • the four axesdata · tensor · pipeline · expert
  • the taxcollectives + the pipeline bubble
  • the numberMFU — model FLOPs utilization

00 Four ways to split a model

A frontier model overflows a single GPU in every dimension: the parameters don’t fit, theactivations for a full batch don’t fit, and even if they did, one GPU would take years. So the model is cut apart along four independent axes, usually all at once. Split the batch across replicas (data), split each layer’s matrices (tensor), split the stack of layers into stages (pipeline), or split an MoE’s experts (expert). Each axis solves a different “doesn’t fit”, and each one adds a different kind of network traffic. Click through them:

An 8-GPU slice of a cluster, relabelled for each parallelism mode. The tint groups GPUs that must talk to each other; the dashed box names the collective that traffic runs. Real training composes several of these at once — e.g. DP × TP × PP × EP — so a single 16k-GPU job is a 4-D mesh. Every mode’s cost line is the same shape: GPUs waiting on the network.

The pattern to carry forward: every axis trades compute capacity for communication. Data parallelism is cheapest to reason about but all-reduces the entire gradient each step; tensor parallelism has the heaviest traffic and must stay inside a node over NVLink; expert parallelism’s all-to-all depends on a router that’s never quite balanced. None of that traffic does any floating-point work — and all of it registers as a busy GPU.

Data parallelism is the one every run uses, and its entire cost is a single collective: theall-reduce that sums every replica’s gradient before the next step. It’s worth seeing what that collective actually is, because the name hides the mechanics. The standard ring all-reduce — what NCCL runs under the hood — does it in two halves, each N−1 rounds of send-to-your-neighbour. Firstscatter-reduce: every GPU keeps passing a chunk clockwise and adding it in, until each GPU owns one fully-summed chunk. Then all-gather: those finished chunks walk back around the ring until every GPU has all of them. It’s clever and bandwidth-optimal — and it is pure communication.

A ring of N GPUs syncing one gradient, split into N chunks. Scatter-reduce (N−1 steps) slides chunks clockwise and sums them until each GPU holds one fully-reduced chunk; all-gather (N−1 steps) copies those around until every GPU is identical (green). The whole time, the left gauge — nvidia-smi — is pinned near 100%: the device is streaming ~2×(N−1)/N of a gradient across the wire. The right gauge is the truth: zero tensor-core FLOPs. The cores idle, waiting on NCCL, and no dashboard shows it. Multiply that gap by every training step of every data-parallel run, and it’s the utilization gap this whole company is built to measure.
GPUs
nvidia-smi says
the device is busy the whole collective — DMA engines streaming bytes counts as busy
tensor-core work · useful FLOPs
all bytes on the wire, no math — cores idle waiting on NCCL

01 The purest bubble: pipeline parallelism

Pipeline parallelism gives the cleanest picture of the whole problem. Put the first layers on GPU 0, the next on GPU 1, and so on: an assembly line. Feed one batch in and only one GPU works at a time — the rest wait their turn. The fix is to chop the batch into microbatches so several are in flight at once, like parts moving down the line. But the line still has to fill at the start anddrain at the end, and during those the early and late stages sit idle. That idle wedge is thebubble. Change the microbatch count and the stage count and watch it breathe:

Rows are GPUs (stages), columns are time; each purple cell is a microbatch computing, each hatched cell is a GPU idling in the bubble. The efficiency gauge is just filled ÷ total = M / (M + S − 1). More microbatches (M) amortize the fill/drain and shrink the bubble; more stages (S) deepen the pipeline and grow it. The left gauge — nvidia-smi — barely notices.
microbatchesstages
nvidia-smi says
a stage with any resident kernel reads busy — even mid-bubble
pipeline efficiency · goodput
filled cells ÷ total = M / (M + S − 1)

This is the thesis with no room to hide. A GPU stalled in the bubble is doing literally zerofloating-point work, yet the process holds the device, so utilization reads high. The efficiency gauge — the honest one — falls straight out of the geometry: with few microbatches or many stages, a third or more of your cluster-time can be bubble. Real schedules (1F1B, interleaved / virtual pipelining) pack the forward and backward passes tighter to shrink it, but they never reach zero, and this widget only shows the forward half — the real picture doubles it.

02 The one number that survives: MFU

Stack it all up — all-reduce for data parallelism, mid-layer collectives for tensor parallelism, the pipeline bubble, all-to-all for experts, plus kernels that were already memory-bound before any of this — and you get the real efficiency of a training run. The field has a name for the honest version:MFU, model FLOPs utilization — the actual useful FLOPs done, divided by what the hardware could do at peak. It’s the same idea Karpathy’s estimate_mfu()computes for a single GPU, lifted to the whole cluster.

  what the dashboard shows            what MFU shows
  ┌──────────────────────────┐        ┌──────────────────────────┐
  │ 16,384 GPUs   ~100% busy  │        │ useful FLOPs / peak       │
  │ ████████████████████████  │   vs   │ ██████████░░░░░░░░░░░░░░░  │
  └──────────────────────────┘        └──────────────────────────┘
     one green number                    PaLM: 46% · GPT-class: 30–50%
     hides the collectives,              the collectives, the bubble,
     the bubble, the stalls              and the memory walls, counted

Published frontier runs report MFU in the 30–50% range — PaLM’s own paper states 46.2% across 6144 chips, and it was celebrated as excellent. Read that plainly: at the frontier of engineering, half the most expensive compute humanity has ever assembled is going into the tax, and the dashboard everyone watches shows 100%.

03 Why an observability company cares

the plasmient thread

This layer is the thesis at its most expensive. A single training run can cost tens of millions of dollars in compute, and the gap between “busy” and “useful” on it is routinely a factor of two — a factor of two on the biggest line item in the industry. The bubble is idle GPUs. The all-reduce is idle tensor cores waiting on NCCL. The hot expert is a bottleneck while its peers coast. Every one of those reads as 100% utilization, and none of them is visible without joining the kernel timeline to the collective timeline to the cluster topology.

That join is exactly what Plasmient is for. The single ops that make up this cluster live on the roofline; the model being trained islayer 02; the weights it produces get served inlayer 05. Scaling is where the gap costs the most per hour — which makes it where measuring the gap is worth the most. Back to the Frontier Stack for the rest of the tree.

Full references — go to the source

Megatron-LM and the Narayanan 2021 paper are the two to read first — they define how TP, PP and DP compose. GPipe names the bubble; ZeRO and DeepSpeed-MoE handle memory and experts. PaLM is where you see the honest MFU number in print.