Repository navigation
AutoTP: sequence sharding for training - #8860
Draft
pengdurice wants to merge 3 commits into
Draft
pengdurice wants to merge 3 commits into
pengdurice wants to merge 3 commits into
Conversation
…rding`) Signed-off-by: pengdurice <pengduhit@gmail.com>
Two problems when decoder layers are recomputed in backward (transformers 5 gradient_checkpointing_enable() uses non-reentrant checkpointing): - A block switched from separate collectives to the fused column path on its second call. Under checkpointing the second call is the backward recomputation of the first micro-step, which then saves different tensors than the forward pass: CheckpointError at ZeRO-1/2 in the first backward. Blocks now switch from the model's second forward pass on, counted by a pre-hook on the model. - ZeRO-3 replaces torch.nn.functional.linear with a custom autograd function that saves the weight, and inside a non-reentrant checkpoint PyTorch then does not call the weight's tensor hooks (torch 2.8). A linear weight left replicated in the decoder layers kept its partial gradient. At ZeRO-3 the gradient is now summed in a post-accumulate hook, registered before ZeRO-3's. Tests: every sequence-sharding mode with and without checkpointing at ZeRO 1 and 3, and a model with only attention sharded (replicated MLP linears) under checkpointing at ZeRO 1 and 3. Signed-off-by: pengdurice <pengduhit@gmail.com>
… of the block In its first forward pass a sequence-sharded block hands the gathered block input to every column-parallel linear of the block, so that a linear reading it records itself. A linear that never reads it (it reads another tensor of the block, or the block uses its weight without calling it, as an absorbed MLA attention does with kv_b_proj) kept that reference until the next first pass, which never comes: one copy of the whole sequence's hidden states per decoder layer stayed allocated. A forward hook at the end of each block now drops it. Signed-off-by: pengdurice <pengduhit@gmail.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of: #8672 (Q4 2026 roadmap), "Enable training of large scale models".
Summary
AutoTP training today runs the norms, residual adds and other token-wise ops of every decoder layer on the whole
sequence on every tensor-parallel rank, keeps their activations at full size, and makes one all-reduce per
column-parallel linear in backward plus one per row-parallel linear in forward (7 per Qwen3 layer), all on the compute
stream. This PR adds
tensor_parallel.sequence_sharding(defaultfalse): the hidden states between the attentionand MLP blocks are split along the sequence over the TP ranks, as in Megatron-LM's sequence parallelism.
sequence_sharding_overlap(defaulttrue): the column-parallel linears that read a block input run in oneautograd node; backward reduce-scatters their summed input gradient while the weight-gradient GEMMs run; only this
rank's part of the input is saved for backward and gathered again there.
The name avoids "sequence parallel", which in DeepSpeed means Ulysses (
sequence_parallel_size), a differentscheme; the two are not combined.
Motivation
Today AutoTP training makes 7 all-reduces per Qwen3 layer on the compute stream (5 column-parallel input gradients in
backward, 2 row-parallel outputs in forward). On Qwen3-32B with activation recompute on 2 nodes, AutoTP got 62% slower
from TP 2 to TP 8 (Megatron-LM: 19%), and its best TP layout was 1.42x slower than Megatron-LM's best.
This PR is one of four. The others add copy-engine collectives for sequence sharding,
vocab_parallel_embedding, andZeRO-1/2
overlap_param_gather. The table shows where DeepSpeed ends up with them: the Qwen2.5-72B and Qwen3-32B runsuse all four options, the Qwen3-8B run uses only
overlap_param_gather. Each framework ran at its best measuredsetting, all three in one allocation per model, 8 x H200 per node, random weights, 8,192-token sequences, bf16
compute, fp32 master weights and Adam states, bf16 gradient reduction, no recompute; seconds per step, mean of steps
4-13; MFU at 989 TFLOPS per GPU. DeepSpeed runs the Hugging Face model with Liger kernels for RMSNorm, rotary
embedding and SwiGLU (applied by our benchmark script, not part of these PRs; without them DeepSpeed was 4.02 against
Megatron-LM's 3.32 s per step on Qwen3-8B without TP). Megatron-LM uses Transformer Engine's fused kernels, TorchTitan
its compile regions.
AutoTP before these options: Qwen2.5-72B 25.22 s (micro-batch 1; micro-batch 2 did not fit), Qwen3-32B 20.54 s
(TP 8, micro-batch 1).
Results
Measured with this branch, 8 x H200 per node, bf16 compute (torch autocast), fp32 master weights and Adam states,
8,192-token sequences, no activation recompute,
vocab_parallel_lm_headwith the Liger vocabulary-parallel loss, andLiger kernels for RMSNorm, rotary embedding and SwiGLU (applied by our benchmark script, not part of this PR). Master
and this branch ran in one allocation for each model. Step time is the mean of steps 4-13 of 13.
The loss the benchmark script prints after the last step (
global_loss) is within 2.5e-4 of master's in all threesetups.
Design
apply_autotp_sequence_shardingruns infinalize_autotpafter AutoTP has replaced thelinears. A block is the parent module of a row-parallel linear (
self_attn,mlp). Forward pre-hooks on the blocksgather their inputs; a pre-hook on the first decoder layer splits the hidden states and a forward hook on the last
gathers them again, so the final norm, output layer and loss see the whole sequence (combine with
vocab_parallel_lm_headto keep those sharded too).deepspeed.initialize(): every TP linear in the decoder layers belongs to a block; the blocks are inone numbered layer list and every layer has one; no column-parallel layer gathers its output; no DeepCompile
autotppass; no AutoTP layer class with its ownforward(LinearAllreduceWithReplicatedInput,LmHeadLinearAllreduce,SubParamLinearAllreduce,SubParamLinearLayer), which would keep its all-reduce. Thesequence length must be divisible by the TP size (checked in the forward pass).
work on contiguous buffers and need no reordering copy at batch > 1. The ops between blocks act on each token
alone, so any split is valid.
a gradient hook sums them over the TP group (skipped where AutoTP already registered one).
layers.pygets one branch inLinearLayer.forwardand one inLinearAllreduce.forward,engine.pyfour lines infinalize_autotp; the rest is a new module.Duplicate check
Searched issues and pull requests on 2026-10-18 for "AutoTP sequence parallel", "tensor parallel sequence parallel
reduce-scatter" and
sequence_sharding: none adds sequence sharding to AutoTP. DeepSpeed's sequence-parallel work(Ulysses, ALST, AutoSP) splits attention over ranks of its own and is a different scheme. Searched again on
2026-10-19: still none.
Related open pull request: #8851 (AutoTP mode per model) changes
add_bias(output, self.bias)inLinearAllreduce.forwardto passself.is_training_mode(). This PR changes the lines just above it, so whichever ofthe two merges second needs a one-line rebase. If #8851 merges first, the new
add_biascall in this PR'sLinearLayer.forwardbranch should passself.is_training_mode()too.The change
deepspeed/module_inject/autotp_sequence_sharding.py(new, 381 lines)ScatterToSequenceShards,GatherFromSequenceShards,GatherToTensorParallelBlock,ReduceScatterFromTensorParallelBlock,ColumnGroupFromSequenceShards), the block hooks andapply_autotp_sequence_shardingdeepspeed/module_inject/layers.py(+19, -2)LinearLayer.forwardandLinearAllreduce.forward; two class attributes onTensorParallel_Layerdeepspeed/runtime/engine.py(+5)finalize_autotpdeepspeed/runtime/tensor_parallel/config.py(+15)sequence_sharding,sequence_sharding_overlapdocs/_pages/config-json.md,docs/_tutorials/autotp-training.mdtests/unit/v1/autotp/test_autotp_sequence_sharding.py(new)Tests
TestAutoTPSequenceSharding(2 ranks, AutoTP 2, a 2-layer Qwen3 built from a config, untied; ZeRO 1 and 3; modesplainandoverlap): 3 SGD steps match the unsharded model's losses (within 1e-5), the decoder norms see half ofthe sequence on each rank, and under ZeRO-1 a decoder norm weight matches the unsharded model after the steps (its
summed gradient).
TestAutoTPSequenceShardingRejects: a sequence length not divisible by TP raises.Hardware: 1 node, 4 x H200 (driver 580.126.09), torch 2.8.0+cu126, transformers 5.15.1, pytest 9.1.1, pytest-forked
1.7.5.
In the study, with this code under another name: losses equal TP 1 within 1.9e-6 in fp32 over 5 AdamW steps at batch 1
and 2 (within 1.1e-3 in bf16, 9.0e-4 with autocast and the vocabulary-parallel loss); every parameter gradient equals
the unsharded model's within a relative 4.3e-7 to 1.1e-6 (fp32, one backward, ZeRO-0).
pre-commit run --filespasses on every changed file.Compatibility
autotppassdeepspeed.commcollectives onlyAI assistance
This change, its tests and this description were prepared with AI assistance (Claude).
🤖 Generated with Claude Code