Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
50 changes: 29 additions & 21 deletions megatron/core/distributed/distributed_data_parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from .. import parallel_state
from ..config_logger import has_config_logger_enabled, log_config_to_disk
from ..fp8_utils import is_float8tensor
from ..fp8_utils import is_float8tensor, post_all_gather_processing
from ..process_groups_config import ProcessGroupCollection
from ..transformer.cuda_graphs import is_graph_capturing
from ..transformer.transformer_config import TransformerConfig
Expand Down Expand Up @@ -500,26 +500,34 @@ def start_param_sync(self, *unused, force_sync: bool = False, force_dispatch: bo

for bucket_group in self.bucket_groups + self.expert_parallel_bucket_groups:
bucket_group.start_param_sync(force_sync=force_sync)
# For MXFP8 params, we need to copy the all-gathered param data from the buffer to
# the param.data, since param buffer is not mapped to model params for MXFP8 case.
# The paramaters are cast from bf16 to MXFP8 during copy.
# In the case of "overlap_param_gather=True", the param copy is done
# in "finish_param_sync" stage after zeroing the shared gardient buffers.
if (
self.ddp_config.reuse_grad_buf_for_mxfp8_param_ag
and not self.ddp_config.overlap_param_gather
):
for bucket in bucket_group.buckets:
for param in bucket.params:
param_start, param_end = bucket.param_to_index[param]
param_slice = bucket.param_data.view(-1)[param_start:param_end]
param.data.copy_(param_slice.view(param.data.shape))
# All-gathered params are not needed after being copied to param.data.
# Zero out the param buffer (shared with grad buffer) for gradient accumulation.
# We cannot zero out the entire grad buffer because one grad buffer may
# correspond to multiple param buffers. If we zero out the entire grad buffer,
# it would clear the data of those param buffers that have not yet completed AG.
bucket.param_data.zero_()

if not self.ddp_config.overlap_param_gather:
# For MXFP8 params, we need to copy the all-gathered param data from the buffer to
# the param.data, since param buffer is not mapped to model params for MXFP8 case.
# The paramaters are cast from bf16 to MXFP8 during copy.
# In the case of "overlap_param_gather=True", the param copy is done
# in "finish_param_sync" stage after zeroing the shared gardient buffers.
if self.ddp_config.reuse_grad_buf_for_mxfp8_param_ag:
for bucket in bucket_group.buckets:
for param in bucket.params:
param_start, param_end = bucket.param_to_index[param]
param_slice = bucket.param_data.view(-1)[param_start:param_end]
param.data.copy_(param_slice.view(param.data.shape))
# All-gathered params are not needed after being copied to param.data.
# Zero out the param buffer (shared with grad buffer) for gradient
# accumulation. We cannot zero out the entire grad buffer because one grad
# buffer may correspond to multiple param buffers. If we zero out the entire
# grad buffer, it would clear the data of those param buffers that have not
# yet completed AG.
bucket.param_data.zero_()
else:
fp8_params = []
for bucket in bucket_group.buckets:
for param in bucket.params:
if is_float8tensor(param):
fp8_params.append(param)
if len(fp8_params) > 0:
post_all_gather_processing(fp8_params)

def start_grad_sync(self, *unused):
"""
Expand Down
20 changes: 15 additions & 5 deletions megatron/core/distributed/param_and_grad_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@
from megatron.core.process_groups_config import ProcessGroupCollection
from megatron.core.rerun_state_machine import get_rerun_state_machine

from ..fp8_utils import is_float8tensor, is_mxfp8tensor, modify_underlying_storage
from ..fp8_utils import (
is_float8tensor,
is_mxfp8tensor,
modify_underlying_storage,
post_all_gather_processing,
)
from ..utils import is_torch_min_version, log_on_each_pipeline_stage
from .distributed_data_parallel_config import DistributedDataParallelConfig
from .reduce_scatter_with_fp32_accumulation import reduce_scatter_with_fp32_accumulation
Expand Down Expand Up @@ -311,10 +316,7 @@ def finish_param_sync(self, skip_next_bucket_dispatch: bool = False):
# For the mxfp8_param with "reuse_grad_buf_for_mxfp8_param_ag=True",
# we need to copy the param_data from the shared_param/grad_buffer to param.data
# after the param all-gather.
if (
self.ddp_config.reuse_grad_buf_for_mxfp8_param_ag
and self.ddp_config.overlap_param_gather
):
if self.ddp_config.reuse_grad_buf_for_mxfp8_param_ag:
for bucket in self.buckets:
for param in bucket.params:
param_start, param_end = bucket.param_to_index[param]
Expand All @@ -326,6 +328,14 @@ def finish_param_sync(self, skip_next_bucket_dispatch: bool = False):
# correspond to multiple param buffers. If we zero out the entire grad buffer,
# it would clear the data of those param buffers that have not yet completed AG.
bucket.param_data.zero_()
else:
fp8_params = []
for bucket in self.buckets:
for param in bucket.params:
if is_float8tensor(param):
fp8_params.append(param)
if len(fp8_params) > 0:
post_all_gather_processing(fp8_params)

def start_grad_sync(self):
"""
Expand Down
31 changes: 30 additions & 1 deletion megatron/core/fp8_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@
Fp8Padding = None
Fp8Unpadding = None

try:
from transformer_engine.pytorch.tensor.utils import (
post_all_gather_processing as te_post_all_gather_processing,
)
except ImportError:
te_post_all_gather_processing = None


def is_float8tensor(tensor: torch.Tensor) -> bool:
"""Check if a tensor is a Transformer Engine Float8Tensor.
Expand Down Expand Up @@ -172,7 +179,15 @@ def _quantize_param_shard_impl(
raise NotImplementedError(
f"FSDP with --fp8-param-gather is not supported in TE v{get_te_version()}"
)
cast_master_weights_to_fp8(*args)

# For newer TE versions (i.e., have post_all_gather_processing function), we keep the
# columnwise data and manually call post_all_gather_processing after all-gather, this
# makes fp8 params compatible with CUDA graph.
kwargs = {}
if te_post_all_gather_processing is not None:
kwargs["manual_post_all_gather_processing"] = True

cast_master_weights_to_fp8(*args, **kwargs)

def _correct_amax_history_if_needed_impl(model: List[torch.nn.Module]) -> None:
pass
Expand Down Expand Up @@ -406,6 +421,20 @@ def correct_amax_history_if_needed(model: List[torch.nn.Module]):
_correct_amax_history_if_needed_impl(model)


def post_all_gather_processing(model_params):
"""
Post-processing after all-gather for weights in distributed optimizer.
- tensorwise: may need to create a transposed view to match backend GEMM.
- blockwise: create column-wise storage.
"""
if te_post_all_gather_processing is not None:
te_post_all_gather_processing(model_params)
else:
# If the TE version is old and does not have post_all_gather_processing function, this is
# a no-op, and the transpose/columnwise data will be created in the next forward pass.
pass


def is_first_last_bf16_layer(config: TransformerConfig, layer_no: int):
"""Check if the layer is in bf16."""
num_bf16_layers_at_start = (
Expand Down
2 changes: 1 addition & 1 deletion megatron/core/transformer/transformer_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,7 @@ class TransformerConfig(ModelParallelConfig):
cuda_graph_scope: Optional[List[str]] = None
"""Determines the CUDA graphs capturing scope.
When cuda_graph_impl is set to "transformer_engine", valid values are "attn", "mlp", "moe",
"moe_router", "moe_preprocess", "mamba". None means ["attn", "mlp"].
"moe_router", "moe_preprocess", "mamba". None means the full layer.
When cuda_graph_impl is set to "local", "full_iteration" can be specified as cuda_graph_scope
to enable whole iteration CUDA graph. All other values enable layerwise CUDA graph."""

Expand Down
Loading
Loading