Tensor parallelism
ZeRO and FSDP split the storage of a model across GPUs, but every GPU still does all of the math for its own tokens. Before each layer runs, the full weights are gathered onto every GPU, and every GPU stores the full activations of its own sequences. For a 70B model at 8,192 tokens that means 182 GB of activations per sequence, which no amount of sharding removes.
Tensor parallelism takes the opposite approach. Split each weight matrix itself across GPUs, have each GPU do of every matrix multiply on the same tokens, and combine the partial results with a collective. Each GPU then stores of the weights and computes and stores only part of the activations. Shoeybi et al. (2019) worked out, in the Megatron-LM paper, how to do this for a Transformer with only two all-reduces per layer in the forward pass, using nothing but the algebra of block matrices. This section derives their splits from scratch, checks them in NumPy and PyTorch, counts the communication, and explains why tensor parallelism almost never leaves a single node.
One matrix multiply, split two ways
Take a linear layer with input ( tokens, which is for a micro-batch of sequences of tokens) and weight , split over GPUs. There are two natural ways to cut .
Column parallel. Cut into column blocks, with . Then
GPU holds the full input and its block , and computes , the -th block of output columns. No communication is needed in the forward pass. The output is left split along its last dimension.
Row parallel. Cut into row blocks , and the input correspondingly into column blocks with . Block multiplication gives
GPU holds and and computes a partial sum of the full shape . The real output is the sum of the partial sums, so the GPUs must all-reduce them.
In plain words, splitting the weight's columns splits the output and needs no communication, while splitting its rows splits the input and leaves partial sums that must be added.
The backward pass mirrors this. With upstream gradient (§0.2), the column-parallel layer has , local to GPU , but , a sum over GPUs, so the backward pass needs an all-reduce. The row-parallel layer needs the full on every GPU, which it has (the forward all-reduce gave every GPU the full ), and then and are both local. Each split needs exactly one all-reduce, one in the forward pass and the other in the backward pass.
Column- and row-parallel linear layers
Implement column_parallel(X, A, t) returning the list of t output shards [X @ A_1, ..., X @ A_t] for the column blocks of A, and row_parallel(X, A, t) returning (partials, Y), where partials[i] = X_i @ A_i for the matching column blocks of X and row blocks of A, and Y is their sum (the result of the all-reduce). Assume t divides the relevant dimensions, and use np.split so that block i is the i-th contiguous block.
The Megatron MLP, one all-reduce
Now the feed-forward network of a Transformer block (§5.3), with and . Which way should each matrix be split?
Try splitting by rows first. Then is a sum of partial products, and we would have to all-reduce it before the GELU, because . That costs a synchronization in the middle of the block.
Split by columns instead. GPU gets , a block of columns of the hidden activation, and because GELU is applied elementwise, . Each GPU can apply it to its own block with no communication. The hidden activation is now split along its columns, which is exactly the input a row-parallel layer wants. So split by rows, and
In plain words, each GPU runs a narrow MLP with hidden units on all the tokens, and the narrow MLPs' outputs are added with one all-reduce.
Shoeybi et al. name the two conjugate communication points and . Operator sits where the replicated input enters the parallel region. It is the identity in the forward pass and an all-reduce in the backward pass (the column-parallel sum). Operator sits where the partial sums leave. It is an all-reduce in the forward pass and the identity in the backward pass. So the MLP costs one all-reduce forward and one backward.
Biases need one piece of care. The first layer's bias splits with 's columns, each GPU adding its own slice before the GELU. The second layer's bias is not split, and it must be added once, after the all-reduce, not on every GPU before it, or it would be counted times. Gated feed-forward networks such as SwiGLU split the same way, with the gate and up projections column-parallel (with matching column blocks, so that each GPU multiplies the right gate with the right value) and the down projection row-parallel.
Tensor-parallel MLP on simulated shards
Implement tp_mlp(X, A, b1, B, b2, t) computing the Megatron way on t simulated GPUs. Shard A and b1 by columns and B by rows into t contiguous blocks, have "GPU" i compute its partial output using only its own shards, all-reduce (sum) the partials, and add b2. Return (Z, partials), the final output and the list of the t partial outputs before the all-reduce. Use the provided gelu.
Attention, split by heads
Attention splits even more naturally, because its heads are already independent (§4.5). Give each GPU of the layer's heads. The query, key, and value projections are column-parallel, each GPU holding the columns of , , and that belong to its heads. Each GPU then computes full attention for its heads, on all tokens, with no communication at all. The output projection is row-parallel, because the identity of §4.5, , says that the head outputs times their row blocks of add up. One all-reduce, , finishes the attention block, and sits at its input.
Two constraints follow. The number of heads must be divisible by . With grouped-query attention and key-value heads, each GPU also needs the key-value heads its query heads use, so should divide , or the key-value heads are replicated. Llama 3 70B, with 64 query heads and 8 key-value heads, splits cleanly up to , one key-value head per GPU.
Put together, a Transformer layer under tensor parallelism communicates four times per training step, two all-reduces in the forward pass ( after attention and after the MLP) and two in the backward pass ( at their inputs), each of the full activation . The cell below checks the attention split numerically.
Embeddings and the loss
The embedding table and the output layer are the largest single matrices in many models, each (for Llama 3 70B, , a billion parameters), so they are split too.
Vocabulary-parallel embedding. Split the table by rows, so GPU holds the embeddings of token ids in its range of . Each GPU looks up the tokens that fall in its range, writes zeros for the others, and an all-reduce sums the results, so each token's row comes from exactly one GPU.
Vocabulary-parallel cross-entropy. The output layer is column-parallel, so GPU ends with logits for its vocabulary entries. Gathering the full logits would be expensive (gigabytes, as §10.1 showed). Instead compute the loss from three small all-reduces. The cross-entropy for a token with logits and target is . Each GPU computes its local maximum, and an all-reduce with the max operation gives the global maximum for numerical stability. Each GPU computes , and an all-reduce sums them. The GPU that owns contributes and the others contribute 0, and an all-reduce sums those. Every all-reduce is over numbers instead of .
Vocabulary-parallel cross-entropy
Implement vocab_parallel_ce(logit_shards, targets). logit_shards[i] has shape (n, V_i) and holds the logits of vocabulary entries offset_i to offset_i + V_i - 1, where the offsets are the cumulative sizes of the earlier shards. targets has shape (n,) with ids in [0, V). Return the mean cross-entropy over the n tokens. Each shard may only be reduced locally to arrays of shape (n,), and those arrays combined across shards with an elementwise max or sum (the all-reduces). Do not concatenate the shards. It must be stable for logits as large as .
What tensor parallelism costs
Memory. Each GPU holds of every layer's large matrices, so weights, gradients, and optimizer states all shrink by (norm gains and a few biases are replicated, which is negligible). Activations shrink too, but not completely. Korthikanti et al. (2022) redo the count of §10.1 under tensor parallelism. The tensors inside the parallel regions are split, , , , the attention matrices, the input, and the two wide MLP tensors, bytes plus . The tensors outside them are replicated on every GPU, the two norm inputs, the inputs to the first linear of each block, and the two dropout masks, bytes. So
The replicated is a floor that does not fall with , and we remove it below.
Communication. Each all-reduce is of a activation in 16 bits, bytes, so each GPU sends bytes per all-reduce, and four of them per layer per step. Compare with the compute. A layer's forward pass costs about FLOPs for its weight products (§4.7), split over GPUs, against two all-reduces. With per-GPU throughput and bandwidth , the ratio of communication time to compute time is
In plain words, the bigger the model's width and the faster the link, the cheaper tensor parallelism is, and every extra GPU in the group makes it more expensive.
The number of tokens has cancelled. Unlike data parallelism, no batch size can hide this communication, because it scales with the tokens exactly as the compute does. Worse, it sits on the critical path. The next layer cannot start until the all-reduce finishes, so it is hard to overlap.
For a width-8192 model at over NVLink, communication takes about 0.6 times as long as compute at peak throughput (less at realistic throughput, but it adds directly to the step time). Over InfiniBand the same split takes 5.6 times as long as the compute. That is the reason for the rule every practitioner knows. Tensor parallelism stays within a node, usually at , the number of GPUs sharing an NVSwitch. Larger NVLink domains, such as 72-GPU racks, relax the rule. Splitting matrices finer also makes each GPU's matrix multiplies smaller and less efficient, another reason not to push past the point where memory is solved.
Techniques that hide some of this communication do exist. Asynchronous tensor parallelism (or collective matmul, Wang et al. 2023) splits each matrix multiply into chunks and overlaps sending one chunk with computing the next, and fused kernels can overlap the all-reduce with the following computation. They reduce the exposed time but do not change the bytes.
Sequence parallelism removes the replicated activations
Look again at the bytes that tensor parallelism leaves replicated. They belong to the LayerNorms, the dropouts, and the residual adds, which work on each token independently. Korthikanti et al. observed that these regions can be split along the sequence instead, each GPU handling of the tokens. They called this sequence parallelism.
The transitions change. Entering a tensor-parallel region, the column-parallel layer needs every token, so the sequence-split activations must be all-gathered. Leaving it, the row-parallel layer's partial sums must be added, and the result is to be split by sequence again. An all-reduce followed by keeping only your own tokens is exactly a reduce-scatter. So the conjugate pair , is replaced by a new conjugate pair. Korthikanti et al. call them , an all-gather in the forward pass and a reduce-scatter in the backward pass, and , a reduce-scatter in the forward pass and an all-gather in the backward pass. (They rename Megatron's and as and , so watch the letters when you read the two papers side by side.)
Here is the beautiful part. Tensor parallelism did one all-reduce at each point. Sequence parallelism does one all-gather and one reduce-scatter instead, and an all-reduce is a reduce-scatter plus an all-gather (§10.2). So the communication volume is unchanged, and every activation is now split ways,
and with selective recomputation or FlashAttention, . (To remove the whole , Korthikanti et al. also store only the local sequence shard of the norm's output, the input of the first column-parallel layer, and all-gather it again during the backward pass when its weight gradient needs it. That one extra all-gather per block is the only added traffic, and they overlap it with computation.)
Activation memory and traffic under tensor parallelism
Implement tp_layer_cost(T, b, d, h, t, sp=False, recompute="none") returning (act_bytes, comm_bytes) for one GPT-3-style layer on one GPU of a tensor-parallel group of size t. Activations follow Korthikanti et al.'s Table 2. Without sequence parallelism, "none" gives and "selective" gives . With it, "none" gives and "selective" gives . With "full" recomputation, keep only either way. comm_bytes is what the GPU sends per training step in the layer's tensor-parallel collectives, using 16-bit activations and ring costs. That is four all-reduces of the activation (or the equivalent all-gathers and reduce-scatters with sp), plus two more when recompute="full" reruns the forward pass. With t == 1 there is no communication. Return integers for the activations (round down) and a float for the communication.
In PyTorch
The and operators are four lines each as custom autograd functions, which is how Megatron-LM implements them. The code below needs a tensor-parallel process group and is shown for reference.
PyTorch also has tensor parallelism built in, on top of distributed tensors. You describe each submodule's split, and parallelize_module shards the weights and inserts the collectives. With Shard(1) layouts (the sequence dimension) at the boundaries, the same plan gives sequence parallelism. The PrepareModuleInput entries gather the tokens once per block, at its input, rather than once for each of the three projections. This plan follows PyTorch's tensor-parallel tutorial for a Llama-style model, whose attention takes the hidden states and the rotary frequencies.
On a single process we can still check the core claim of this section, that the sharded computation gives exactly the dense gradients, by letting autograd play the role of the collectives. Summing the partial outputs is , whose backward is the identity, and giving each shard its own copy of the input makes 's backward a sum of their gradients.
| data parallel (FSDP) | tensor parallel | tensor plus sequence parallel | |
|---|---|---|---|
| splits | the batch (and the model states) | each matrix, by columns or rows | each matrix, and the sequence outside them |
| weights per GPU | with ZeRO-3 | ||
| activations per GPU | full, for its own micro-batch | ||
| communication | weights and gradients, about bytes per step, overlappable | 4 all-reduces of activations per layer, on the critical path | same volume as tensor parallel |
| scales with | tokens per GPU | width and link speed | the same |
| typical degree | hundreds to thousands | up to 8, inside a node | up to 8, inside a node |
Read more
- Shoeybi et al. (2019). Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism. The column and row splits, the f and g operators, and an 8.3B model trained with 8-way tensor parallelism.
- Korthikanti et al. (2022). Reducing Activation Recomputation in Large Transformer Models. Sequence parallelism, the activation formulas under tensor parallelism, and selective recomputation.
- Narayanan et al. (2021). Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM. Section 3 shows why tensor parallelism should stay inside a node.
- Wang et al. (2023). Overlap Communication with Dependent Computation via Decomposition in Large Deep Learning Models. Collective matmuls that hide tensor-parallel communication.
- PyTorch. Large Scale Transformer model training with Tensor Parallel. The
parallelize_moduleinterface and sequence parallelism on distributed tensors. - Tazi et al. (2025). The Ultra-Scale Playbook. Its tensor and sequence parallelism chapters measure throughput as the degree grows across the node boundary.