-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Support GEMM + Swiglu fused MLP #3971
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
ba650c7
Support GEMM + Swiglu fused MLP (#3890)
ksivaman 7b07521
Merge branch 'NVIDIA:main' into fused_grouped_mlp_main
ksivaman 661f6d9
Merge branch 'NVIDIA:main' into fused_grouped_mlp_main
ksivaman 2198c0d
Update megatron/core/transformer/moe/experts.py
ksivaman 619e02c
Merge branch 'main' into fused_grouped_mlp_main
ksivaman be3ec51
Merge branch 'main' into fused_grouped_mlp_main
ksivaman d2b77bc
fix lint
ksivaman d64a6bf
Merge branch 'main' into fused_grouped_mlp_main
ksivaman cb7d273
Merge branch 'main' into fused_grouped_mlp_main
Phlip79 f7e17cb
small fix
yaox12 439688e
Merge branch 'main' into fused_grouped_mlp_main
yaox12 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -243,6 +243,9 @@ def __init__( | |
| # or bucket.grad_data. | ||
| self.cached_param_buffer_shard_list = [None] * len(self.buckets) | ||
| self.cached_grad_buffer_shard_list = [None] * len(self.buckets) | ||
| # Track grad mode used to create cached param views. Rebuild if mode changes to avoid | ||
| # mixing no_grad-created views with in-place updates in grad-enabled mode. | ||
| self._cached_param_buffer_shards_grad_enabled = None | ||
|
|
||
| def reset(self): | ||
| """ | ||
|
|
@@ -403,23 +406,28 @@ def start_param_sync(self, force_sync: bool = False): | |
| # Standard distributed optimizer path: use _coalescing_manager. | ||
| # all_gather_into_tensor writes directly into a contiguous output buffer and | ||
| # does not need a copy-back step, so coalescing works correctly. | ||
| with _coalescing_manager( | ||
| self.intra_distributed_optimizer_instance_group, async_ops=async_op | ||
| ) as cm: | ||
| for idx, bucket in enumerate(self.buckets): | ||
| if self.cached_param_buffer_shard_list[idx] is None: | ||
| self.cached_param_buffer_shard_list[idx] = shard_buffer( | ||
| bucket.param_data, self.intra_distributed_optimizer_instance_size | ||
| current_grad_enabled = torch.is_grad_enabled() | ||
| if self._cached_param_buffer_shards_grad_enabled != current_grad_enabled: | ||
| self.cached_param_buffer_shard_list = [None] * len(self.buckets) | ||
| self._cached_param_buffer_shards_grad_enabled = current_grad_enabled | ||
| with torch.no_grad(): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comment on what this is trying to do? |
||
| with _coalescing_manager( | ||
| self.intra_distributed_optimizer_instance_group, async_ops=async_op | ||
| ) as cm: | ||
| for idx, bucket in enumerate(self.buckets): | ||
| if self.cached_param_buffer_shard_list[idx] is None: | ||
| self.cached_param_buffer_shard_list[idx] = shard_buffer( | ||
| bucket.param_data, self.intra_distributed_optimizer_instance_size | ||
| ) | ||
| local_data_view = self.cached_param_buffer_shard_list[idx][ | ||
| self.intra_distributed_optimizer_instance_rank | ||
| ] | ||
| dist_all_gather_func( | ||
| bucket.param_data, | ||
| local_data_view, | ||
| group=self.intra_distributed_optimizer_instance_group, | ||
| async_op=async_op, | ||
| ) | ||
| local_data_view = self.cached_param_buffer_shard_list[idx][ | ||
| self.intra_distributed_optimizer_instance_rank | ||
| ] | ||
| dist_all_gather_func( | ||
| bucket.param_data, | ||
| local_data_view, | ||
| group=self.intra_distributed_optimizer_instance_group, | ||
| async_op=async_op, | ||
| ) | ||
| if async_op: | ||
| self.param_gather_handle = cm | ||
| else: | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -361,7 +361,10 @@ def _build_model_and_main_param_groups( | |
| if model_param.type() in ['torch.cuda.HalfTensor', 'torch.cuda.BFloat16Tensor']: | ||
|
|
||
| # Generate sharded model param. | ||
| if is_float8tensor(model_param) and config.fp8_recipe != "delayed": | ||
| if ( | ||
| cls._is_distopt_quantized_param(model_param) | ||
| and config.fp8_recipe != "delayed" | ||
| ): | ||
| # MXFP8Tensor and BlockwiseQTensor don't support view(-1) | ||
| shard_model_param = None | ||
| else: | ||
|
|
@@ -381,7 +384,7 @@ def _build_model_and_main_param_groups( | |
| # precision at the beginning of training (this problem will not occur if the | ||
| # training is long enough or if the main params are loaded from a | ||
| # checkpoint). | ||
| if is_float8tensor(model_param): | ||
| if cls._is_distopt_quantized_param(model_param): | ||
| if hasattr(model_param, 'get_high_precision_init_val'): | ||
| shard_main_param = ( | ||
| model_param.get_high_precision_init_val() | ||
|
|
@@ -913,6 +916,70 @@ def _get_main_param_and_optimizer_states(self, model_param): | |
| tensors[k] = v | ||
| return tensors | ||
|
|
||
| @staticmethod | ||
| def _is_grouped_quantized_tensor(tensor: torch.Tensor) -> bool: | ||
| """Check if tensor is a TE GroupedTensor using quantized storage.""" | ||
| return ( | ||
| hasattr(tensor, "split_into_quantized_tensors") | ||
| and callable(tensor.split_into_quantized_tensors) | ||
| and getattr(tensor, "quantizer", None) is not None | ||
| ) | ||
|
|
||
| @classmethod | ||
| def _is_distopt_quantized_param(cls, tensor: torch.Tensor) -> bool: | ||
| """Check if tensor should follow quantized parameter path in dist optimizer.""" | ||
| return is_float8tensor(tensor) or cls._is_grouped_quantized_tensor(tensor) | ||
|
|
||
| def _expand_quantized_param_shard_for_cast( | ||
| self, | ||
| model_param: torch.Tensor, | ||
| shard_main_param: Optional[torch.Tensor], | ||
| start_offset: Optional[int], | ||
| ): | ||
| """Expand one quantized model param to cast-ready entries. | ||
|
|
||
| For grouped quantized tensors, split into member quantized tensors and map the sharded | ||
| master slice to per-member offset ranges, while preserving deterministic ordering across | ||
| DP ranks. | ||
| """ | ||
| if not self._is_grouped_quantized_tensor(model_param): | ||
| return [model_param], [shard_main_param], [start_offset] | ||
|
|
||
| quantized_members = model_param.quantized_tensors | ||
| if quantized_members is None: | ||
| quantized_members = model_param.split_into_quantized_tensors() | ||
|
|
||
| shard_start = 0 if start_offset is None else start_offset | ||
| shard_size = 0 if shard_main_param is None else shard_main_param.numel() | ||
| shard_end = shard_start + shard_size | ||
| shard_flat = None if shard_main_param is None else shard_main_param.view(-1) | ||
|
|
||
| expanded_model_params = [] | ||
| expanded_shard_main_params = [] | ||
| expanded_start_offsets = [] | ||
| member_offset = 0 | ||
| for member in quantized_members: | ||
| member_numel = member.numel() | ||
| member_start = member_offset | ||
| member_end = member_start + member_numel | ||
| overlap_start = max(member_start, shard_start) | ||
| overlap_end = min(member_end, shard_end) | ||
|
|
||
| member_master = None | ||
| member_start_offset = None | ||
| if overlap_start < overlap_end: | ||
| local_start = overlap_start - shard_start | ||
| local_end = overlap_end - shard_start | ||
| member_master = shard_flat[local_start:local_end] | ||
| member_start_offset = overlap_start - member_start | ||
|
|
||
| expanded_model_params.append(member) | ||
| expanded_shard_main_params.append(member_master) | ||
| expanded_start_offsets.append(member_start_offset) | ||
| member_offset = member_end | ||
|
|
||
| return expanded_model_params, expanded_shard_main_params, expanded_start_offsets | ||
|
|
||
| def _set_main_param_and_optimizer_states(self, model_param, tensors): | ||
| """Set the main param and optimizer states corresponding to the input model_param. | ||
|
|
||
|
|
@@ -2145,7 +2212,7 @@ def split_state_dict_if_needed(self, state_dict): | |
| fp8_gbuf_indices = [] | ||
| for gbuf_idx, gbuf_range_maps in enumerate(self.gbuf_ranges): | ||
| for dtype, _ in gbuf_range_maps.items(): | ||
| if is_float8tensor(self.buffers[gbuf_idx].params[0]): | ||
| if self._is_distopt_quantized_param(self.buffers[gbuf_idx].params[0]): | ||
| fp8_gbuf_indices.append(gbuf_idx) | ||
| if len(fp8_gbuf_indices) == 0: | ||
| return | ||
|
|
@@ -2167,7 +2234,7 @@ def split_state_dict_if_needed(self, state_dict): | |
| new_state_dict = {'buckets_coalesced': state_dict['buckets_coalesced']} | ||
| for gbuf_idx, gbuf_range_maps in enumerate(self.gbuf_ranges): | ||
| for dtype, _ in gbuf_range_maps.items(): | ||
| if not is_float8tensor(self.buffers[gbuf_idx].params[0]): | ||
| if not self._is_distopt_quantized_param(self.buffers[gbuf_idx].params[0]): | ||
| new_state_dict[gbuf_idx] = state_dict[dtype_to_gbuf_idx[dtype]] | ||
|
|
||
| for fp8_gbuf_idx in fp8_gbuf_indices: | ||
|
|
@@ -2367,7 +2434,7 @@ def _get_fp8_params_and_shard_fp32_from_fp8(self): | |
| idx = 0 | ||
| for buffer in buffers: | ||
| for param in buffer.params: | ||
| if is_float8tensor(param): | ||
| if self._is_distopt_quantized_param(param): | ||
| fp8_params.append(param) | ||
| shard_fp32_from_fp8.append(None) | ||
| shard_offsets_in_fp8.append(None) | ||
|
|
@@ -2382,7 +2449,7 @@ def get_shard_fp32_from_fp8(shard_main_groups, model_groups): | |
| """ | ||
| for shard_main_group, model_group in zip(shard_main_groups, model_groups): | ||
| for shard_main_param, model_param in zip(shard_main_group, model_group): | ||
| if is_float8tensor(model_param): | ||
| if self._is_distopt_quantized_param(model_param): | ||
| param_range_map = self._get_model_param_range_map(model_param) | ||
| param_range = param_range_map["param"] | ||
| assert param_range.size == shard_main_param.nelement() | ||
|
|
@@ -2459,8 +2526,29 @@ def _copy_main_params_to_model_params(self): | |
| if self.config.use_precision_aware_optimizer_no_fp8_or_ds_fp8: | ||
| return | ||
|
|
||
| fp8_params, shard_fp32_from_fp8, shard_offsets_in_fp8 = ( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comment on what this is trying to do? |
||
| self._get_fp8_params_and_shard_fp32_from_fp8() | ||
| ) | ||
| expanded_fp8_params = [] | ||
| expanded_shard_fp32_from_fp8 = [] | ||
| expanded_shard_offsets_in_fp8 = [] | ||
| for model_param, shard_main_param, start_offset in zip( | ||
| fp8_params, shard_fp32_from_fp8, shard_offsets_in_fp8 | ||
| ): | ||
| sub_model_params, sub_shard_main_params, sub_start_offsets = ( | ||
| self._expand_quantized_param_shard_for_cast( | ||
| model_param, shard_main_param, start_offset | ||
| ) | ||
| ) | ||
| expanded_fp8_params.extend(sub_model_params) | ||
| expanded_shard_fp32_from_fp8.extend(sub_shard_main_params) | ||
| expanded_shard_offsets_in_fp8.extend(sub_start_offsets) | ||
|
|
||
| quantize_param_shard( | ||
| *self._get_fp8_params_and_shard_fp32_from_fp8(), self.data_parallel_group | ||
| expanded_fp8_params, | ||
| expanded_shard_fp32_from_fp8, | ||
| expanded_shard_offsets_in_fp8, | ||
| self.data_parallel_group, | ||
| ) | ||
|
|
||
| # Utility method for copying group params. | ||
|
|
@@ -2480,7 +2568,7 @@ def copy_group_params(shard_main_groups, model_groups): | |
| world_range.start : world_range.end | ||
| ] | ||
|
|
||
| if is_float8tensor(model_param): | ||
| if self._is_distopt_quantized_param(model_param): | ||
| # FP8 params are quantized in the above "quantize_param_shard" function. | ||
| continue | ||
| else: | ||
|
|
@@ -2592,8 +2680,12 @@ def copy_group_params(model_groups, shard_main_groups): | |
| # Use param from state_dict to initialize main_param | ||
| model_param = model_param_to_state_dict_param_map[model_param] | ||
|
|
||
| if is_float8tensor(model_param): | ||
| shard_model_param = dequantize_fp8_tensor(model_param).view(-1)[ | ||
| if self._is_distopt_quantized_param(model_param): | ||
| if self._is_grouped_quantized_tensor(model_param): | ||
| dequantized_model_param = model_param.float() | ||
| else: | ||
| dequantized_model_param = dequantize_fp8_tensor(model_param) | ||
| shard_model_param = dequantized_model_param.view(-1)[ | ||
| param_range.start : param_range.end | ||
| ] | ||
| else: | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the purpose of this?