perf(checkpointing): describe prepended-axis shards with the checkpointable path by LiRunGuo · Pull Request #7287 · NVIDIA/Megatron-LM · GitHub
Skip to content

perf(checkpointing): describe prepended-axis shards with the checkpointable path - #7287

Merged
dimapihtar merged 3 commits into
NVIDIA:mainfrom
LiRunGuo:perf/prepended-axis-checkpointable
Oct 7, 2026
Merged

dimapihtar merged 3 commits into
NVIDIA:mainfrom
LiRunGuo:perf/prepended-axis-checkpointable

Conversation

@LiRunGuo

Copy link
Copy Markdown
Contributor

Motivation

mcore_to_pyt_state_dict routes every ShardedTensor with a non-zero prepend_axis_num
through the legacy PyTorch ShardedTensor conversion
(sharded_tensor_to_torch_sharded_tensor). That conversion enumerates the entire global
shard grid
— remote shards included — for each converted key:

for fragment_offsets in product(*map(range, some_sh_ten.axis_fragmentations)):

so its cost grows with the number of fragments across all logical axes, not with the number of
locally held shards.

Prepended axes are not an exotic corner case (closes #7275):

  • TransformerBlock.sharded_state_dict passes (0, global_layer_offset, num_layers) as the
    sharded_offsets of every layer whenever the stack is homogeneous, and
    sharded_state_dict_default turns those offsets into a prepended layer axis of every
    parameter of the block.
  • SequentialMLP.sharded_state_dict adds a prepended global-expert axis on top of it for
    each routed expert.

For a 48-layer MoE model with 128 experts and EP=8 the per-rank local shards for one weight key
therefore describe a 48 x 128 grid — 4.7M grid entries per rank in total — and all of that work
happens synchronously before the async write request is returned, so asynchronous
checkpointing cannot hide it from the training loop.

What changed

A prepended axis has extent one within a local chunk (that is what prepend_axis_num means:
the local data covers a single index along the prepended axes). The DCP checkpointable path can
describe that directly, so prepended-axis shards no longer need the legacy conversion:

  • CheckpointableShardedTensor now reports prepended axes as singleton dimensions of the local
    chunk (sizes = (1,) * prepend_axis_num + local_shape) and exposes the local data through a
    matching view (data.view((1,) * prepend_axis_num + local_shape)) — a view, no payload copy.
  • mcore_to_pyt_state_dict routes prepended-axis shards to the checkpointable path, keeping the
    existing restrictions (PyTorch >= 2.6, regular grid, no flattened shards).

The resulting DCP metadata is identical to the metadata produced by the legacy conversion
(same chunk offsets, chunk sizes, global size and dtype — asserted by the new tests), so:

  • checkpoints written before this change still load, and
  • checkpoints written after it are indistinguishable from the old format.

Measurements (8xH200, PyTorch 2.10, torch_dist)

Local (non-TransformerEngine) MoE model, 48 layers, 128 experts, EP=8, 1,109 local shards per
rank, 1,646 unique keys, 4,734,725 grid entries per rank:

phase before after
mcore_to_pyt_state_dict 11.71 s 0.07 s (165x)
synchronous prefix of async_save() 17.75 s 5.22 s (3.4x)
save_preprocess (unchanged) 0.58 s 0.60 s

A synthetic prepended-axis workload with 3,072 keys/rank x 128 grid entries (393,216 entries)
lands in the same place: 3.69 s -> 0.027 s.

Testing

  • New: tests/unit_tests/dist_checkpointing/test_checkpointable.py::TestPrependedAxisCheckpointable
    — write-item metadata parity with the legacy conversion, save/load round trip, and loading a
    checkpoint written by the legacy conversion.
  • New: tests/unit_tests/dist_checkpointing/models/test_moe_expert_sharding.py — MoE model
    round trips for EP=1/2/4 and EP=4 -> EP=2 resharding (asserts the prepended layer/expert axes
    are present). Runs without TransformerEngine.
  • pytest tests/unit_tests/dist_checkpointing/{test_checkpointable,test_torch_dist, test_serialization,test_validation,test_local,test_fully_parallel,test_replication}.py
    on 8 GPUs: 65 passed, 2 skipped.
  • black, isort, ruff, pylint clean on the changed files.

Follow-ups (not in this PR)

The legacy branch of mcore_to_pyt_state_dict (and sharded_tensor_to_torch_sharded_tensor)
is now reachable only for torch < 2.6; removing it once the minimum PyTorch version allows
would be a separate cleanup.

…ntable path

`mcore_to_pyt_state_dict` routes ShardedTensors with a non-zero `prepend_axis_num`
through the legacy PyTorch ShardedTensor conversion, which enumerates the entire
global shard grid (including remote shards) for every converted key. Homogeneous
transformer stacks prepend a layer axis to every parameter and MoE routed experts
add a global-expert axis on top of it, so the conversion cost grows with the
number of fragments across all logical axes instead of with the number of locally
held shards. The conversion runs synchronously before the async write request is
returned, so it cannot be hidden by asynchronous checkpointing.

A prepended axis has extent one within a local chunk, which the checkpointable
path can describe directly: expose prepended axes as singleton dimensions of the
local chunk metadata and of the local data view (a view, no payload copy). The
resulting DCP metadata is identical to the legacy one, so checkpoints stay
readable in both directions.

Measured on 8xH200 for a local MoE model (48 layers, 128 experts, EP=8,
SequentialMLP experts, 4.7M grid entries per rank): `mcore_to_pyt_state_dict`
drops from 11.71 s to 0.07 s, and the synchronous prefix of an async save from
17.75 s to 5.22 s.

Signed-off-by: Gawr Gura 0721 <LiRunGuo@users.noreply.github.com>
Adds regression coverage for routing prepended-axis shards (homogeneous layer
axis, MoE expert axis) through the checkpointable path:

- DCP write-item metadata parity against the legacy conversion, including the
  singleton prepended dimensions of the local chunk;
- save/load round trip of a standalone prepended-axis shard, checking the stored
  chunk metadata and the loaded values;
- loading a checkpoint written by the legacy conversion (old-format checkpoints
  must keep loading);
- a MoE model end-to-end test (local, non-TransformerEngine experts) covering
  EP=1/2/4 round trips and EP=4 -> EP=2 resharding, which asserts the expert
  weights carry the prepended layer/expert axes.

Signed-off-by: Gawr Gura 0721 <LiRunGuo@users.noreply.github.com>
@copy-pr-bot

copy-pr-bot Bot commented Sep 13, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@Connor-XY

Copy link
Copy Markdown
Contributor

@NVIDIA/core-adlr @NVIDIA/core-nemo The checkpointing expert review is approved on the current head (471fa1c), and there are no outstanding inline review threads. Could the already-requested final-review teams complete the repository-level review? This is the prepended-axis checkpoint conversion optimization tracked in #7275.

@svcnvidia-nemo-ci svcnvidia-nemo-ci added Final Review PR is in the "final review" stage waiting-on-maintainers Waiting on maintainers to respond and removed waiting-on-maintainers Waiting on maintainers to respond labels Sep 21, 2026
@dimapihtar

Copy link
Copy Markdown
Contributor

/ok to test f4af454

@dimapihtar
dimapihtar requested a review from jaredcasper October 6, 2026 13:10
@dimapihtar
dimapihtar added this pull request to the merge queue Oct 7, 2026
@svcnvidia-nemo-ci svcnvidia-nemo-ci added Approved All necessary approvals have been made and removed Final Review PR is in the "final review" stage labels Oct 7, 2026
@nemo-automation-bot

Copy link
Copy Markdown

Merged via the queue into NVIDIA:main with commit e4b048e Oct 7, 2026
105 of 109 checks passed
@svcnvidia-nemo-ci svcnvidia-nemo-ci removed the waiting-on-maintainers Waiting on maintainers to respond label Oct 7, 2026
cspades pushed a commit to cspades/Megatron-LM that referenced this pull request Oct 7, 2026
…ntable path (NVIDIA#7287)

Signed-off-by: Gawr Gura 0721 <LiRunGuo@users.noreply.github.com>
Co-authored-by: Gawr Gura 0721 <LiRunGuo@users.noreply.github.com>
Co-authored-by: Dmytro Pykhtar <37850217+dimapihtar@users.noreply.github.com>

This branch was previously deployed

2 inactive deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Approved All necessary approvals have been made community-request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ENHANCEMENT] Reduce torch_dist checkpoint preparation overhead for prepended-axis tensors

6 participants