You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Gefen's FSDP2 optimizer state cannot be checkpointed through torch.distributed.checkpoint (DCP) save()/load(), and cannot be resharded across a different GPU count / topology. It resumes only through the in-memory full-state gather path, and only on the same topology.
This is a pre-existing design limitation (documented in COMPATIBILITY.md → Optimizer checkpoint scope), filed here to track a proper fix. It is independent of the CPU-offload work in #80 — it was re-surfaced while adding a resume test there.
What doesn't work
dcp.save/dcp.load of the optimizer state fails. Gefen packs its FSDP2 optimizer state as a variable-size rank-local payload (_gefen_rank_local_payload_*) because, under fully_shard, each rank learns its own codebook and block geometry, so the per-rank state genuinely differs in size. DCP's fixed-shape load planner rejects it:
ValueError: Size mismatch between saved torch.Size([15484]) and current:
torch.Size([2861]) for optimizer.state.0.weight._gefen_rank_local_payload_0
(15484 = a warmed optimizer's payload; 2861 = a fresh template's — the planner requires them to match.)
Consequences:
No sharded / parallel optimizer-state disk checkpoint (each rank writing its own shard in parallel).
No optimizer-state resharding — save on N GPUs, resume on M is not supported; resume requires the same GPU count and sharding layout.
What does work (supported path)
Model weights via DCP — unaffected; weights are ordinary DTensors, so dcp.save/load of the model shards works normally.
Inherent to the quantized-codebook design under FSDP2: per-rank heterogeneous codebooks/geometry ⇒ variable-size per-rank state ⇒ incompatible with DCP's uniform, fixed-shape, shard-addressable planner. The rank-local blob is the current workaround that preserves correctness at the cost of DCP-shardability.
Impact
Fine-tuning / same-topology resume: fine — the full-state path covers it.
Large-scale distributed pretraining with elastic / resharded checkpoints: blocked.
Checkpoint-time memory: the full-state gather concentrates the entire optimizer state on rank-0 CPU (~1 byte/param, e.g. ~70 GB for a 70B model).
Possible direction
Give the FSDP2 optimizer state a DCP-compatible representation — fixed-shape per-rank tensors plus a resharding-aware planner / Stateful implementation — so dcp.save/load and N→M resharding work. There is an existing unmerged branch, feat/dtensor-portable-resharding ("Add sharded portable DCP resharding", "Prove checkpoint resume from two ranks to four"), that appears to target exactly this; it should be validated and landed (or superseded).
Acceptance criteria
dcp.save/dcp.load round-trips the FSDP2 optimizer state without a size-mismatch error.
Optimizer-state resume across a changed GPU count (e.g. 2 → 4) is exact.
Checkpoint-time memory does not require gathering the full optimizer state onto a single rank.
Regression test exercising a real dcp.save/dcp.load disk round trip (currently impossible, hence the in-memory path in tests/test_fsdp2_cpu_offload.py / test_gefen_fsdp2_checkpoint.py).
Summary
Gefen's FSDP2 optimizer state cannot be checkpointed through
torch.distributed.checkpoint(DCP)save()/load(), and cannot be resharded across a different GPU count / topology. It resumes only through the in-memory full-state gather path, and only on the same topology.This is a pre-existing design limitation (documented in
COMPATIBILITY.md→ Optimizer checkpoint scope), filed here to track a proper fix. It is independent of the CPU-offload work in #80 — it was re-surfaced while adding a resume test there.What doesn't work
dcp.save/dcp.loadof the optimizer state fails. Gefen packs its FSDP2 optimizer state as a variable-size rank-local payload (_gefen_rank_local_payload_*) because, underfully_shard, each rank learns its own codebook and block geometry, so the per-rank state genuinely differs in size. DCP's fixed-shape load planner rejects it:(15484 = a warmed optimizer's payload; 2861 = a fresh template's — the planner requires them to match.)
Consequences:
What does work (supported path)
dcp.save/loadof the model shards works normally.get_state_dict(full_state_dict=True, cpu_offload=True)→torch.save→torch.load→set_state_dict(broadcast_from_rank0=True). Same-topology only. This is whattests/test_gefen_fsdp2_checkpoint.pyand the PR CPU-offloaded training compatability: CPU-resident stepping + DeepSpeed ZeRO CPU-offload #80 resume test use.Why
Inherent to the quantized-codebook design under FSDP2: per-rank heterogeneous codebooks/geometry ⇒ variable-size per-rank state ⇒ incompatible with DCP's uniform, fixed-shape, shard-addressable planner. The rank-local blob is the current workaround that preserves correctness at the cost of DCP-shardability.
Impact
Possible direction
Give the FSDP2 optimizer state a DCP-compatible representation — fixed-shape per-rank tensors plus a resharding-aware planner /
Statefulimplementation — sodcp.save/loadand N→M resharding work. There is an existing unmerged branch,feat/dtensor-portable-resharding("Add sharded portable DCP resharding", "Prove checkpoint resume from two ranks to four"), that appears to target exactly this; it should be validated and landed (or superseded).Acceptance criteria
dcp.save/dcp.loadround-trips the FSDP2 optimizer state without a size-mismatch error.dcp.save/dcp.loaddisk round trip (currently impossible, hence the in-memory path intests/test_fsdp2_cpu_offload.py/test_gefen_fsdp2_checkpoint.py).