Repository navigation
Conversation
Describe, in global ranks, where ZeRO keeps each element of a live parameter, so a transfer planner can read or write training storage directly: - zero_param_storage_affine_map: low-precision replicas under ZeRO-0/1/2 (DP group, or the expert DP group for MoE) and ds_tensor partitions under ZeRO-3, excluding padding. - zero_optimizer_storage_affine_maps: the fp32 master copy and its optimizer states. ZeRO-1/2 and the bf16 optimizer gather each rank's hp fragment address with one all-gather per DP group; ZeRO-3 reuses the partition map. Frozen parameters have no map. - zero_param_storage_shard / zero_optimizer_storage_shard: flat views of ZeRO's own buffers laid out as the maps describe. Offloaded optimizer states raise NotImplementedError on every rank; MiCS and hpZ are not covered. No change to ZeRO, the planner or the executor. Signed-off-by: Jin, Youzhi <[email protected]>
A layout map says which tensor- or expert-parallel rank holds which shard; a ZeRO storage map says which data-parallel ranks store which interval of one such shard. nest_affine_maps combines the two into a map of what every process stores, so a transfer can be planned straight from runtime storage. The result describes the flattened parameter. A ZeRO partition of an N-D shard is generally not an N-D box, and plan_transfer addresses shards as boxes, so storage maps are flat. Replicated layout pieces are cut wherever any holder's storage begins or ends, and every holder is listed in locations. Co-authored-by: Copilot <[email protected]> Signed-off-by: Jin, Youzhi <[email protected]>
autotp_storage_affine_maps nests each parameter's published AutoTP layout map with the ZeRO storage of every tensor-parallel shard, giving a map of what each process stores, keyed by global rank. The shards are the ones zero_param_storage_shard and zero_optimizer_storage_shard already return, so a transfer can read from or write into a live AutoTP + ZeRO engine. Parameters AutoTP leaves whole are replicated over the tensor-parallel group. The tensor-parallel ranks exchange their shards' storage maps in one all-gather, so no rank layout is assumed. Co-authored-by: Copilot <[email protected]> Signed-off-by: Jin, Youzhi <[email protected]>
Add a phase 2 section to the affine IR spec: layout maps versus storage maps, why storage maps describe the flattened parameter, how nest_affine_maps derives them for dense, AutoTP and AutoEP parameters, replica choice, plan lifetime, and what is not described yet. Co-authored-by: Copilot <[email protected]> Signed-off-by: Jin, Youzhi <[email protected]>
nest_affine_maps described the flattened parameter, so a layout piece was cut into its contiguous runs: a row-parallel shard became one piece per row, which made planning large models slow and could exceed PlanBudget. A process's stored run of its layout shard is a partial row, whole rows, and a partial row, so describe it as at most 2 * ndim - 1 boxes in the parameter's own shape. plan_transfer now accepts such a piece in a 1-D shard when it is dense there, and treats its storage as one interval of the shard. Co-authored-by: Copilot <[email protected]> Signed-off-by: Jin, Youzhi <[email protected]>
all_gather_into_tensor on gloo requires the output to stack the inputs along their first axis; the (world, n, 3) output raised on CPU. Co-authored-by: Copilot <[email protected]> Signed-off-by: Jin, Youzhi <[email protected]>
pathlib's stat() returns os.stat_result, whose size field is st_size. Co-authored-by: Copilot <[email protected]> Signed-off-by: Jin, Youzhi <[email protected]>
autotp_rollout_e2e.py trains Qwen3-style dense models with AutoTP + ZeRO and moves the weights into rollout engines with a different TP size through a Universal checkpoint, through gathered parameters, and through ZeRO storage affine maps (plan_transfer + execute_transfers), checking every path bit for bit and recording time and peak memory per sync. A --replica_policy chooses which ZeRO-0/1/2 replica each target block is read from; the default reads locally when possible and otherwise from the least-loaded holder. Needs DeepSpeed with deepspeedai/DeepSpeed#8809 and deepspeedai/DeepSpeed#8885. Co-authored-by: Copilot <[email protected]> Signed-off-by: Jin, Youzhi <[email protected]>
A replicated layout block can be contiguous in one holder's shard and strided in another's. nest_affine_maps merged rows wherever any copy allowed it, so the box was not one run of addresses in the strided copy and nesting failed; merge only where every copy is contiguous. A scalar parameter has no axis to split, so describe it as a single 0-D box, and let plan_transfer place a 0-D piece in a flat shard. Co-authored-by: Copilot <[email protected]> Signed-off-by: Jin, Youzhi <[email protected]>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2d8d873e4f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
- autotp_storage_affine_maps: a parameter AutoTP leaves whole in a model built under zero.Init keeps its partition over the whole world, so every TP rank reports the same storage. Describe that shared copy on its own instead of nesting it once per TP rank, which was rejected. - zero_param_storage_shard: reject a gathered ZeRO-3 parameter. Persistent parameters stay gathered between steps and the forward reads that copy, so a write to the partition alone would be lost. Callers release gathered copies with engine.empty_partition_cache() first. - Reject NVMe-offloaded ZeRO-3 parameter storage, whose partition is a placeholder while swapped out. Co-authored-by: Copilot <[email protected]> Signed-off-by: Jin, Youzhi <[email protected]>
…o zero-storage-affine-map Signed-off-by: Jin, Youzhi <[email protected]> # Conflicts: # deepspeed/checkpoint/affine_ir_spec.md
|
Thanks @jinyouzhi, I went through the Why the invariant from #8624 still holds. For a flat shard, a piece's local footprint becomes the run I added adversarial cases locally against 42bafc5 and all of them behave as expected:
Executor (#8809). No change is needed for flat segments. Segments on the flat side carry the piece's own row-major strides. Three small requests:
On the numbers: |
…uffer offset On CPU, .cpu() returns the view itself, so the gathered shard kept its storage offset into the flat buffer and as_strided read the neighbouring parameter. Also pin flat target shards cut mid-row and a map mixing flat and matrix shards. Co-authored-by: Copilot <[email protected]> Signed-off-by: Jin, Youzhi <[email protected]>
Summary
Describes, as affine maps in global ranks, where a live ZeRO engine stores each element of a parameter and its optimizer states, so
plan_transfercan read from or write into training storage directly (e.g. training → rollout weight sync) without gathering full parameters first.zero_affine.pyzero_param_storage_affine_map: low-precision replicas under ZeRO-0/1/2 (DP or expert-DP group),ds_tensorpartitions under ZeRO-3, padding excluded.zero_optimizer_storage_affine_maps: fp32 master + optimizer states (ZeRO-1/2, bf16 optimizer, ZeRO-3).zero_param_storage_shard/zero_optimizer_storage_shard: flat views of ZeRO's own buffers matching the maps.autotp_storage_affine_maps: AutoTP layout × ZeRO storage, one all-gather over the TP group.affine.py:nest_affine_mapscomposes a layout map (TP/EP shard placement) with per-shard storage maps. A process's stored run of an N-D shard is described as at most2·ndim−1N-D boxes in the parameter's own shape, so plans stay small (row-parallel shards are not cut per row).affine_transfer.py:plan_transferaccepts an N-D piece in a 1-D (flat) shard only when the piece is dense there; every other rank mismatch is still refused.affine_ir_spec.md: phase-2 section on layout vs storage maps.test_convert_checkpointstat().size→st_size(from Preserve non-floating buffers in ZeRO checkpoint conversion #8515).No change to ZeRO, AutoTP or the executor. Offloaded optimizer states raise
NotImplementedError; MiCS / hpZ are not covered.Tests
tests/unit/checkpoint/test_zero_affine.py,test_affine_transfer_plan.py: ZeRO-0/1/2/3 param + optimizer maps checked against ZeRO's own buffers (bitwise rebuild), AutoTP nest, N-D box count, flat-shard acceptance/refusal.autotp_storage_affine_maps+plan_transfer+ executor ([checkpoint] Execute affine transfer plans with bounded peer staging #8809), ZeRO-2 and ZeRO-3, weights bitwise equal after every sync. Plans are built once and reused.lowestbalancedEach map has 3,192 pieces on both sides; before N-D boxes (one piece per stored row) the same ZeRO-3 maps had 592k source / 2.36M target pieces and planning took 207 s. Under ZeRO-2 every DP rank holds a replica:
lowestis the planner's default holder choice,balancedis areplica_selectorin the example (local copy first, else the least-loaded holder).