Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ae41d13
native fp8 support for megatron-fsdp
kunlunl Nov 2, 2025
231b464
Rename low_precision.py to mixed_precision.py
kunlunl Nov 13, 2025
b4e01a8
Format
kunlunl Nov 13, 2025
838eaad
Format
kunlunl Nov 13, 2025
5d91ce9
1. Fix transpose_buffer alloc bug
shjwudp Dec 3, 2025
54afe06
Merge remote-tracking branch 'nvidia/dev' into megatron-fsdp-fp8-para…
shjwudp Dec 3, 2025
5f28b68
Revert fine-grained pre-backward hooks to lower CPU overhead
shjwudp Dec 5, 2025
c9a3758
Fix delay grad reduce
shjwudp Dec 5, 2025
c72c17b
fix reduce_scatter_gradients
shjwudp Dec 5, 2025
7507e20
fix init memory overhead
shjwudp Dec 8, 2025
07649dc
Merge pull request #3 from shjwudp/megatron-fsdp-fp8-params-jianbinc-…
kunlunl Dec 9, 2025
e983d56
Fix MoE fsdp_dtensor checkpoint issue
shjwudp Dec 12, 2025
f4f71f9
Merge pull request #4 from shjwudp/megatron-fsdp-fp8-params-jianbinc-…
kunlunl Dec 12, 2025
7000e71
Merge branch 'dev' into kunlunl/megatron-fsdp-fp8-params
kunlunl Dec 12, 2025
6e7c6ed
Fix lint error
kunlunl Dec 12, 2025
b3e181c
Fix type hint error when TE is not installed
kunlunl Dec 12, 2025
9fd51d1
Switch to all-gather fwd weights during backward to support MXFP8 act…
shjwudp Dec 13, 2025
569f3d1
Optional fine-grained all-gather for mxfp8-param-gather support
shjwudp Dec 15, 2025
5c19891
fix ..
shjwudp Dec 15, 2025
66117f1
Merge branch 'kunlunl/megatron-fsdp-fp8-params' into megatron-fsdp-fp…
kunlunl Dec 15, 2025
240880d
Merge pull request #5 from shjwudp/megatron-fsdp-fp8-params-jianbinc-…
kunlunl Dec 15, 2025
7fbc543
Merge branch 'dev' into kunlunl/megatron-fsdp-fp8-params
kunlunl Dec 15, 2025
b32273c
fix bug on HSDP
shjwudp Dec 16, 2025
01666cf
Merge branch 'kunlunl/megatron-fsdp-fp8-params' of https://github.com…
shjwudp Dec 16, 2025
bcc9da4
Merge pull request #6 from shjwudp/megatron-fsdp-fp8-params-jianbinc-…
kunlunl Dec 16, 2025
d8192d7
Fix blockwise FP8 cast main weight to model weight shard
shjwudp Dec 16, 2025
02cda20
fix memory clean-up
shjwudp Dec 16, 2025
313f884
Merge pull request #7 from shjwudp/megatron-fsdp-fp8-params-jianbinc-…
kunlunl Dec 17, 2025
e8582ee
Merge branch 'dev' into kunlunl/megatron-fsdp-fp8-params
kunlunl Dec 17, 2025
71e1534
Remove unused import
kunlunl Dec 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 51 additions & 24 deletions megatron/core/distributed/fsdp/src/megatron_fsdp/megatron_fsdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@
import torch.nn as nn
from torch.utils._pytree import tree_flatten, tree_map, tree_unflatten

from .mixed_precision import (
fp8_create_transpose_cache,
fp8_discard_transpose_cache,
is_float8tensor,
)
from .param_and_grad_buffer import (
AllGatherPipeline,
BucketingPolicy,
GradReducePipeline,
ParamAndGradBuffer,
PrefetchOrder,
override_sharded_param_methods_with_safety_checks,
to_local_if_dtensor,
)
from .utils import FSDPDistributedIndex

logger = logging.getLogger(__name__)
Expand All @@ -34,23 +48,12 @@
from megatron.core.distributed.distributed_data_parallel_config import (
DistributedDataParallelConfig,
)
from megatron.core.fp8_utils import is_float8tensor
from megatron.core.utils import is_submodule
except ImportError:
# Megatron-LM is not installed, use Megatron-FSDP as a standalone module.
logger.info("Megatron Core is not installed, Megatron-FSDP will run without Megatron Core.")
from .distributed_data_parallel_config import DistributedDataParallelConfig
from .utils import is_float8tensor, is_submodule

from .param_and_grad_buffer import (
AllGatherPipeline,
BucketingPolicy,
GradReducePipeline,
ParamAndGradBuffer,
PrefetchOrder,
override_sharded_param_methods_with_safety_checks,
to_local_if_dtensor,
)
from .utils import is_submodule


class TrainingState(Enum):
Expand Down Expand Up @@ -400,6 +403,7 @@ def all_gather_and_wait_parameters_ready(
prefetch=True,
prefetch_order=PrefetchOrder.FORWARD_PASS_ORDER,
wait_bucket_ready=True,
bwd=False,
):
"""
All-gather parameters across the data parallel group and wait for
Expand All @@ -426,11 +430,14 @@ def all_gather_and_wait_parameters_ready(
and self.ddp_config.outer_dp_sharding_strategy != "no_shard"
and (self.microbatch_count == 0 or self.model_auto_sync)
),
bwd=bwd,
)
if wait_bucket_ready:
for param in params:
bucket_id = self.param_and_grad_buffer.param_to_param_group[param]
ag_pipeline.wait_bucket_ready(bucket_id)
ag_pipeline.wait_bucket_ready(bucket_id, bwd)
if bwd and is_float8tensor(param):
fp8_create_transpose_cache(param)

for param in params:
# This setting is needed to make FSDP store the weight object when used
Expand Down Expand Up @@ -489,19 +496,18 @@ def _register_fsdp_hooks(self, root_module):
"""
fsdp_unit_modules = self.fsdp_unit_modules

