Repository navigation
Add unpadded shards for MFSDP v2 RowAtomic/BlockAtomic layouts - #8116
Conversation
|
This PR has been automatically converted to draft because all PRs must start as drafts. When you are ready for review, click Ready for Review to begin the review process. This will:
See the contribution guide for more details. |
83fe378 to
7b80509
Compare
| Unlike ``build_for_row_atomic``, this never pads. Tensors are packed back to | ||
| back in tensor-id order, so ``size`` is the sum of all tensor numels. |
There was a problem hiding this comment.
back to back
Could the alignment be an performance issue? I forgot what alignment nccl likes.
There was a problem hiding this comment.
Yes, there is a cost. In our measurements the uneven all-gather / reduce-scatter (list-based dist.all_gather / dist.reduce_scatter) are somewhat slower than the even *_into_tensor / *_tensor paths, and in end-to-end runs the exposed AG/RS show some impact.
The NCCL team is already working on optimizing the uneven collectives. The padded layout stays the default, so this only affects users who opt into ShardLayout.UNPADDED.
There was a problem hiding this comment.
Non-blocking: I was talking about alignment, not balance. Also, even with equal-size inputs, dist.all_gather is known to be slower than all_gather_into_tensor/all_gather_single. dist.all_gather does extra data copy.
| return layout | ||
|
|
||
| @classmethod | ||
| def build_for_row_atomic_unpadding( |
There was a problem hiding this comment.
Consider for future PRs: there may be some code sharable with the even version.
There was a problem hiding this comment.
Done: shape validation and per-tensor alignment are shared via _row_atomic_alignments; the two packing algorithms stay separate.
| """Slice ``buffer`` into each peer's range under ``placements`` along ``mesh_axis``.""" | ||
| return [ | ||
| buffer.local_buffer.narrow(0, peer_range.start - buffer.offset, peer_range.numel) | ||
| for peer_range in self.layout.get_peer_ranges(self.mesh, placements, mesh_axis) |
There was a problem hiding this comment.
get_peer_ranges appears to be very specific for this use case. Before we have more use cases to justify the API, consider getting the peers from mesh.get_group(mesh_axis) and then calling get_rank_range for each.
There was a problem hiding this comment.
Done. _peer_chunks now uses dist.get_process_group_ranks(mesh.get_group(axis)) + get_rank_range.
| "model gradients during FSDP. If 'auto', then the main gradient data-type will " | ||
| "be used for the gradient communication / reduction data-type. When using NCCL " | ||
| "v2.27+, reduction is always computed in FP32 if using NCCL Symmetric kernels.") | ||
| group.add_argument('--megatron-fsdp-uneven-shards', action='store_true', default=False, |
There was a problem hiding this comment.
"" instead of ''. See the style guide.
| assert not (args.megatron_fsdp_uneven_shards and args.nccl_ub), \ | ||
| '--megatron-fsdp-uneven-shards is not compatible with --use-nccl-ub' |
There was a problem hiding this comment.
Nit: this is already checked in ParameterGroup. Should we expose all invalid config combinations in validate_args? That could be a lot of redundancy. Really a question, not a request for change.
There was a problem hiding this comment.
Removed both asserts. The v2 adapter already rejects the nccl_ub conflict, and v1 ignores the flag like the other v2-only fields.
|
|
||
|
|
||
| @pytest.mark.parametrize("use_out", [False, True]) | ||
| def test_quantized_dbuffer_uneven_allgathers_every_plane(distributed_setup, use_out): |
There was a problem hiding this comment.
Would you like to parameterize the existing test instead of having a new one? They look almost identical.
There was a problem hiding this comment.
Done, parametrized over ShardLayout.
| device: torch.device | str, | ||
| *, | ||
| block_size: int = 1, | ||
| uneven_shards: bool = False, |
There was a problem hiding this comment.
There was a problem hiding this comment.
Done: shard_layout: ShardLayout = ShardLayout.PADDED (PADDED / UNPADDED) from fully_shard down to DBuffer.empty. The config layer keeps a bool, megatron_fsdp_padded_shards (default True).
| @@ -503,14 +520,7 @@ def allgather( | |||
| raise NotImplementedError( | |||
| "Symmetric-memory allgather() requires equal shard sizes." | |||
| ) | |||
| # Unequal segments only arise with TensorAtomic on a 1-D mesh, so ``view`` | |||
| # spans the whole global buffer and the layout's rank segments index it. | |||
| chunks = [ | |||
| view.local_buffer.narrow( | |||
| 0, self.layout.rank_to_offset[rank], self.layout.rank_size(rank) | |||
| ) | |||
| for rank in range(self.layout.dp_size) | |||
| ] | |||
| chunks = self._peer_chunks(view, input_placements, axis) | |||
| dist.all_gather(chunks, local_buffer, group=group) | |||
| local_buffer = view.local_buffer | |||
There was a problem hiding this comment.
This loop body has got longer over time. Can we extract this into a separate method? Maybe all_gather_one_axis and let all_gather call it?
(Can be separate PR)
There was a problem hiding this comment.
Agreed, will do in a follow-up PR.
7b80509 to
30c6ac7
Compare
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
53880c0 to
c68b1cd
Compare
ee35bae to
0ca597d
Compare
Signed-off-by: Xiaoda Zhang <xiaodaz@nvidia.com>
0ca597d to
9da9339
Compare
…en the padded and unpadded builders; drop GlobalLayout.get_peer_ranges; replace the uneven_shards bool with a ShardLayout enum (PADDED / UNPADDED) Signed-off-by: Xiaoda Zhang <xiaodaz@nvidia.com>
9da9339 to
fab7790
Compare
|
🔄 Merge queue validation started! You can track the progress here: https://github.com/NVIDIA/Megatron-LM/actions/runs/40611787685 |
Add an opt-in unpadded layout for MFSDP v2 RowAtomic/BlockAtomic parameter groups.
Tensors are packed back to back and rank boundaries snap to the nearest row
(32-row block for MXFP8) boundary, so shards are generally unequal but carry zero
padding. The default padded layout is unchanged.
Motivation: the padded layout rounds every group up to
dp_size * LCM(row sizes)(
* 32for MXFP8). For groups mixing many row widths this is large; theDeepSeek-V3-shaped dense group measured for #7585 pads 13% at DP=4 and over 80% at
DP=16.
Changes:
layout_builder.build_for_row_atomic_unpadding: padding-free builder; shardsdiffer by at most one row/block and may be empty. Shape validation and per-tensor
alignment are shared with the padded builder.
GlobalLayout.validate_for_row_atomiconly checks that no rank boundary splits arow/block, instead of requiring equal segments; padded layouts still pass.
DBuffer: the list-basedall_gather/reduce_scatterpaths slice per-peer chunksvia
get_rank_range, so uneven layouts work on multi-axis (HSDP/HFSDP) meshes.QuantizedDBuffer: scale planes derive from the unpadded data layout.ShardLayout.PADDED | UNPADDEDenum,fully_shard(shard_layout=...);DistributedDataParallelConfig.megatron_fsdp_padded_shards(default True) and--no-megatron-fsdp-padded-shardsfor MCore. Incompatible withnccl_ub(symmetric memory needs equal shards).
Uneven NCCL collectives are somewhat slower than the even
*_into_tensorpaths; theNCCL team is optimizing them. Only users opting into
UNPADDEDare affected.Tests: existing DBuffer, QuantizedDBuffer, SGD/HSDP/HFSDP parity, and MXFP8 training
tests are parametrized over
ShardLayout; layout builder tests added; adapter flagcovered.
tests/unit_tests/distributed/mfsdp_v2passes on 4xGB200.Issue tracking
Linked issue: Related to #7585, #6587