[fsdp] fix: make deferred gradient sync configurable - #7458
Conversation
Preserve the current communication-optimized default while allowing memory-constrained jobs to synchronize and reshard gradients after every micro-batch. Keep legacy engine configs on the current deferred-sync behavior. Signed-off-by: Mengyuyang <781650427@qq.com>
|
failed my training because of the same issue。。。 |
|
Measured what the deferral actually costs, in case it helps the docs paragraph land more concretely. The retained buffer is fp32, not the bf16 gradient. if (self.reduce_dtype is None or unsharded_param is None
or unsharded_param.grad is None
or unsharded_param.grad.dtype == self.reduce_dtype):
return
self.unsharded_accumulated_grad = unsharded_grad.to(self.reduce_dtype)bf16 never equals fp32, so every parameter gets an upcast copy. The part that surprised me is that it does not scale out. Two gloo ranks up to eight, 262,144-parameter model, bf16 params / fp32 reduce, measured after the first non-final micro-batch: The shard halves per rank as FSDP promises; the retained gradient stays at the whole model in fp32, so at ws=8 it is 8x the parameter shard on the same card. That works out to 4 bytes per parameter regardless of topology — 108 GiB a rank at 27B, which is more than the "slightly increases peak device memory" the current docs suggest and is why this flag matters. Reproducer if useful: model.set_requires_gradient_sync(False)
model(x).sum().backward()
held = sum(
p.unsharded_accumulated_grad.numel() * p.unsharded_accumulated_grad.element_size()
for m in model.modules()
if (s := getattr(m, "_get_fsdp_state", None)) and s()._fsdp_param_group
for p in s()._fsdp_param_group.fsdp_params
if p.unsharded_accumulated_grad is not None
)Worth asserting the 4-bytes-per-parameter figure in the test file rather than only the context-manager behaviour? It would pin the reason the flag exists, and it runs on CPU. |
What does this PR do?
Complete the configuration contract for deferred FSDP gradient synchronization introduced by #7095.
The merged implementation currently defers synchronization on every non-final micro-batch, but it does not expose the documented
use_no_sync_for_gradient_accumulationsetting. This PR adds the missing FSDP engine option and keeps its defaulttrueto preserve the current runtime behavior. Memory-constrained jobs can explicitly set it tofalseto synchronize and reshard gradients after every micro-batch.The change supports both FSDP1 and FSDP2, preserves forward-only behavior, and retains the current deferred-sync behavior for legacy engine config objects that do not define the new field.
Related: #7095, #6010.
Checklist Before Starting
[fsdp] fix: make deferred gradient sync configurable.mainstill has the unconditional deferred-sync path and does not defineuse_no_sync_for_gradient_accumulation.Test
Focused CPU tests that do not depend on the local two-rank Gloo backend:
python -m pytest -q \ tests/workers/test_fsdp_gradient_accumulation_sync_on_cpu.py \ tests/workers/config/test_engine_config_on_cpu.py \ -k "not distributed_accumulation_matches_per_micro_batch_sync"Result:
Additional validation completed:
truefor the actor, reference model, and critic defaults.false.git diff --checkpassed.The real two-rank Gloo equivalence test was attempted on Windows, but the local PyTorch backend failed during
init_process_group()withmakeDeviceForHostname(): unsupported gloo device, before entering the FSDP code under test. It remains enabled for Linux CI.The complete pre-commit suite is not marked as passed locally because the current upstream tree contains root-level
tests/test_*files rejected by the repository'svalidate-structurehook. This PR does not add or modify those files.API and Usage Example
No existing CLI or Python API is removed. The new FSDP engine option defaults to
true, matching the current merged runtime behavior.With
Mmicro-batches,truereduces gradient synchronization fromMrounds to one, but temporarily retains unsharded gradients and can increase peak device memory. Setting the option tofalsetrades additional gradient communication for lower peak memory. When there is only one micro-batch, both settings follow the normal synchronized backward path.Design & Code Changes
use_no_sync_for_gradient_accumulation: bool = TruetoFSDPEngineConfig.no_sync()and FSDP2set_requires_gradient_sync(False)paths on the new option.getattr(..., True)compatibility fallback so legacy or subclass-specific engine config objects preserve the current deferred-sync behavior.Checklist Before Submitting
Important
Please check all the following items before requesting a review, otherwise the reviewer might deprioritize this PR for review.
pre-commit install && pre-commit run --all-files --show-diff-on-failure --color=always. Relevant changed-file hooks passed locally; see the test limitation above.ci-requestchannel in theverlSlack workspace or the linked Feishu group.recipesubmodule is not affected.