def release_module_parameters(module, *unused):
def release_module_parameters(module, bwd, *unused):
for param in module.parameters():
bucket_id = self.param_and_grad_buffer.param_to_param_group[param]
self.all_gather_pipeline.release_bucket(bucket_id)
self.all_gather_pipeline.release_bucket(bucket_id, bwd)

if not self.ddp_config.keep_fp8_transpose_cache:
release_params_fp8_transpose_cache(module.parameters())

def release_params_fp8_transpose_cache(params):
for param in params:
if is_float8tensor(param):
param._transpose_invalid = True
param._transpose = None
fp8_discard_transpose_cache(param)

def _grad_acc(param):
"""
Expand Down Expand Up @@ -558,7 +564,7 @@ def _post_backward(module, *unused):
if self.ddp_config.data_parallel_sharding_strategy == "optim_grads_params":
# Deallocate the module parameters after the backward pass,
# because we have our data-parallel gradients computed.
release_module_parameters(module)
release_module_parameters(module, bwd=True)
module._training_state = TrainingState.IDLE
param_list = list(module.parameters())
else:
Expand Down Expand Up @@ -614,6 +620,10 @@ def _pre_forward_param_unshard(
# that are not FSDP units. Do not recurse unless absolutely necessary,
# to allocate as little memory as possible for this forward pass.
param_list = list(module.parameters(recurse=False))
# TODO(mxfp8): Do we really need this?
self.all_gather_and_wait_parameters_ready(
params=param_list, prefetch=False, bwd=True
)

# All-gather the parameters before the forward pass.
self.all_gather_and_wait_parameters_ready(
Expand Down Expand Up @@ -726,7 +736,9 @@ def _pre_backward(module: nn.Module, *unused):
if isinstance(module, tuple(fsdp_unit_modules)):
# All-gather / unshard the module parameters before the backward pass.
self.all_gather_and_wait_parameters_ready(
list(module.parameters()), prefetch_order=PrefetchOrder.BACKWARD_PASS_ORDER
list(module.parameters()),
prefetch_order=PrefetchOrder.BACKWARD_PASS_ORDER,
bwd=True,
)

self._root_pre_backward_hook_issued = False
Expand Down Expand Up @@ -754,7 +766,9 @@ def _root_pre_backward(module: nn.Module, *unused):
for bucket_id in range(ag_pipeline.num_buckets):
group = self.param_and_grad_buffer.parameter_groups[bucket_id]
if group.fsdp_unit_id is not None:
ag_pipeline.bucket_can_be_released[bucket_id] = True
ag_pipeline.bucket_can_be_released[
ag_pipeline.get_bucket_key(bucket_id, bwd=False)
] = True
# Track parameters that require gradient reduction and optimization.
self._params_require_handle_grad = set()
for param_group in self.param_and_grad_buffer.parameter_groups:
Expand All @@ -777,7 +791,7 @@ def _post_forward(module: nn.Module, input: Any, output: Any):
return output

# Release the module parameters after the forward pass to save memory.
release_module_parameters(module)
release_module_parameters(module, bwd=False)
module._training_state = TrainingState.IDLE

return output
Expand Down Expand Up @@ -985,17 +999,30 @@ def start_param_sync(self, *unused, force_sync: bool = False, force_dispatch: bo
else:
self.synchronize_param_gather()
for bucket_id in range(self.all_gather_pipeline.num_buckets):
self.all_gather_pipeline.async_bucket_gather(bucket_id=bucket_id)
group = self.param_and_grad_buffer.parameter_groups[bucket_id]

self.all_gather_pipeline.async_bucket_gather(bucket_id=bucket_id, bwd=False)
# TODO(mxfp8): Is this correct?
if group.transpose_weight_buffer is not None:
self.all_gather_pipeline.async_bucket_gather(bucket_id=bucket_id, bwd=True)

if group.model_weight_buffer is None:
continue

if group.model_weight_buffer.is_data_distributed:
# If model weight is sharded, we wait for the all-gather to complete and
# then release the bucket immediately to save memory usage.
self.all_gather_pipeline.wait_bucket_ready(bucket_id)
self.all_gather_pipeline.wait_bucket_ready(bucket_id, False)
# TODO(mxfp8): Is this correct?
if group.transpose_weight_buffer is not None:
self.all_gather_pipeline.wait_bucket_ready(bucket_id, True)

for bucket_id in range(self.all_gather_pipeline.num_buckets):
self.all_gather_pipeline.wait_bucket_ready(bucket_id)
group = self.param_and_grad_buffer.parameter_groups[bucket_id]
self.all_gather_pipeline.wait_bucket_ready(bucket_id, False)
# TODO(mxfp8): Is this correct?
if group.transpose_weight_buffer is not None:
self.all_gather_pipeline.wait_bucket_ready(bucket_id, True)

def start_grad_sync(self, *unused):
"""
Expand Down
Loading
Loading