Tensor parallelism: split inside the layer
Tensor parallelism (Megatron-LM) splits the weight matrices themselves across GPUs. The arrangement is chosen so that communication happens as rarely as possible.
The MLP block. Two matmuls in sequence: Y = GeLU(X @ A), then Z = Y @ B.
Split A by columns across GPUs. Each GPU computes a slice of Y. Crucially, GeLU is elementwise, so each GPU can apply it to its own slice without needing anyone else's — no communication.
Then split B by rows, matching the column split of A. Each GPU computes a partial Z using its slice of Y and its rows of B. The partials sum to the correct answer, so one all-reduce finishes the block.
GPU 0: X @ A[:, :h/2] -> Y0 -> GeLU -> Y0 @ B[:h/2, :] -> Z_partial_0
GPU 1: X @ A[:, h/2:] -> Y1 -> GeLU -> Y1 @ B[h/2:, :] -> Z_partial_1
all-reduce -> Z
One all-reduce for two matmuls. Splitting the other way round would need communication between them.
The attention block splits naturally by head: give each GPU a subset of heads, let it compute those heads' attention independently, then all-reduce after the output projection. Also one all-reduce.
So: two all-reduces per transformer layer. For an 80-layer model that is 160 all-reduces per forward pass, on the critical path.
Communication volume. A ring all-reduce moves 2(N−1)/N times the data per GPU. For an activation of shape [batch, seq, d_model] in fp16:
bytes per all-reduce = 2 * batch * seq * d_model * 2 * (N-1)/N
At decode (seq = 1), batch 32, d_model = 8192, TP=4:
2 * 32 * 1 * 8192 * 2 * 0.75 = 786,432 bytes = 0.79 MB per all-reduce
x 160 all-reduces = 126 MB per token
Over NVLink at 900 GB/s that is 0.14 ms — against a decode step of a few milliseconds, so roughly 5% overhead. Over PCIe at 64 GB/s it is 1.97 ms, which can double your step time. This is the single most important practical fact in this module: TP belongs inside a node, on NVLink.
The GQA ceiling. TP splits attention by head, and a KV head cannot be split across GPUs without replicating its cache. With 8 KV heads you can run TP-8 cleanly, one KV head per GPU. At TP-16 you must duplicate KV heads, so per-GPU cache stops shrinking as you add GPUs — you pay for hardware that adds bandwidth and compute but no cache capacity. The KV head count silently caps useful TP degree.
MLP under TP-2: column-split then row-split, one all-reduce
X (replicated on both GPUs)
| |
A[:, :h/2] A[:, h/2:] <- column-parallel
| |
GeLU GeLU <- elementwise, NO comms needed
| |
B[:h/2, :] B[h/2:, :] <- row-parallel
| |
Z_part_0 Z_part_1
\__________ + __________/ <- ONE all-reduce
|
Z
REMEMBERColumn-parallel then row-parallel matmuls let a layer be split with exactly one all-reduce at the end, and there are two such all-reduces per transformer layer.