Concept
Llama-3-70B's weights alone are 141.10 GB in BF16: they don't fit on one 80 GB GPU, before any KV cache. Even when a model fits, spreading it over several GPUs adds their memory bandwidth together, and decode is bandwidth-bound. There are three main ways to split a model, and each one adds a different kind of message.
Tensor parallelism (TP) splits every weight matrix across the devices. Megatron-LM's arrangement splits the attention heads and the MLP's hidden dimension so that each block needs just two all-reduces in the forward pass: one after attention, one after the MLP. Every device holds of every layer and does of every multiply. The messages are small (one activation vector per token) but there are many of them, twice per layer, on the critical path of every step. TP wants the fastest link you have, which is why it normally stays inside one NVLink-connected server.
Pipeline parallelism (PP) gives each device a contiguous slice of the layers. A token's activations pass from stage to stage: one message per boundary per step. Cheap on the link, so PP can span slower links between servers. The catch is the bubble: stage 2 can't start until stage 1 finishes, so with a single batch only one stage is busy at a time. Keeping several micro-batches in flight fills the pipeline; GPipe's bubble fraction is for stages and micro-batches.
Expert parallelism (EP) applies to mixture-of-experts models, whose MLP is many "expert" MLPs and a router that sends each token to its top-. Different devices hold different experts, so every MoE layer needs an all-to-all: send each token to the devices holding its experts (dispatch), and bring the results back (combine).
Real deployments mix them, for example TP inside a server, PP or EP across servers, and data parallelism (independent replicas) on top.
Interactive
What splitting a model across devices sends
Llama-3-70B decode, context 2,048 per sequence, BF16 activations. Link bandwidths and latencies from the simulator's hardware.py. First-order estimates: no overlap with compute; real collective libraries use faster algorithms for small messages.
Communication is 25% of a decode step if nothing overlaps. Two all-reduces per layer (after attention and after the MLP), 160 per step for 80 layers.
Concept
With the simulator's link figures and this page's first-order model, a Llama-3-70B decode step at batch 16 on 4× H100 takes 14.47 ms of compute, and TP's 160 all-reduces add 4.94 ms over NVLink but 24.23 ms over 100 GbE. Notice that on NVLink almost all of it (4.80 ms) is the per-message latency term, not bandwidth: decode messages are tiny. That is a pessimistic estimate (collective libraries have low-latency algorithms for small messages, and engines overlap communication with compute), but the direction is right: decode-time TP is latency-sensitive, and slow links make it impractical. A pipeline boundary over InfiniBand, by contrast, moves 256 KiB per step in about 46 µs.
Maths
Ring all-reduce of bytes over devices sends bytes per device in messages, so on a link with bandwidth and per-message latency
TP per decode step: all-reduces of ( tokens, width , bytes per activation). PP: boundaries of bytes each. EP with top- routing over layers: about bytes per layer leave each device. Pipeline bubble: ; with 4 stages, 4 micro-batches idle 43% of the time and 16 micro-batches 16%.
Code
// src/lib/inference/parallel.ts (excerpt)
export function ringAllReduce(size: number, n: number, link: Link): Comm {
if (n <= 1) return ZERO;
const bytes = (2 * (n - 1) * size) / n;
const messages = 2 * (n - 1);
return {
bytes,
time: bytes / link.bandwidth + messages * link.latency,
messages,
};
}
The links (NVLink 4, PCIe Gen5, InfiniBand NDR, 100 GbE) are copied from the
simulator's hardware.py, and a unit test checks the copy against the
Python fixture.