Skip to content

Universal Checkpointing: read AutoEP ZeRO-3 optimizer files one at a time - #8844

Draft
pengdurice wants to merge 1 commit into
deepspeedai:masterfrom
pengdurice:peng-fix-ucp-stream-expert-states-v1
Draft

pengdurice wants to merge 1 commit into
deepspeedai:masterfrom
pengdurice:peng-fix-ucp-stream-expert-states-v1

Conversation

@pengdurice

Copy link
Copy Markdown
Contributor

Universal Checkpointing: read AutoEP ZeRO-3 optimizer files one at a time

Problem

ds_to_universal converts a ZeRO-3 AutoEP checkpoint in two parts. The dense parameters go through the usual
extract-and-merge path, which reads one optimizer file per worker and writes fragments to tmp/. The routed experts
go through _consolidate_zero3_autoep_expert_states, which first loads every rank's optimizer file into one dict
(optim_states_by_rank) and then keeps a copy of every expert fragment in memory until all of them are merged.

Together those files hold every optimizer state of the model: fp32 (or bf16) master weights plus both Adam moments.
For a large MoE model that is more than one host has:

Model Parameters Optimizer state, fp32 master + fp32 Adam (12 B/param) bf16 master + bf16 Adam (6 B/param)
MiniMax-M3 426.2B 4.65 TiB 2.33 TiB
GLM-5.2 743.4B 8.11 TiB 4.06 TiB

The H200 nodes this was found on have 2 TiB of host memory, so the conversion cannot finish for either model.

Change

_consolidate_zero3_autoep_expert_states reads one rank's optimizer file at a time. Each expert fragment it finds is
written to <output>/tmp/autoep_expert_fragments/ (one file per parameter, optimizer state, EP rank and partition
rank) and the rank's file is released before the next one is read. The merge then loads, for one parameter and one
state at a time, only that parameter's fragments. Peak host memory becomes one optimizer file plus one full expert
tensor, instead of all optimizer files.

The checks are unchanged: missing ranks, duplicate or missing expert-data-parallel partitions, shape mismatches and
placement disagreements raise the same errors. The temporary fragments live under <output>/tmp, which the converter
already uses for the dense fragments and removes at the end.

Tests

Test Result
tests/unit/checkpoint/test_autoep_affine_universal.py (calls the function directly; CPU) 20 passed (master 1c862f7 + this change); master 5f266e2 + this change, both files together: 63 passed
tests/unit/v1/moe/test_autoep_checkpoint.py (AutoEP ZeRO-3 save, convert, load at other layouts; GPU) 43 passed

Hardware for the GPU tests: 1 node of 8 H200 (driver 580.126.09), torch 2.8.0+cu126, pytest 9.1.1.
pre-commit run --files passes on the changed file.

Not measured: the peak host memory of a full-size conversion before and after. The bound follows from the code:
before, every rank's optimizer file is loaded at once (for MiniMax-M3 with fp32 master weights and fp32 Adam states,
4.65 TiB in total, more than the 2 TiB of the H200 hosts this was found on); after, one rank's file plus one
parameter's fragments.

Duplicate check

