Skip to content

FSDP orchestration: apply + loading/saving - #46990

Closed
3outeille wants to merge 139 commits into
mainfrom
split/a-pr-4-fsdp-orchestration
Closed

3outeille wants to merge 139 commits into
mainfrom
split/a-pr-4-fsdp-orchestration

Conversation

@3outeille

@3outeille 3outeille commented Jul 1, 2026

Copy link
Copy Markdown
Member

CI

Summary

  • FSDP:
    • Only 1 model for now has base_fsdp_plan. Will do another PR to edit every other models later
    • now wired through from_pretrained
    • For FSDP: loading through shard-on-Read + saving like TP (DCP optional)
    • Add FSDP Ci
    • DistributedMixin
  • TP:
    • Wired DistributedConfig everywhere (no more tp_plan=auto)
    • TP left untouched (no Dtensor yet)

Stack

3outeille and others added 30 commits June 17, 2026 03:51
Move FSDP2 wrapping and plan verification to distributed/fsdp.py, keep
integrations/fsdp.py as a backward-compatible re-export, and update core
call sites to import from transformers.distributed.fsdp.
naming

Co-authored-by: Arthur <48595927+ArthurZucker@users.noreply.github.com>
@3outeille

Copy link
Copy Markdown
Member Author

flagging #47253 for here

Base automatically changed from split/a-pr-3-dual-path-loading to main July 13, 2026 04:02
@github-actions

Copy link
Copy Markdown
Contributor

[For maintainers] Suggested jobs to run (before merge)

run-slow: cohere2_moe, deepseek_v4, glm_moe_dsa, gpt_oss

@github-actions

Copy link
Copy Markdown
Contributor

CI recap

Dashboard: View test results in Grafana
Latest run: 29395720891:1
Result: success | Jobs: 16 | Tests: 169,443 | Failures: 0 | Duration: 14h 13m

@3outeille

3outeille commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

This PR has been split into 2 smaller stacked PRs (stack order #47352#47357) for easier review:

Closing this PR in favor of the stack above.

@ArthurZucker ArthurZucker left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overall LGTM, would like to remove onliners, many are introduced where. we might not need them!

Comment on lines +3452 to +3458
if self._tp_size is not None:
state_dict, needs_dist_barrier = self._gather_tp_state_dict_for_save(
state_dict, is_checkpoint_writer=save_on_this_rank
)
elif is_fsdp_managed_module(model_to_save):
state_dict, needs_dist_barrier = self._gather_fsdp_state_dict_for_save(model_to_save)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be a single distributed function with the if else modeling does not care

"metadata": {"total_parameters": self.num_parameters(), **state_dict_split.metadata},
"weight_map": weight_map,
}
# TODO: it would be very nice to do the writing concurrently, but safetensors never releases the GIL,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"torch",
docker_image=[{"image": "huggingface/transformers-torch-light"}],
marker="not generate",
marker="not (generate or is_training_test or is_tensor_parallel_test or is_fsdp_test)",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wowwww nice catch


distributed_config = DistributedConfig(enable_expert_parallel=True)
distributed_config = DistributedConfig(
tp_size=int(os.environ["WORLD_SIZE"]),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you actually need this?

raise ValueError(
"FSDP+TP is not supported yet. "
"Use DistributedConfig(fsdp_size=N) or DistributedConfig(tp_size=N), not both. "
"2D support will come soon."

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

link the PR, maybe next release?!

Comment on lines +81 to +88
if not is_torch_available():
raise RuntimeError("PyTorch is required to use DistributedConfig.")

if not torch.distributed.is_available() or not torch.distributed.is_initialized():
raise RuntimeError(
"torch.distributed must be initialized before using DistributedConfig with tp_size > 1 or "
"fsdp_size > 1. Call dist.init_process_group(...) first, or launch with torchrun."
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file can't be imported already no?

elif distributed_config.fsdp_size > 1:
device_map, device_mesh = initialize_fully_sharded_data_parallelism(distributed_config)

distributed_config.validate()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weird that we validate last

Comment on lines +255 to +274
def _gather_tp_state_dict_for_save(
self,
local_state_dict: dict,
*,
is_checkpoint_writer: bool = True,
) -> tuple[dict, bool]:
"""All-gather TP-sharded weights for checkpoint writing."""
full_state_dict = gather_state_dict_for_save(local_state_dict, self._tp_plan, self._device_mesh, self._tp_size)
if not is_checkpoint_writer:
full_state_dict = {}
return full_state_dict, True

def _gather_fsdp_state_dict_for_save(self, model_to_save) -> tuple[dict, bool]:
"""Gather FSDP-sharded weights to full CPU tensors on rank 0."""
if not _is_torch_distributed_initialized():
raise ValueError(
"Saving an FSDP-wrapped model requires torch.distributed to be initialized. "
"Call save_pretrained from every rank after init_process_group."
)
return gather_full_state_dict(model_to_save), True

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loads of one liners, we usually avoid them when possible

Comment on lines +3642 to +3647
# `gather_full_state_dict` concentrates the full state on rank 0 only;
# other ranks then loop over an empty shard list and would race ahead
# of rank 0's safetensors writes. Barrier so any subsequent
# `from_pretrained` on this path sees the consolidated files.
if needs_dist_barrier:
_distributed_barrier()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure this should be here

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.

4 participants