✦ We begin with the original dense TPU schemes — data parallelism, FSDP, tensor parallelism, their mixed form, and pipelining — then splice in the GPU fabric model and expert parallelism for MoEs. For each, we ask when communication becomes the bottleneck. (This summary is the adaptation’s; the chapter’s own dek described its four dense schemes.)
This page is a working model, not a description of one. Every green number can be dragged left or right, or double-clicked to type an exact value. Every blue number is computed live from the green ones — try it here: drag the batch and watch the per-chip batch follow (hover any blue number for its formula). They share one model-and-hardware state, so a change made anywhere propagates everywhere. Parallelism degrees remain scheme-local: the dense mixed group uses N = DP·TP, while EP and PP are modeled in their own sections; composite worked examples state their full product explicitly. And scrub without fear — restores every scrubbed number to its default while keeping your model, hardware, and spec/measured picks (it's the same button as in the top bar, which lights up orange whenever a scrub has strayed), any single number reverts on its own when you double-click it and commit it blank, and the browser's back button walks through your earlier configurations.
Whose words are you reading? Source passages come from the original TPU and GPU chapters (© 2022 Maruan Al-Shedivat, © 2025 Google LLC, MIT license); AI-authored departures are explicitly labeled, with these conventions: wherever the chapter printed a fixed number, this page computes it live (these in-place swaps aren't individually marked); the interactive figures and their captions replace the original static figures; ✦ margin notes and passages explicitly labeled as adaptation are AI-written editorial voice — the initial edition was built by Fable (Anthropic) and this adversarial review and its corrections were performed by OpenAI Codex — including instructions, asides, and the new roofline primer; the expert parallelism and GPU network sections instead mash up Chapter 12 source passages, with their AI-written connective and adaptation prose labeled by the same convention; the chapter's single-letter mesh-axis names are rendered as named parallelism degrees throughout — its X is DP, its Y is TP, the pipelining section's Z is PP, and chapter 12's expert axis Z is EP (a global substitution; each is its own scrubbable variable, adjusted in the text where its section uses it); under a GPU preset the hardware vocabulary follows suit — TPU→GPU, ICI→NVLink, DCN→InfiniBand, pod→node, MXU→tensor core — so the article reads as one consistent machine, and any TPU preset restores the chapter's exact words (sentences that deliberately compare the two never swap); content woven into the chapter's text by this edition carries a dotted underline like that (and splice edits beside it that standard quotation practice would allow — a bracket or an ellipsis — go unmarked); where a sentence had to be altered to host a live element, a Δ margin note quotes the original and states the change; and where this edition's additions make a chapter statement inaccurate as written, an italic (Ed: …) interjection corrects it in place.
The goal of “model scaling” is to be able to increase the number of chips used for training or inference while achieving a proportional, linear increase in throughput (we call this strong scaling). While performance on a single chip depends on the trade-off between memory bandwidth and FLOPs, performance at the cluster level depends on hiding inter-chip communication by overlapping it with useful FLOPs. This is non-trivial, because increasing the number of chips increases the communication load while reducing the amount of per-device computation we can use to hide it. As we saw in Section 3, sharded matrix multiplications often require expensive AllGathers or ReduceScatters that can block the TPUs from doing useful work. The goal of this section is to find out when these become too expensive.
In this section, we'll discuss five common parallelism schemes: (pure) data parallelism, fully-sharded data parallelism (FSDP / ZeRO sharding), tensor parallelism (also known as model parallelism), expert parallelism (for Mixture-of-Experts models), and (briefly) pipeline parallelism. For each, we'll show what communication cost we incur and at what point that cost starts to bottleneck our compute cost.We'll focus on communication bounds — since while memory capacity constraints are important, they typically do not bound us when using rematerialization (activation checkpointing) and a very large number of chips during pre-training. (Ed: This edition is expanded to discuss expert parallelism, unlike the original.) For this section, you can focus solely on inter-chip communication costs, since as long as we have a large enough single-chip batch size, the transfer of data from HBM to MXU is already overlapped with computation.
We'll use the following notation to simplify calculations throughout this section.
| Notation | Meaning (model parameters) | Live value |
|---|---|---|
| D | dmodel (the hidden dimension/residual stream dim) | |
| F | dff (the feed-forward dimension)adaptation F convention (everywhere): the width of one expert (= dff when dense); math runs through k·F, weights hold E·F, and the chapter's equations are the E = k = 1 case (Chapter 12's resolution). One honest limitation: models that mix dense and MoE blocks have two genuinely different F's — DeepSeek-V3 runs its first three layers dense at a much wider width — and this page approximates such models as uniformly MoE. Hover any F for the live widths. | |
| B | Batch dimension (number of tokens in the batch; total, not per-device) | |
| T | Sequence length | — |
| L | Number of layers in the model |
| Notation | Meaning (hardware characteristic) | Live value |
|---|---|---|
| C | FLOPS/s per chip | |
| W | Network bandwidth (bidirectional per TPU mesh axisone-way GPU or node egress, often subscripted as e.g. Wici or Wdcn) | ici · dcn |
| DP | Number of chips along the data-parallel mesh axis (the chapter's X) | |
| TP | Number of chips along an alternate, tensor-parallel mesh axis (the chapter's Y) | |
| Z | Number of chips along a third mesh axis, labeled Z | — |
| PP | Pipeline stages (the pipelining section's Z) | |
| EP | Expert-parallel degree (chapter 12's Z; see the expert-parallelism section) |
The chapter's examples are dense LLaMA-era models; the frontier has since gone
Mixture-of-Experts.Shapes
from each model's published config.json on Hugging Face; parameter
totals from its safetensors metadata. Retrieved August 2026.
E and k count shared experts, so k·F is the activated
width for the architectures represented by the live presets; column headers
explain each field. The dense models from the
top-bar dropdown lead the table for contrast, and whichever model is loaded
shows its row in live green — scrub it right here.
| Model | params | D | F | act. k·F | L | E | k |
|---|---|---|---|---|---|---|---|
| (chapter default) | 70.6B | 8,192 | 28,672 | 28,672 | 80 | 1 | 1 |
| 13.0B | 5,120 | 13,824 | 13,824 | 40 | 1 | 1 | |
| 8.54B | 3,072 | 24,576 | 24,576 | 28 | 1 | 1 | |
| Counting example: 256 routed + 1 shared expert → E 257; top-8 + shared → k 9. Its first three layers are actually dense (see the F-convention note above). | 685B | 7,168 | 2,048 | 18,432 | 61 | 257 | 8+1 |
| Kimi K3 (reference only)K3 is not a live preset because its routed experts operate after a projection from residual D = 7,168 into a 3,584-wide latent space. Its routed-expert intermediate width is F = 3,072. The page's single D×F expert model cannot represent both dimensions faithfully. | 2.78T | 7,168 | 3,072 | 55,296 | 93 | 896+2 | 16+2 |
| 753B | 6,144 | 2,048 | 18,432 | 78 | 257 | 8+1 | |
| 1.60T | 7,168 | 3,072 | 21,504 | 61 | 385 | 6+1 | |
| 2.45T | 8,192 | 2,048 | 22,528 | 92 | 513 | 10+1 | |
| 952B | 6,144 | 3,072 | 24,576 | 66 | 258 | 6+2 | |
| 427B | 6,144 | 3,072 | 15,360 | 60 | 129 | 4+1 |
Click a supported model to load its shape (D, F, L, E, k) into the page's shared state (the top bar follows); click a column header to sort. F = per-expert width; act. k·F = activated width per token; E / k = total / activated experts, counting shared. Across the supported live MoE presets, per-expert F is just 2,048 or 3,072, and activated width k·F clusters between 15k and 25k even as total parameter counts span hundreds of billions to trillions. Since the tensor-parallelism bound later in this chapter scales with the activated width k·F, that clustering is why the TP limits look so similar across the supported frontier presets. K3 is retained as a reference row, but its latent-MoE shape is deliberately not loaded into these formulas.
Every hardware number this page computes with, spec and sustained, with its
source.Full
citations live in SOURCES.md alongside this
page — every value traces to a vendor spec sheet, a published measurement, or the
book's own benchmarks; retrieved 2026-08-17; click any cell to pin its citation
and follow the source link. Methodology for the synthesized numbers: NVIDIA
datasheets headline sparsity FLOP/s, halved here to dense; "bidirectional"
bandwidths are halved to per-direction; per-GPU scale-out is the node's NIC total
divided by its GPUs. ≈ marks factors that are estimates (stated basis, no
direct public measurement — e.g. Blackwell collectives inherit H100's measured
NCCL ratio until independent nccl-tests exist) rather than measured.
Hover any cell for its citation. The sustained and achieved
columns are the measured fractions of spec: flip the top bar's
spec / measured control and every equation on the page derates
by them (compute × sustained, bandwidth × achieved — the loaded hardware's row
shows them as live green scrubs). Wall-clock estimates that already assume an
MFU keep using spec peak, so nothing double-counts. Notice the punchline the
citations force: TPUs sustain far closer to their paper numbers than the
power-throttled NVIDIA parts.
| Hardware | C (dense bf16) | × sust. | W link | × achv. | W scale-out | HBM |
|---|---|---|---|---|---|---|
| 459 TF | ≈0.72 | 180 GB/s | ≈0.95 | 6.25 GB/s | 96 GB | |
| 197 TF | ≈0.67 | 90 GB/s | ≈0.95 | 3.13 GB/s | 16 GB | |
| 989 TF | 0.73 | 450 GB/s | 0.82 | 50 GB/s | 80 GB | |
| 2.25 PF | 0.69 | 900 GB/s | ≈0.82 | 50 GB/s | 180 GB | |
| 2.5 PF | ≈0.70 | 900 GB/s | ≈0.82 | ≈50 GB/s | 186 GB | |
| 2.5 PF | ≈0.70 | 900 GB/s | ≈0.82 | 100 GB/s | 288 GB | |
| 989 TF | ≈0.73 | 200 GB/s | 0.80 | 50 GB/s | 80 GB |
For simplicity's sake, we'll approximate a Transformer as a stack of MLP blocks — attention is a comparatively small fraction of the FLOPs for larger models as we saw in Section 4. We will also ignore the gating matmul, leaving us with the following simple structure for each layer:adaptation With this simplification each layer holds 2·D·E·F weights (E = 1 for a dense model, so simply 2·D·F), and the whole stack has 2·D·E·F·L = parameters at the current settings — the “P” in this page's communication arithmetic. Memory questions are different: a real checkpoint holds the gated MLP's third matrix and the attention stack too, so the memory meters price weights at Pw ≈ 3·D·E·F·L + 2.5·D²·L = , which tracks the model table's published totals to within a few percent (vocab embeddings and MHA-era attention excepted).
Forward pass: need to compute Loss[B]
Backward pass: need to compute dWout[F, D], dWin[D, F]
We provide this for comparison to the algorithms with communication added.
Here are the 4 parallelism schemes we will discuss. Each scheme can be thought of as uniquely defined by a sharding for In, Win, Wout, and Out in the above diagram.adaptation A quick reminder of the book's notation: a subscript on an array dimension names the mesh axis it is split over — In[BDP, D] means the batch dimension is carved into DP pieces, one per chip along axis DP — while a subscript on a · names the dimension being contracted. The explorer below the four descriptions lets you click through the schemes: for each one it draws all four arrays with their shards colored by chip, the per-chip local shapes at the live DP and TP, and the collectives the scheme pays for in the forward and backward pass.
1. Data parallelism: activations sharded along batch, parameters and optimizer state are replicated on each device. Communication only occurs during the backwards pass.
2. Fully-sharded data parallelism (FSDP or ZeRO-3): activations sharded along batch (like pure data parallelism), parameters sharded along same mesh axis and AllGathered just-in-time before use in forward pass. Optimizer state also sharded along batch. Reduces duplicated memory.
3. Tensor parallelism (also called Megatron sharding or model parallelism): activations sharded along D (dmodel), parameters sharded along F (dff). AllGather and ReduceScatter activations before and after each block. Compatible with FSDP.
4. Pipeline parallelism: weights sharded along the layer dimension, activations microbatched and rolled along the layer dimension. Communication between pipeline stages is minimal (just moving activations over a single hop). To abuse notation:adaptation Notice what all four schemes have in common: every one runs the same matmuls — the FLOPs never change, only where the arrays live and which collectives must run between the multiplies. So for each scheme the question is always whether those collectives can hide behind the matmuls. Before the chapter dissects the schemes one by one, this adaptation inserts a short primer — First, Feel the Roofline — building the one picture that answers that question for all four.
✦ This entire section is an addition of this adaptation — the chapter's ideas, our framing. The original text resumes at Data Parallelism.
The core DP/FSDP/TP rooflines in this chapter are one picture wearing a few costumes. Mixed sharding combines those clocks; expert and pipeline parallelism add topology- and scheduling-specific activation traffic. Before we meet them, let's get the core picture into your fingers.
When a chip works on one layer of our Transformer, two clocks run at the same time:
The compute clock: the MXU has to chew through this layer's share of FLOPs. With B = tokens split over DP = chips, that's 4 · B · D · k·FDP · C = per layer (k·F because a token only multiplies through its k activated experts).
The network clock: whatever bytes this scheme moves have to squeeze through the interconnect at Wici = . Crucially, these two clocks can overlap when the implementation schedules the collective successfully: the network carries bytes while the MXU multiplies. Under that explicit assumption, a layer costs the max, not the sum. Communication that fits under the compute clock is hidden; communication that pokes out past it leaves silicon idle.
Try: with the toggle on weights, drag B = down and watch only the compute bar shrink — the network bar doesn't hear the batch size at all, so at some point the comms must poke out. Then flip to activations and drag again: now both bars move together, and no amount of batch will save you.
That toggle captures the core DP/FSDP/TP distinction: what travels?
Now the roofline itself. In Part 1 of the original book, a single chip was compute-bound only when its arithmetic intensity — FLOPs per byte touched — beat the ratio of FLOP speed to memory bandwidth. The identical logic applies here, one level up, with the interconnect playing the role of memory. For weight-moving schemes, your FLOPs scale with B/DP and your bytes don't, so:
Try: drag the dot up the slope and feel where the ridge is. Then make the interconnect worse — drag Wici = down — and watch the ridge slide right: a slower network demands a bigger per-chip batch before it can hide. Faster chips (drag C = up) do the same thing, which is why each hardware generation makes parallelism harder, not easier.
Syntax:
When your model fits on a single chip with even a tiny batch size (>240 tokens, so as to be compute-bound), you should always use simple data parallelism. Pure data parallelism splits our activations across any number of TPUs so long as the number of TPUs is smaller than our batch size. The forward pass involves no communication, but at the end of every step, each TPU performs an AllReduce on its local gradients to synchronize them before updating the parameters.
Pure Data Parallelism Algorithm:
Forward pass: need to compute Loss[BDP]
Backward pass: need to compute dWout[F, D], dWin[D, F]adaptation The {UDP} annotation below marks a result that is unreduced over the DP axis: each chip holds a partial sum from its own slice of the batch.
We ignore the details of the loss function and abbreviate Tmp = Win · In. Note that, although our final loss is the average AllReduce(Loss[BDP]), we only need to compute the AllReduce on the backward pass when averaging weight gradients.
Note that the forward pass has no communication — it's all in the backward pass! The backward pass also has the great property that the AllReduces aren't in the “critical path”, meaning that each AllReduce can be performed whenever it's convenient and doesn't block you from performing subsequent operations. The overall communication cost can still bottleneck us if it exceeds our total compute cost, but it is much more forgiving from an implementation standpoint. We'll see that model/tensor parallelism doesn't have this property.adaptation In the primer's terms: because the AllReduce can be launched whenever convenient, the only question left is whether the total comms fits under the total compute — the roofline question, answered just below. Tensor parallelism's collectives, by contrast, block the very next matmul.
Why do this? Pure data parallelism reduces activation memory pressure by splitting our activations over the batch dimension, allowing us to almost arbitrarily increase batch size as long as we have more chips to split the batch dimension over. Especially during training when our activations often dominate our memory usage, this is very helpful.
Why not do this? Pure data parallelism does nothing to
reduce memory pressure from model parameters or optimizer states, which means
pure data parallelism is rarely useful for interesting models at scale where
our parameters + optimizer state don't fit in a single TPU. To give a sense of
scale, if we train with parameters in bf16 and optimizer state in fp32 with
AdamAdam
stores parameters, first order and second order accumulators. Since the params
are in bfloat16 and optimizer state is in float32, this gives us
2 + 8 = 10 bytes per parameters., the largest model
we can fit has TPU memory / 10 parameters, so e.g. on
a TPUv5p chip with
of HBM and pure data parallelism this is about
parameters.
To make this useful for real models during training, we'll need to at least partly shard the model parameters or optimizer.
When do we become bottlenecked by communication? As we can see above, we have two AllReduces per layer, each of size 2DF (for bf16 weights). When does data parallelism make us communication bound?adaptation The network here carries weight gradients — 2 · D · E · F = per matrix (all E experts' gradients, not just the k a token used) — whose size doesn't care about the batch. This is the weight-moving roofline from the primer: a batch-blind comms cost that a big enough per-chip batch can always hide.
As in the table above, let C = per-chip FLOPs, Wici = bidirectional per-axis ICI bandwidthone-way per-GPU NVLink egress, bandwidth, and DP = number of shards across which the batch is partitionedWe assume this partitioning is done over an ICI mesh, so the relevant network bandwidth is Wici.. Let's calculate the time required to perform the relevant matmuls, Tmath, and the required communication time Tcomms. Since this parallelism scheme requires no communication in the forward pass, we only need to calculate these quantities for the backwards pass.
Communication time: From a previous section we know that the time required to perform an AllReduce in a 1D mesh depends only on the total bytes of the array being AllReduced and the ICI bandwidth Wici; specifically the AllReduce time is 2 · total bytes / Wici. Since we need to AllReduce for both Win and Wout, we have 2 AllReduces per layer. Each AllReduce is for a weight matrix, i.e. an array of DF parameters, or 2DF bytes. Putting this all together, the total time for the AllReduce in a single layer is
✦ Adaptation: This DP collective spans more than one NVLink domain, so the live clock uses scale-out-limited bandwidth , not the faster local NVLink egress.
Matmul time: Each layer comprises two matmuls in the forward pass, or four matmuls in the backwards pass, each of which requires 2(B/DP)DF FLOPs. Thus, for a single layer in the backward pass, we have
Since we overlap, the total time per layer is the max of these two quantities:
We become compute-bound when Tmath/Tcomms > 1, or when
The upshot is that, to remain compute-bound with data parallelism, we need the per-device batch size B/DP to exceed the ICI operational intensity, C/Wici. This is ultimately a consequence of the fact that the computation time scales with the per-device batch size, while the communication time is independent of this quantity (since we are transferring model weights). Note the resemblance of the B/DP > C/Wici condition to the single-device compute-bound rule B > 240; in that case as well, the rule came from the fact that computation time scaled with batch size while data-transfer size was (in the B ≪ F, D regime) independent of batch size.adaptation Try: drag DP = up and watch the compute bar in the meter below shrink toward the frozen comms bar — every doubling of chips halves Tmath and does nothing to Tcomms. The verdict flips the moment the per-chip batch dips below .adaptation With a Mixture-of-Experts model loaded (E > 1 — click e.g. DeepSeek-V3 in the intro's frontier-models table), a penalty paragraph from Chapter 12 appears just below the meter: the gradients move all E experts while the FLOPs touch only the activated width, which inflates this per-chip floor by E/k = .
For a Mixture of Experts (MoE) model, where we have E experts and k experts per token, this increases to
which inflates the per-GPU token batch size by a factor of E/k, i.e.
For example, the new OpenAI OSS model with k=4 and E=128, this increases to 32 · 2475 = 79,200 across nodes, a kind of ridiculously high number.adaptation Kept as the chapter's static example (its 2475 is the H100 cross-node ridge from the GPU section). At your current hardware and model, the same computation reads (E/k) · C/Wcollective = tokens per chip. Expert parallelism — sharding the experts themselves, so gradients stop crossing the whole DP axis — is the standard escape; it gets its own section below.
Let's put in some real numbers to get a sense of scale. For TPUv5p,
C =
and W =
for 1D data parallelism over ICI, so our batch size per chip must be at
least to avoid
being
communication-bound.adaptation
The famous 2,550 — the same constant the primer
promised would keep reappearing. It's live here: change the hardware in the
machine bar and this floor moves with it. Since we can do data
parallelism over multiple axes, if we dedicate all three axes of a TPUv5p pod
to pure data parallelism, we 3x our bandwidth
Wici and can scale down to only
BS= per TPU or
tokens per batch per pod (of
chips)!
This tells us that it's fairly hard to become bottlenecked by pure data
parallelism!
Syntax:
Fully-sharded data parallelism (often called FSDP or ZeRO-sharding) splits the model optimizer states and weights across the data parallel shards and efficiently gathers and scatters them as needed. Compared to pure data parallelism, FSDP drastically reduces per-device memory usage and saves on backward pass FLOPs, with very minimal overhead.
You'll remember (from Section 3) that an AllReduce can be decomposed into an AllGather and a ReduceScatter. This means that, instead of doing the full gradient AllReduce for standard data parallelism, we can shard the weights and optimizer states across chips, AllGather them at each layer during the forward pass and ReduceScatter across the weights during the backward pass at no extra cost.
Fully-Sharded Data Parallelism (FSDP):
Forward pass: need to compute Loss[BDP]
Backward pass: need to compute dWout[F, DDP], dWin[DDP, F]
This is also called "ZeRO Sharding", from "Zero Redundancy Optimizer" since we don't perform any unnecessary compute or store any unnecessary state. ZeRO-{1,2,3} are used to refer to sharding the optimizer states, gradients, and weights in this way, respectively. Since all have the same communication costTechnically, FSDP adds communication in the forward pass that pure DP doesn't have, but this is in the same proportion as the backward pass so it should have no effect on the comms roofline. The key here is that ZeRO-3 turns a backward-pass AllReduce into an AllGather and a ReduceScatter, which have the same total comms volume., we can basically always do ZeRO-3 sharding, which shards the parameters, gradients, and optimizer states across a set of devices.
Why would we do this? Standard data parallelism involves a lot of duplicated work. Each TPU AllReduces the full gradient, then updates the full optimizer state (identical work on all TPUs), then updates the parameters (again, fully duplicated). For ZeRO sharding (sharding the gradients/optimizer state), instead of an AllReduce, you can ReduceScatter the gradients, update only your shard of the optimizer state, update a shard of the parameters, then AllGather the parameters as needed for your forward pass.adaptation Try: in the memory meter below, drag DP = down toward 1 and watch the meter fill up and overflow — at DP = 1 you're just pure DP on one chip's memory budget. Every doubling of DP halves the parameter and optimizer stripes.
When do we become bottlenecked by communication? Our relative FLOPs and comms costs are exactly the same as pure data parallelism, since each AllReduce in the backward pass has become an AllGather + ReduceScatter. Recall that an AllReduce is implemented as an AllGather and a ReduceScatter, each with half the cost. Here we model the forward pass since it has the same FLOPs-to-comms ratio as the backward pass:adaptation The chapter writes these equations for one mesh axis. The live line beneath them (and every meter on this page) spreads the collective over MDP = mesh axes — the MDP the chapter's takeaway below refers to — so Tcomms is divided by MDP.
✦ Adaptation: This FSDP collective crosses NVLink domains, so Wcollective is the scale-out-limited .
Therefore, as with pure data-parallelism, we are compute bound when B / DP > C / Wcollective, i.e. when the per-device batch size B/DP exceeds the collective fabric's operational intensity C/Wcollective ( / = right now). This is great for us, because it means if our per-device batch size is big enough to be compute-bound for pure data-parallelism, we can — without worrying about leaving the compute-bound regime — simply upgrade to FSDP, saving ourselves a massive amount of parameter and optimizer state memory! Though we did have to add communication to the forward pass, this cost is immaterial since it just overlaps with forward-pass FLOPs.
For example, borrowing only DeepSeek-V2's reported batch size as a dense-model thought experiment (this calculation does not model its expert parallelism), take a batch size of ~40M tokens.adaptation This qualifier is added because the source imports DeepSeek-V2's batch into a dense FSDP calculation; it does not model that MoE's expert parallelism. This would allow us to scale to roughly chips, or around TPUv5 pods, before we hit a bandwidth limit.adaptation Load the DeepSeek scenario with the button below, then make the batch your own: the mini-calculator that follows is an addition of this edition. With B = tokens, FSDP scales to DPmax chips before hitting the bandwidth limit.
For LLaMA-3 70B, which was trained for approximately (15e12 · 70e9 · 6) FLOPs, we could split a batch of tokens over roughly B / (α / 3) = chips (roughly pods of chips), each with FLOPs running at peak FLOPs utilization (often called MFU), and train it in approximately . Not bad! But let's explore how we can do better.adaptation The chapter's numbers (16M tokens, 18,823 chips, 17 days) are one point of this live sentence — the recipe button below restores them. Then drag B and watch chips and wall-clock trade off: a bigger batch rides the same ridge on more chips and finishes sooner, which is exactly why the labs fight for every doubling of critical batch size. The equation below, also an addition, shows the wall-clock arithmetic.
Syntax:
(we use TP to eventually combine with FSDP)
In a fully-sharded data-parallel AllReduce we move the weights across chips. We can also shard the feedforward dimension of the model and move the activations during the layer — this is called “1D model parallelism” or Megatron sharding (Shoeybi et al. 2019). This can unlock a smaller efficient batch size per pod. The figure below shows an example of a single matrix sharded in this way:
As noted, In[B, DTP] ·D Win[D, FTP] ·F Wout[FTP, D] → Out[B, DTP] means we have to gather our activations before the first matmul. This is cheaper than ZeRO sharding when the activations are smaller than the weights.adaptation Compare the two freights live, per layer in bf16: gathering activations moves 2 · B · D = , gathering weights moves 2 · D · E · F = — right now the lighter freight is . Try: drag B = down toward the per-group batches a mixed scheme would see (a few thousand tokens) and watch the verdict flip in the activations’ favor. This is typically true only with some amount of ZeRO sharding added (which reduces the size of the gather). This is one of the reasons we tend to mix ZeRO sharding and tensor parallelism.
Tensor Parallelism:adaptation Watch the phrase on critical path. With pure data parallelism the AllReduce happened after the loss was already computed, so the network could grind away while the chips moved on. Here the matmuls cannot start until the gathers finish — these collectives sit squarely in the layer’s serial path.
Forward pass: need to compute Loss[B]
Backward pass: need to compute dWout[FTP, D], dWin[D, FTP]
One nice thing about tensor parallelism is that it interacts nicely with the two matrices in our Transformer forward pass. Naively, we would do an AllReduce after each of the two matrices. But here we first do In[B, DTP] · Win[D, FTP] → Tmp[B, FTP] and then Tmp[B, FTP] · Wout[FTP, D] → Out[B, DTP]. This means we AllGather In at the beginning, and ReduceScatter Out at the end, rather than doing an AllReduce.adaptation And since an AllReduce is itself an AllGather plus a ReduceScatter, one AllGather in and one ReduceScatter out is half the bytes of the naive two-AllReduce plan.
How costly is this? Let's only model the forward pass - the backwards pass is just the transpose of each operation here. In 1D tensor parallelism we AllGather the activations before the first matmul, and ReduceScatter them after the second, sending two bytes at a time (bf16). Let's figure out when we're bottlenecked by communication.
Noting that we want compute cost to be greater than comms cost, we get:adaptation Notice that B · D appears in both clocks, so the batch cancels out of the ratio. This is the flat orange line from the primer: tensor parallelism’s compute-to-comms ratio is pinned at k · F / (TP · αTP) = no matter the batch — a weight-moving scheme can hide its comms behind more tokens per chip, but no batch size can raise this bar.
✦ Adaptation: This TP collective spans NVLink domains. The live clock therefore uses scale-out-limited bandwidth rather than the local .
Thus for instance, for TPUv5p, C/Wici = in bf16, so we can only do tensor parallelism up to TP < F / . When we have multiple ICI axes, our Tcomms is reduced by a factor of MTP, so we get TP < MTP · F / .
Note that this doesn't depend on the precision of the computation, since e.g. for int8, on TPUv5p, Cint8/Wici is instead of but the comms volume is also halved, so the two factors of two cancel.
Let's think about some examples:adaptation The buttons below load each model's real shape into the page's state — every number, meter, and verdict recomputes when you click one. Try: with a model loaded, set TP = to 8, then 16, then 32, and watch the verdicts. Or hold the model fixed and scrub C = : faster chips shrink TPmax = on the fabric carrying the current TP collective, which is why each hardware generation makes tensor parallelism a little harder to hide.
Syntax:
The nice thing about FSDP and tensor parallelism is that they can be combined. By sharding Win and Wout along both axes we both save memory and compute. Because we shard B along DP, we reduce the size of the model-parallel AllGathers, and because we shard F along TP, we reduce the communication overhead of FSDP. This means a combination of the two can get us to an even lower effective batch size than we saw above.
Forward pass: need to compute Loss[B]adaptation Count what actually sits on the critical path: on the TP axis, one AllGather in (step 1) and one ReduceScatter out (step 6) of activation bytes; the weight gathers on the DP axis (steps 2 and 4) can be prefetched. Those two TP-axis activation hops and two DP-axis weight hops are exactly the 2 · 2 factors in TTP comms and TFSDP comms below.
Backward pass: need to compute dWout[FTP, DDP], dWin[DDP, FTP]
What's the right combination of FSDP and TP? A simple but key maxim is that FSDP moves weights and tensor parallelism moves activations. That means as our batch size shrinks (especially as we do more data parallelism), tensor parallelism becomes cheaper because our activations per-shard are smaller.adaptation This maxim is the primer's weights-move vs activations-move toggle made load-bearing: per layer in bf16, FSDP's freight is 2 · D · E · F/TP = of weights while TP's is 2 · B · D/DP = of activations — each scheme shrinks the other's bill.
Thus by combining both we can push our minimum batch size per replica down even more. We can calculate the optimal amount of FSDP and TP in the same way as above:
TPU closed form. Let DP be the number of chips dedicated to FSDP and TP be the number of chips dedicated to tensor parallelism. Let N be the total number of chips in our slice with N = DPTP. Let MDP and MTP be the number of mesh axes over which we do FSDP and TP respectively (these should roughly sum to 3). We'll purely model the forward pass since it has the most communication per FLOP. Then adding up the comms in the algorithm above, we have
And likewise our total FLOPs time is
✦ Adaptation — GPU topology correction: the TPU equations immediately above are not valid GPU substitutions. An outer FSDP reduction does not become TP times faster while TP remains inside one NVLink domain; the scale-out link still carries the reduction. The live clocks, meter, and explorers use Chapter 12's max(Tdomain, Tscale-out) model. In the equations below, bytes = 4·D·E·F and G is the selected NVLink-domain size. The closed-form optimum below is therefore shown only on TPU; on GPU the explorer finds the topology-aware minimum directly.
To simplify the analysis, we make two assumptions: first, we allow DP and TP to take on non-integer values (as long as they are positive and satisfy DPTP = N); second, we assume that we can fully overlap comms on the DP and TP axis with each other. Under the second assumption, the total comms time is
Before we ask under what conditions we'll be compute-bound, let's find the optimal values for DP and TP to minimize our total communication. Since our FLOPs is independent of DP and TP, the optimal settings are those that simply minimize comms. To do this, let's write Tcomms above in terms of DP and N (which is held fixed, as it's the number of chips in our system) rather than DP and TP:
Because TFSDP comms is monotonically increasing in DP, and TTP comms is monotonically decreasing in DP, the maximum must be minimized when TFSDP comms = TTP comms, which occurs when
This is super useful! This tells us, for a given B, F, and N, what amount of FSDP is optimal. Let's get a sense of scale. Plugging in realistic values, namely N = 64 (corresponding to a 4x4x4 array of chips), B = 48,000, F = 32768, gives roughly DP ≈ . So we would choose DP to be 16 and TP to be 4, close to our calculated optimum.adaptation The chapter rounds this to ≈13.9; the pinned live value here is √(48,000 · 2 · 64 / 32,768) exactly. And at whatever is loaded right now, DPopt = . Press the first button below to load the chapter's exact scenario into the whole page.
Now let's return to the question we've been asking of all our parallelism strategies: under what conditions will we be compute-bound? Since we can overlap FLOPs and comms, we are compute-bound whenadaptation Same question as the primer's: does the slower of the two comms clocks fit under the compute clock?
By letting α ≡ C / Wici, the ICI arithmetic intensity, we can simplify:
Since we calculated DPopt to make the LHS maximum equal, we can just plug it into either side (noting that TPopt = N/DPopt), i.e.
Further simplifying, we find that
where the left-hand-side is proportional to the communication time and the right-hand-side is proportional to the computation time. Note that while the computation time scales linearly with the batch size (as it does regardless of parallelism), the communication time scales as the square root of the batch size. The ratio of the computation to communication time thus also scales as the square root of the batch size:
To ensure that this ratio is greater than one so we are compute bound, we require
To get approximate numbers, again plug in F = 32,768, α = 2550, and MDPMTP = 2 (as it must be for a 3D mesh). This gives roughly B/N > . This roughly wins us a factor of eight compared to the purely data parallel (or FSDP) case, where assuming a 3D mesh we calculate that B/N must exceed about to be compute bound.adaptation At this page's live values the threshold is tokens/chip (the 850 for pure FSDP is α/3, all three mesh axes carrying the weight collectives). Watch the two clocks race in the meter below. Try: drag B = down until the verdict flips to comms-bound, then check B/N = against the threshold — the flip lands right at the ridge (assuming DP is near DPopt; if it's far off, the flip comes sooner).
Below we plot the ratio of FLOPs to comms time for mixed FSDP + TP, comparing it both to only tensor parallelism (TP) and only data parallelism (FSDP), on a representative 4x4x4 chip array. While pure FSDP parallelism dominates for very large batch sizes, in the regime where batch size over number of chips is between roughly 100 and 850, a mixed FSDP + TP strategy is required in order to be compute-bound.adaptation The live chart below plays this figure's role: flip its view toggle to ratio to see Tmath/Tcomms for all three schemes, where any curve above 1 is compute-bound. It is drawn at the page's current N = chips — press the 4×4×4 chapter-example preset above to reproduce the chapter's exact frame.
Here's another example of TPU v5p 16x16x16 showing the FLOPs and comms time as a function of batch size for different sharding schemes.adaptation That second figure is the same chart in absolute-times view. A 16x16x16 slice is 4096 chips — exactly the page's default DP·TP = 512 · 8, so the “back to page defaults” preset above reproduces it.
The black curve is the amount of time spent on model FLOPs, meaning any batch size where this is lower than all comms costs is strictly comms bound. You'll notice the black curve intersects the aqua curve at about 4e5, as predicted.adaptation On the live chart that crossing sits at B = N·α²·E/(MDP·MTP·k²·F) = total tokens; the chapter's 4e5 is this same formula evaluated at its 16x16x16, F ≈ 30k scenario.
Here's an interactive animation to play with this, showing the total compute time and communication time for different batch sizes:
You'll notice this generally agrees with the above (minimum around FSDP=256, TP=16), plus or minus some wiggle factor for some slight differences in the number of axes for each.adaptation The chapter's animation swept the FSDP/TP split itself; on this page that sweep is the earlier DP-axis explorer, whose optimum at the current state is DPopt = → nearest power of two -way FSDP. The ±wiggle from mesh-axis bookkeeping is exactly the MDP, MTP factors you can scrub above.
✦ This section is drawn from Chapter 12 (GPUs) of the same book and merged into this chapter's flow by this adaptation; condensed passages are marked. Its cost model is Chapter 12's switched GPU fabric (NVLink node + InfiniBand scale-out — see the GPU network model below). One naming change throughout: Chapter 12 calls the expert-parallel axis Z; this edition names every parallelism degree after its scheme, so that axis is rendered EP here. The routed/shared-expert split and the hardware-domain generalization of the H100-specific formula are AI-written adaptation material, labeled again at the live estimate.
As we've already noted above, Mixture of Expert (MoE) models come with E times more model weights with only k times more FLOPs, making data parallelism significantly harder.adaptation In Chapter 12 "noted above" pointed at its Data Parallelism section; on this page that passage lives at the end of Data Parallelism. This page's E and k count all experts, including always-on shared experts. Expert routing instead uses Er = E − s routed experts and kr = k − s routed selections, where s = shared experts. Right now, Er = and kr = . We can mitigate the routed weight cost by sharding along the expert dimension, i.e. Win[EEP, D, F]. To do the MLP block, we need to introduce 2x AllToAll to send our activations to the corresponding experts.
What does an AllToAll cost here? GPUs within a node have all-to-all connectivity, which makes AllToAlls, well, quite easy: each GPU just sends directly to the destination. For Mixture of Expert (MoE) models, we frequently want to do a sparse or ragged AllToAll, where we guarantee at most kr of N shards on the output dimension are non-zero; the cost is reduced by kr/N.adaptation Condensed from Chapter 12's intra-node collectives discussion (two paragraphs on dense and ragged AllToAlls, with the exact expected-occupancy footnote) — see Chapter 12 for the full derivation. The takeaway below is carried verbatim.
For the eight-GPU H100 node used in Chapter 12, the cost of this AllToAllEP→kr([B, D, k]) if it spans multiple nodes is roughly TAllToAll = 2 · B · D · (EP − 8)/(W · EP) · min(8 · kr / EP, 1), so for pure expert parallelism we neededited The chapter reads: “the cost of this AllToAllZ->k([B, D, k]) if it spans multiple nodes is roughly $T_\text{AllToAll} = 2 \cdot B \cdot D \cdot (Z-8)/Z \min(8 * k / Z, 1)$” — its inline expression omits the division by W, evidently a typo (the chapter's own displayed Tcomms just below includes it); restored here.
✦ Adaptation: The live estimate is the substantive mash-up here. Within one NVLink domain it uses the finite ragged AllToAll cost from Chapter 12 rather than calling that transfer free. Beyond the domain it takes the slower of the local switched-fabric component and the chapter's scale-out component, replacing the H100-specific 8 with the selected hardware's domain size. Shared experts remain in the total k-wide compute, but never become routed AllToAll destinations.
For that H100 case, Chapter 12 concludes that we either need kr > EP/8 with F > α · (EP − 8)/kr or EP ≫ kr and F > 8 · α, where α = C/W. This gives you two domains in which expert parallelism is possible, one with a small amount of expert parallelism (roughly 2-node) and small F, or one with large F and EP arbitrarily large (up to Er-way expert parallelism).
You'll see both cases in practice, either a small amount of expert-parallelism (like DeepSeek v3 which has very small F and relatively small, restricted cross-node expert parallelism), or models with large F, in which case we can do significant cross-node EP alongside TP.
You'll probably notice we've avoided talking about pipelining at all in the previous sections. Pipelining is a dominant strategy for GPU parallelism that is somewhat less essential on TPUs. Briefly, pipelined training involves splitting the layers of a model across multiple devices and passing the activations between pipeline stages during the forward and backward pass.adaptation On this page the split is live: with L = layers over PP = pipeline stages (scrubbable below), each device owns about consecutive layers. The algorithm is something like:
This pseudocode should run on a Cloud TPU VM. While it's not very efficient or realistic, it gives you a sense how data is being propagated across devices.
batch_size = 32
d_model = 128
d_ff = 4 * d_model
num_layers = len(jax.devices())
key = jax.random.PRNGKey(0)
# Pretend each layer is just a single matmul.
x = jax.random.normal(key, (batch_size, d_model))
weights = jax.random.normal(key, (num_layers, d_model, d_model))
def layer_fn(x, weight):
return x @ weight
# Assume we have num_layers == num_pipeline_stages
intermediates = [x]
for i in range(num_layers):
x = layer_fn(x, weights[i])
intermediates.append(x)
if i != num_layers - 1:
x = jax.device_put(x, jax.devices()[i+1])
def loss_fn(batch):
return jnp.mean(batch ** 2) # make up some fake loss function
loss, dx = jax.value_and_grad(loss_fn)(x)
for i in range(num_layers - 1, -1, -1):
_, f_vjp = jax.vjp(layer_fn, intermediates[i], weights[i])
dx, dw = f_vjp(dx) # compute the jvp dx @ J(L)(x[i], W[i])
weights[i] = weights[i] - 0.01 * dw # update our weights
if i != 0:
dx = jax.device_put(dx, jax.devices()[i-1])
Why is this a good idea? Pipelining is great for many reasons: it has a low communication cost between pipeline stages, meaning you can train very large models even with low bandwidth interconnects. This is often very useful on GPUs since they are not densely connected by ICI in the way TPUs are.adaptation The chapter doesn't quantify "low communication cost," so the check below is ours. A stage-boundary hop is a single point-to-point copy of one activation block — 2D = per token in bf16 — and it's the same roofline question as ever: does the hop fit under one stage's compute clock? The line below runs the numbers for one microbatch.
Why is this difficult/annoying? You might have noticed in the pseudocode above that TPU 0 is almost always idle! It's only doing work on the very first and last step of the pipeline. The period of idleness is called a pipeline bubble and is very annoying to deal with. Typically we try to mitigate this first with microbatching, which sends Mmicro = small batches through the PP = -stage pipeline, keeping TPU 0 utilized for at least a larger fraction of the total step time.
The overall communication cost of pipelining is tiny: with NMB microbatches and Nstages, we have Tcomms per hop = 2 · B · D / (W · NMB) and NMB + Nstages − 2 hops, so roughlyadaptation This passage — visible because a GPU preset is loaded — is Chapter 12's treatment of pipeline parallelism, moved here into the chapter's pipelining section by this adaptation. Its cost formula and reason (2) are verbatim; reasons (1) and (3) are condensed to a sentence each, as marked.
Since we are dividing by Nlayers, this is vastly smaller than any of the other costs. In other words, from a communication standpoint, pipelining is basically free. So why don't we just do pipelining? There are a few reasons:
(1) Code complexity: pipelining fits poorly into automatic parallelism frameworks (like XLA's GSPMD), because microbatching and custom zero-bubble schedules change the structure of the program.adaptation Condensed to one sentence — see Chapter 12 for the full paragraph.
(2) Pipelining makes data parallelism and FSDP hard: probably the biggest reason not to do pipelining is that it plays badly with FSDP and data parallelism. ZeRO-3 sharding in particular works badly, since it requires us to AllGather the weights on every microbatch which doesn't work when we have only B / Nmicrobatches tokens to amortize the AllGather cost. Furthermore, during the backward pass, we can't AllReduce or ReduceScatter the gradients until the last microbatch has passed a given stage, which means we have significant non-overlapped communication time.
(3) Pipeline bubbles and step imbalance: naive pipeline schedules leave stages idle in bubbles, and passing activations from stage to stage on the critical path shifts stages relative to each other and adds overhead.adaptation Condensed to one sentence — see Chapter 12, and the live bubble math just above.
There are workarounds for each of these issues, but they tend to be complicated to implement and difficult to maintain; pipelining remains a technique with low communication cost relative to other methods.
A second approach is to carefully overlap the forward matmul Wi @ xi, the backward dx matmul Wi @ ∂L/∂xi+1, and the dW matmul ∂L/∂xi+1 @ xi. Since each of these requires some FLOPs, we can overlap them to fully hide the bubble. Here's our live stand-in for the plot from the recent DeepSeek v3 paper showing their "bubble-free" pipeline schedule:adaptation Toggle the widget's mode: naive is GPipe, 1F1B interleaves one forward with one backward (same bubble, far less activation memory held live), and overlap-dW is the DeepSeek-v3-style schedule — rush every ∂x result down the pipeline to unblock neighbors, and drop the deferred ∂W matmuls into slots that would otherwise sit idle.
Because it is less critical for TPUs (which have larger interconnected pods), we won't delve into this as deeply, but it's a good exercise to understand the key pipelining bottlenecks.adaptation The condensed picture: pipelining's communication is one activation hop per stage boundary, so it thrives on weak interconnects and dominates GPU training; the price is the bubble — currently of each device's time — which microbatching shrinks and careful ∂x/∂W overlap can erase.
The largest possible TPU slice is a TPU v5p SuperPod with 8960 chips (and 2240 hosts). When we want to scale beyond this size, we need to cross the Data-Center Networking (DCN) boundary. Each TPU host comes equipped with one or several NICs (Network Interface Cards) that connect the host to other TPU v5p pods over Ethernet. As noted in the TPU Section, each host has about 200Gbps (25GB/s) of full-duplex DCN bandwidth, which is about full-duplex (egress) bandwidth per TPU.adaptation Per the TPU chapter of the original book: each v5p host serves 4 chips, so 25 GB/s per host ÷ 4 ≈ 6.25 GB/s of egress per chip.adaptation The chapter printed 6.25GB/s; here Wdcn is scrubbable — drag it and this whole section (ridge included) recomputes. The hardware presets in the top bar set it per machine.
Typically, when scaling beyond a single pod, we do some form of model parallelism or FSDP within the ICI domain, and then pure data parallelism across multiple pods. Let N = be the number of TPUs we want to scale to and M = be the number of TPUs per ICI-connected slice. To do an AllReduce over DCN, we can do a ring-reduction over the set of pods, giving us (in the backward pass):
✦ Adaptation: The printed derivation assumes full, equal-size slices (N is a multiple of M). The live model balances the chips across slices, so a partial final slice cannot silently receive a full slice's aggregate NIC bandwidth.
The comms bandwidth scales with M, since unlike ICI the total bandwidth grows as we grow our ICI domain and acquire more NICs. Simplifying, we find that Tmath > Tcomms when
For TPU v5p, the C/Wdcn is about / = . This tells us that to efficiently scale over DCN, there is a minimum batch size per ICI domain needed to egress each node.adaptation This is the primer's roofline wearing one more costume — same slanted roof, much worse network. The ridge that sat at tokens per chip over ICI now sits at tokens per pod over DCN (both carrying the MoE's E/k weight-to-FLOPs factor).
How much of a problem is this? To take a specific example, say we want to train LLaMA-3 70B on TPU v5p with a BS of tokens. LLaMA-3 70B has F ≈ . From the above sections, we know the following:
The TLDR is that we have a nice recipe for training with BS=1M, using roughly DP (FSDP) = 1024 and TP (TP) = 8, but with BS=2M we need to use DCN. As noted above, we have a DCN arithmetic intensity of , so we just need to make sure our batch size per ICI domain is greater than this. This is trivial for us, since with 2 pods we'd have a per-pod BS of , and a per TPU batch size of , which is great (maybe cutting it a bit close, but theoretically sound).adaptation The chapter's printed values (per-pod BS of 1M, per-TPU batch of 111) appear when you load the two-pod preset below; everything is recomputed from the live state, so try the one-pod recipe first and watch both numbers move.
✦ This section is an addition of this adaptation, drawing its text from Chapter 12 (GPUs) of the same book; condensed passages are marked. Chapter 12's per-scheme roofline derivations are not repeated here — they re-derive what this chapter already derived, so they are merged into the scheme sections above (the MoE penalty into Data Parallelism, the TP bound into Tensor Parallelism, expert parallelism into its own section, and the pipelining reasons into Pipelining). What remains here is the network model itself: the fabric, its bandwidths, what collectives cost on it, and the worked examples.
Now let's look at what this has all been building towards: understanding rooflines for LLM scaling on GPU. This is to complement the TPU training chapter here. As we did there, the goal here is to look at the total Tmath and Tcomms for different parallelism strategies and understand at what point Tcomms > Tmath. As before, we consider only the MLP block with operations
where B is the global batch size in tokens (i.e. B = batch size · sequence length).
Here we'll reproduce the table from Chapter 12 showing effective bandwidths at both the GPU and node level:
| Node Type | GPUs per node | GPU egress bandwidth | Node egress bandwidth |
|---|---|---|---|
| H100 | 8 | 450e9 | 400e9 |
| B200 | 8 | 900e9 | 400e9 |
| GB200 NVL72 | 72 | 900e9 | 3600e9 |
| GB300 NVL72adaptation This row is the adaptation's, not Chapter 12's — from NVIDIA's published GB300 NVL72 specs (dense BF16 = 180 PFLOPS/rack ÷ 72 = 2.5 PFLOP/s per GPU; ConnectX-8 at 800 Gb/s per GPU doubles the scale-out egress to 7200e9 per domain). | 72 | 900e9 | 7200e9 |
Let's look at the compute communication rooflines as we did for TPUs for data parallelism, tensor parallelism, pipeline parallelism, expert parallelism, and combinations thereof. For the rest of this section we'll focus on H100 rooflines for specific calculations. GB200-NVL72 has the same general rooflines but because we have a larger node egress bandwidth, we can sometimes be bottlenecked at the node level instead. The scheme derivations are merged into their corresponding sections above; below are the bounds they land on here.
Here is the mapping used by the live GPU rooflines: read Wici as the per-GPU egress bandwidth into the NVLink switch fabric (450 GB/s on H100, 900 GB/s on B200); read a pod as an NVLink domain (8 GPUs per node, or 72 on GB200 NVL72); read Wdcn as each GPU's share of its node's InfiniBand egress (400e9/8 = 50 GB/s on H100 and B200, and likewise 3600e9/72 on GB200); and, because a switched fabric has no mesh axes, set MDP = MTP = 1. Then C/Wici = is the in-node ridge, C/Wdcn = the per-GPU scale-out ratio. DP/FSDP/TP select the local or scale-out collective bandwidth from their degree. Mixed FSDP+TP needs one extra correction: Chapter 12's hierarchical max(Tdomain, Tscale-out) rule, because inner TP does not accelerate an outer reduction until it spans more than one NVLink domain. The mixed meters and explorers implement that rule.
For data parallelism and ZeRO sharding, the compute-bound rule derived in Data Parallelism — B/DP > C/Wcollective — is reused unchanged, where Wcollective is either the GPU or node level egress bandwidth depending on whether we're sharding within a node or across nodes. Thus:
This is quite a bit higher than on a TPU, where the number is 850 with all three axes. On the H100 scale-out fabric the dense asymptotic floor is 990e12/400e9 = 2,475 tokens per GPU, so 16,384 GPUs would require about 40.6M tokens before the small-ring and model-parallel refinements; Llama 3.1 405B used 16M. Chapter 12 quoted a 3,300-token H800 baseline from an unsupported 300 GB/s figure. The reconciled H800 local-link dense baseline is 4,950 in spec mode (990e12/200e9) and about 4,517 in this page's measured mode. Those are not a model of DeepSeek's full sparse run: its EP, PP, and 2-way DP alter the outer reduction. DeepSeek reports a pretraining batch schedule from 3,072 to 15,360 sequences at a 4K maximum sequence length — about 12.6M to 62.9M tokens, with 62.9M at steady state.edited The source says H800 has 300 GB/s and “in practice, they used 4M”. H800 is 200 GB/s per direction by the reconciled spec, DeepSeek reports 160 GB/s measured, and its report gives the sequence-batch schedule above. See the hardware table.
Small-DP correction. The asymptotic ridge above omits the ring factor. With X scale-out domains, the exact dense condition is B/N > (C/Wcollective) · (X−1)/X (and ×E/k for the equal-width MoE model). For exactly two domains the floor is halved, which is why 2-way data parallelism appears so often.
For tensor parallelism, the bound from Tensor Parallelism — TP < F · Wcollective / C — gives about F/-way TP within a node, and pipelining — whose Chapter 12 treatment now lives in Pipelining under GPU presets — is basically free from a communication standpoint. As with above, we get an extra 2X bandwidth when we span exactly 2 fast-fabric domains. For the chapter's eight-GPU H100 node this usually permits 16-way TP; generalized to a domain of G = , the refinement is F > · (TP − G).
Beyond the node level: to a first approximation, because we have full bisection bandwidth, the cost of an AllGather or ReduceScatter is roughly the buffer size in bytes divided by the node egress bandwidth (400GB/s on H100) regardless of any of the details of the tree reduction.
With in-network reductions enabled and using pure data parallelism, theoretically we have 2x the AllReduce bandwidth, which would halve both of these numbers. However, in practice the benefit is closer to 30%, which only really makes up for the fact that we typically struggle to reach the reported numbers. Furthermore, because pure data parallelism is rarely useful, this basically doesn't matter in practice.
What does DeepSeek do? For reference, DeepSeek V3 is trained with 2048 H800 GPUs with:adaptation These two worked examples are Chapter 12's. The buttons load their cited hardware, model shape, batch, and listed parallelism degrees. Because the page's generic chip count is DP·TP, widgets that do not explicitly model EP or PP should be read as component-level views, not as a reconstruction of the full training run.
They had a steady state batch size of 4096 · 15360 = 62,914,560 tokens, or 30k tokens per GPU. You can see that this is already quite large, but their model is also very sparse (k=8, E=256) so you need a fairly large batch size. You can see that with 64-way EP and 16-way PP, we end up with 1024-way model parallelism in total, which means the AllReduce is done at the spine level, and because it's only 2-way, we end up with 2 / (2 − 1) = 2 times more bandwidth in practice. This also helps reduce the cost of the final data-parallel AllReduce overlapping with the final pipeline stages.
What does Llama 3.1 405B do? Llama 3.1 405B trains with a BS of 16M tokens on 16,384 H100 GPUs, or about 977 tokens per GPU. They do:
The decomposition is 8 TP · 16 PP · 128 DP = 16,384 GPUs. This is also a dense model so in general these things are pretty trivial. The 16-way PP reduces the cost of the data parallel AllReduce by 16x, which helps us reduce the critical batch size.
Practical recipe: smaller dense models can use aggressive FSDP when batch permits; larger dense models commonly combine one- or two-domain TP with many-stage PP and DP; MoEs add EP, generally preferring it to TP while keeping latency-sensitive collectives within as few domains as practical.
| Strategy | Description |
|---|---|
| Data Parallelism | Activations are batch sharded, everything else is fully-replicated, we all-reduce gradients during the backward pass. |
| FSDP | Activations, weights, and optimizer are batch sharded, weights are gathered just before use, gradients are reduce-scattered. |
| Tensor Parallelism (aka Megatron, Model) | Activations are sharded along dmodel, weights are sharded along dff, activations are gathered before Win, the result reduce-scattered after Wout. |
| Mixed FSDP + Tensor Parallelism | Both of the above, where FSDP gathers the model sharded weights. |
And here are the "formulas" for each method:
| Strategy | Formula |
|---|---|
| DP | In[BDP, D] ·D Win[D, F] ·F Wout[F, D] → Out[BDP, D] |
| FSDP | In[BDP, D] ·D Win[DDP, F] ·F Wout[F, DDP] → Out[BDP, D] |
| TP | In[B, DTP] ·D Win[D, FTP] ·F Wout[FTP, D] → Out[B, DTP] |
| TP + FSDP | In[BDP, DTP] ·D Win[DDP, FTP] ·F Wout[FTP, DDP] → Out[BDP, DTP] |
| Strategy | Compute per layer (ignoring gating einsum) |
Comms per layer (bytes, forward + backward pass) |
|---|---|---|
| DP | 4BDF/DP + 8BDF/DP | 0 + 8DF |
| FSDP | 4BDF/DP + 8BDF/DP | 4DF + 8DF |
| TP | 4BDF/TP + 8BDF/TP | 4BD + 4BD |
| FSDP + TP | 4BDF/(DPTP) + 8BDF/(DPTP) | (4BD/DP + 4DF/TP) + (8BD/DP + 8DF/TP) |
✦ Adaptation: The source table below this anchor is the dense TPU summary, so it is hidden for the current state rather than allowed to display false MoE or GPU formulas. For GPU guidance, use the GPU TLDR and practical recipe; for MoE routing, use Expert Parallelism. The scheme-level meters remain live for the selected model and hardware.
Let's use LLaMA-2 13B as a basic model for this section. Here are the model details:adaptation Every value in this table is scrubbable, and every answer below is computed from it live: the exercises grade themselves against whatever model you dial in; each question's stated givens (a batch size, a chip count) stay pinned, the way a problem set's givens should. The preset button under the table restores the chapter's LLaMA-2 13B.
| hyperparam | value |
|---|---|
| L | |
| D | |
| F | |
| N | |
| K | |
| H | |
| V |
LLaMA-2 has separate embedding and output matrices and a gated MLP block.
Question 1: How many parameters does LLaMA-2 13B have (I know that's silly but do the math)? Note that, as in Transformer Math, LLaMA-3 has 3 big FFW matrices, two up-projection and one down-projection. We ignored the two "gating" einsum matrices in this section, but they behave the same as Win in this section.
Question 2: Let's assume we're training with BS= tokens and using Adam. Ignoring parallelism for a moment, how much total memory is used by the model's parameters, optimizer state, and activations? Assume we store the parameters in bf16 and the optimizer state in fp32 and checkpoint activations three times per layer (after the three big matmuls).
The total memory used for the parameters (bf16) and the two optimizer states (fp32, the first and second moment accumulators) is (2 + 4 + 4) · ≈ . The activations after the first two matmuls are shaped BF and after the last one BD (per the Transformer diagram above), so the total memory for bf16 is 2 · L · (BD + 2 · BF) = 2LB · (D + 2F) or 2 · · · · (1 + 2 · ) ≈ = , since B=. All other activations are more or less negligible.adaptation Try: drag the batch and watch: the parameter + optimizer term () never moves, while the activation term scales linearly with it. That memory monster is what FSDP-style activation sharding exists to slay.
Question 3: Assume we want to train with 32k sequence length and a total batch size of 3M tokens on a TPUv5p 16x16x16 slice. Assume we want to use bfloat16 weights and a float32 optimizer, as above.
First, let's write down some numbers. With 32k sequence length and a 3M batch size, we have a sequence batch size of .adaptation The chapter says 96, which is 3·220/32,768; the live math here uses a literal 3e6, which gives . Either way: small! Long contexts eat a token budget fast. On a TPU v5p 16x16x16 slice, we have of HBM.
We can't use pure data parallelism, because it replicates the parameters and optimizer states on each chip, which are already around (from Q2) which is more HBM than we have per-chip ().
Let's start by looking purely at memory. Replacing BS= with 3M in Q2, we get ~ total checkpoint activations, and with the optimizer state this brings us to almost exactly = . The TPUv5p slice has of HBM in total, so we are safely under the HBM limit.
Next let's look at whether we'll be comms or compute-bound. With 4096 chips and 3 axes of parallelism, we can do a minimum batch size of · 4096 = tokens. That's slightly above our 3M batch size. So we're actually comms-bound, which is sad. So the general answer is no, we cannot do FSDP alone.
Now we know our primary concern is being comms-bound, so let's plug in some numbers. First of all, we know from above that our per-chip batch size with mixed FSDP + tensor parallelism needs to be above ² / 2F = here. That means we can in theory do this! Let's figure out how much of each.
We have the rule
so here we have sqrt(3e6 · 2 · 4096 / ) = , meaning we'll do roughly way DP and way TP. Per TPU memory will be as in (2), and step time will just be
Above, we simplified the Transformer layer forward pass as Out[B, D] = In[B, D] ·D Win[D, F] ·F Wout[F, D]. How do we derive the comms necessary for the backwards pass?
This follows fairly naturally from the rule in the previous section for a single matmul Y = X · A:adaptation In this appendix X and Y are the input and output matrices of a generic matmul — the chapter's letters, kept as-is since this edition's mesh axes go by DP and TP, so nothing collides.
Using this, we get the following formulas (letting Tmp[B, F] stand for In[B, D] · Win[D, F]):
Note that these formulas are mathematical statements, with no mention of sharding. The job of the backwards pass is to compute these four quantities. So to figure out the comms necessary, we just take the shardings of all the quantities which are to be matmulled in the four equations above (Tmp, dOut, Wout, Win), which are specified by our parallelization scheme, and use the rules of sharded matmuls to figure out what comms we have to do. Note that dOut is sharded in the same way as Out.
Look back at Part 4: Transformer Math, continue to Part 6: Applied Training, which works this content through real LLaMA models, or revisit the original chapter this page adapts.