Searched open and recent pull requests on 2026-10-15 ("universal checkpoint", "ds_to_universal", "AutoEP
checkpoint"): #8800 plans live expert transfers between placements and #8637 links ZeRO-1/2 fragments; neither
changes how ds_to_universal reads the ZeRO-3 AutoEP optimizer files.

Compatibility

Cell Validated
ZeRO-3 + AutoEP checkpoint conversion, save / convert / load at other layouts yes (tests)
Dense (non-AutoEP) ZeRO-3 conversion path unchanged
ZeRO-1 / ZeRO-2 conversion path unchanged

AI assistance

This change, its tests and this description were prepared with AI assistance (Claude).

…time

Signed-off-by: pengdurice <pengduhit@gmail.com>
banxingmjj pushed a commit to openanolis/DeepSpeed that referenced this pull request Oct 26, 2026
…pspeedai#8800)

## Summary

deepspeedai#8623 plans shard-to-shard copies between two `ParamAffineMap`s. Today,
though, the only AutoEP maps come from checkpoint metadata, so nothing
at runtime can feed the planner. This PR closes that gap: it builds the
source map from the placement the live `AutoEPMoELayer` actually holds,
and builds the target map from a target placement descriptor. Both maps
then go straight to `plan_transfer`.

```
AutoEP runtime placement -> source / target ParamAffineMap -> plan_transfer (deepspeedai#8623) -> copy descriptors
```

Maps of what each process actually stores under expert data parallelism
(EDP) and ZeRO are left to a follow-up, which builds them by nesting
these layout maps with ZeRO's storage maps.

Related: deepspeedai#8252 (§8.4 AutoEP expert placement), deepspeedai#8230, deepspeedai#8544.

## Changes

- `AutoEPMoELayer.expert_affine_maps()` returns the affine maps of the
live `w1`/`w2`/`w3`.
- Map ranks are EP-local ranks. The docstring explains how to translate
them with `dist.get_global_rank(self.ep_group, rank)`.
- Each shard is the unpartitioned packed `[local_experts, ...]` tensor.
ZeRO-3/EDP fragments stay outside the map, and the shape comes from
`ds_shape`.
  - No collectives.
- `AutoEPMoELayer.plan_expert_transfer(target_placement, limits=None, *,
replica_selector=None)` returns one `TransferPlan` per expert parameter.
- It only plans: it moves no data, swaps no parameters and migrates no
optimizer state.
- `autoep_affine.plan_autoep_placement_transfer(source_placement,
target_placement, logical_shape, ...)` is the layer-independent form.
Both placements go through the existing
`autoep_placement_to_affine_map`, so no second placement→map conversion
is introduced.
- Placements are compared by explicit rank IDs rather than list order
(`autoep_placements_equal`), and each parameter's published
`DS_AUTOEP_UC_META` placement and affine map must equal the live
lowering.
- The live placement must match how `repack_expert_weights` actually
packed the weights; today that is contiguous and uniform. A descriptor
that disagrees raises `NotImplementedError`, rather than producing a map
that addresses shards the weights do not have.
- A placement the descriptor can express is not necessarily one the
runtime can execute. The target placement is only used for planning.

## Not in scope

- Executing plans or transporting data (deepspeedai#8809), parameter swap,
optimizer-state migration.
- Storage maps of EDP replicas and ZeRO-3 fragments (follow-up PR), and
replica selection policy (deepspeedai#8252).
- Placement generation/versioning: the runtime does not change placement
after construction yet.
- Runtime support for non-uniform placements.

## Tests

`tests/unit/v1/moe/test_autoep_unit.py`:
- `test_live_expert_maps_address_the_packed_runtime_weights`: for each
rank, the map's shard shape and contents match the real packed
`w1/w2/w3`. The reference is the full tensor from
`repack_expert_weights(ep_size=1)`, which does not depend on any affine
map.
-
`test_plan_expert_transfer_moves_live_shards_into_target_packed_order`:
from EP=2 into 3 ranks with reordered, uneven, empty and replicated
placements; executing the plan reproduces the target shards, every
element is written once, and planning never calls `rebuild`/`extract`.
- `test_live_expert_maps_reject_a_placement_the_weights_do_not_have`,
`test_live_expert_maps_identify_placement_ranks_by_id`,
`test_live_expert_maps_reject_a_published_map_the_placement_does_not_produce`.

`tests/unit/checkpoint/test_autoep_affine.py`:
`test_placements_equal_identifies_ranks_by_id`.

Results (Intel Arc Pro B70):
- `test_autoep_unit.py`, `test_autoep_affine.py`,
`test_autoep_affine_universal.py`, `test_affine_transfer_plan.py`,
`test_affine_shard_map.py`, `test_autoep_zero3_metadata.py`: 432 passed,
6 skipped.
- `test_autoep_checkpoint.py` (8× B70): 32 passed, 2 skipped.


## Contribution checklist

**Related work.**
- This PR builds on deepspeedai#8623 (`plan_transfer`).
- Plan execution is deepspeedai#8809; replica policy is deepspeedai#8252.
- deepspeedai#8844 and deepspeedai#8848 change Universal Checkpointing for AutoEP. They do not
produce runtime maps.

A search for AutoEP placement and transfer and for affine maps found no
overlapping PR.

**Commands and results.** Hardware and software: Intel Arc Pro B70 (XPU,
XCCL), Level Zero driver 1.15.39122 (compute runtime 26.27.39122.11),
torch 2.14.0.dev20260804+xpu.
```
pytest -p no:randomly tests/unit/v1/moe/test_autoep_unit.py tests/unit/checkpoint/test_autoep_affine.py \
  tests/unit/checkpoint/test_autoep_affine_universal.py tests/unit/checkpoint/test_affine_transfer_plan.py \
  tests/unit/checkpoint/test_affine_shard_map.py tests/unit/checkpoint/test_autoep_zero3_metadata.py
pytest -p no:randomly tests/unit/v1/moe/test_autoep_checkpoint.py
```
Results are listed under Tests above.

**Numerics, performance, memory.** Training is not affected. The new
methods only run when called and use no collectives.

**Compatibility matrix.**

| | Validated | Not validated |
|---|---|---|
| ZeRO | Independent: maps describe the unpartitioned packed tensors |
Storage under EDP/ZeRO (follow-up) |
| Parallelism | EP=2; target placements on 3 ranks | |
| Precision | Dtype-independent: maps describe shapes and offsets | |
| Accelerator | XPU | CUDA, HPU |

**AI assistance.** This PR was developed with AI assistance (GitHub
Copilot CLI). I reviewed every changed line and ran the tests above.

---------

Signed-off-by: Jin, Youzhi <youzhi.jin@intel.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant