Handle empty FSDP shards in sharded loading: crash and hang fix - #48237
Handle empty FSDP shards in sharded loading: crash and hang fix#48237qgallouedec wants to merge 5 commits into
Conversation
Uneven FSDP sharding can assign a rank an EMPTY local shard (e.g. 2 experts chunked over fsdp=4 leave the last ranks zero rows; at larger scale, GLM-4.6's 20 local experts over fsdp=8 leave fsdp rank 7 empty). Two things then go wrong while loading: 1. The conversion ops receive zero collected pieces for the parameter and raise (torch.cat / torch.stack of an empty list) - fatal at the end of loading on those ranks. Fixed by skipping the mapping when every piece was dropped by the sharding operation: the pre-sharded empty local tensor installed at init is already correct. 2. Those params are never marked _is_hf_initialized, so _initialize_missing_keys runs _init_weights on them - whose first DTensor RNG op is a mesh-wide collective - while fully-loaded ranks skip it: mismatched collectives, and the group hangs silently (0% GPU, all ranks in R state). Fixed by marking empty-local DTensors before the sweep - an empty shard has nothing to initialize. Reproduces on 4 GPUs with a 0.2M-param toy (2 experts, fsdp_size=4): crash on the empty ranks, watchdog abort on the rest. At scale this froze three multi-hour 357B training runs (ep=8 x fsdp=8) before being root-caused.
|
The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update. |
CI recapDashboard: View test results in Grafana |
ArthurZucker
left a comment
There was a problem hiding this comment.
It does make sense to have, I don't think it fixed your issue at core tho, we should expect a better expert sharding to leverage all GPUs no? (at least putting less expert on other and reducing idle?)
| if getattr(self, "_device_mesh", None) is not None: | ||
| # Empty local shards have nothing to initialize; without the mark, running _init_weights on them issues collectives the other ranks never join (hang) | ||
| import itertools | ||
|
|
||
| from torch.distributed.tensor import DTensor | ||
|
|
||
| for param_or_buffer in itertools.chain(self.parameters(), self.buffers()): | ||
| if isinstance(param_or_buffer, DTensor) and param_or_buffer._local_tensor.numel() == 0: | ||
| param_or_buffer._is_hf_initialized = True | ||
|
|
There was a problem hiding this comment.
the better fix is to go in https://github.com/huggingface/transformers/blob/qwen3_vl_moe_tp_plan/src/transformers/core_model_loading.py#L1388-L1388 and if a tensor is empty + tp + number 0 (its not missing so you should go into the set param) -> set the flag
Fixes loading models whose FSDP sharding leaves a rank an empty local shard, which today crashes some ranks and silently hangs the rest.
Uneven sharding makes this easy to hit: GLM-4.6 has 160 experts; ep=8 leaves 20 per EP rank, and chunking 20 over fsdp=8 gives [3,3,3,3,3,3,2,0] (every fsdp-index-7 rank stores zero expert rows). Then:
MergeModulelistreceives zero pieces andtorch.stack([])raisesstack expects a non-empty TensorList(fatal at the end of loading on those ranks)._is_hf_initialized, so_initialize_missing_keysruns_init_weightson them, whose first DTensor RNG op is a mesh-wide collective the fully-loaded ranks never join.Verified live at 64 ranks: the stuck set was ranks 56–63 (fsdp index 7).
Real-checkpoint repro: Mixtral-8x7B on 5 GPUs
Modern MoEs have too many experts to hit this at small scale (which is why it went unnoticed until 64-rank meshes), but Mixtral-8x7B has only 8: with
DistributedConfig(fsdp_size=5)the chunking is [2,2,2,2,0]. On main, loading hangs silently until the NCCL watchdog aborts the job (Watchdog caught collective operation timeout ... SeqNum=1); with this PR it loads cleanly.Minimal repro (4 GPUs, 0.2M params, ~30 s)
A 2-expert Qwen3-MoE toy with
fsdp_size=4leaves ranks 2–3 empty. On main: the empty ranks raise (torch.cat(): expected a non-empty list of Tensors: Qwen's gate/up fusion hitsConcatenatefirst; other models hittorch.stackinMergeModulelist) while the loaded ranks die on the NCCL watchdog. With this PR: loads cleanly.The fix
_initialize_missing_keys: mark DTensor params with empty locals as initializedValidated: the previously-freezing configuration (GLM-4.6, ep=8 × fsdp=8, 64 ranks) loads to completion with the fix.
Found while training 100B–753B MoEs with FSDP2 × EP (#48204, which carries the same commit).