Skip to content

feat(data): scalable canonical-grid reads for MegatronMIMO data loading - #5525

Open
going-song wants to merge 1 commit into
NVIDIA-NeMo:mainfrom
going-song:feat/megatron-mimo-canonical-reads
Open

feat(data): scalable canonical-grid reads for MegatronMIMO data loading#5525
going-song wants to merge 1 commit into
NVIDIA-NeMo:mainfrom
going-song:feat/megatron-mimo-canonical-reads

Conversation

@going-song

@going-song going-song commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

What

Adds opt-in scalable data-parallel reads to MegatronMIMO. By default every data-loading
rank reads the full global micro-batch and forward_step slices out its module-DP shard, so
per-rank read and preprocessing cost scales with the DP degree. With
dataset.megatron_mimo_scalable_dp each rank's sampler reads only its shard and the slice is
skipped:

default            scalable
read    [4, S]     read    [2, S]      (per language rank, dp=2)
slice   [4]->[2]   slice   skipped
forward [2, S]     forward [2, S]      (same shard geometry, identical routing)

Second of three PRs splitting #4608 (original work by @sailor1493, credited
as co-author and signer), per the maintainers' split request in #4609.
Part 1 merged as #5131.

Why a canonical grid (mechanism deviation from #4608)

#4608 sharded each module's loader by its own module-DP (rank, size). A sampler's shard
assignment is a function of (micro_batch_size, data_parallel_size), so under "cyclic"
the direct-HF-SFT default since #5048 — modules with different DP sizes read different
sample sets
, while the BridgeCommunicator routes modality embeddings positionally along
the batch dim. We hit this live as an embedding-count crash in
align_embeddings_by_token_positions; with uniform image sizes it would silently train on
mispaired image-text.

Here all loaders shard on one canonical grid — the LCM of the module DP sizes. Every
group sampler uses the identical (micro_batch // grid, grid) geometry and global
consumed_samples; a rank whose module has DP size d concatenates the windows of its
grid // d consecutive groups (CanonicalGroupBatchSampler). Group streams are
module-independent, so every module materializes the identical ordered micro-batch under
"single" and "cyclic"; for "single" the stream is bit-identical to the default
path's slice. Geometries are truncated to whole micro-batches so ragged dataset sizes cannot
desynchronize the groups. ("Canonical group" is the vocabulary of #4608's reordering
machinery, which ③ will port.)

Configuration

cfg.dataset.megatron_mimo_scalable_dp = True   # DirectHFSFTDatasetConfig; example: --scalable-dp

"single" and "cyclic" are supported; "batch"/"external" and drop_last=False are
rejected in validate() and again at loader build. Off by default — with the switch unset, the
sampler parameters, loader arguments and slicing behavior are identical to main.

Notes for reviewers

  • Scalable reads reject expert_tensor_parallel_size > 1 (unvalidated combination). The
    default slicing path shares the same assumption — happy to hoist the guard into
    MegatronMIMOParallelismConfig validation if preferred.
  • No train-loop changes: the provider forces variable_seq_lengths=True, so the schedule's
    micro_batch_size is shape-inert and the LR/consumed accounting already uses the full
    micro-batch in both modes (feat(data): intra-microbatch reordering for MegatronMIMO (+ sequence packing, scalable DP) #4608's schedule/LR adjustment is unnecessary here).
  • Under "cyclic", scalable on/off are two different valid epoch shuffles (per-iteration
    losses not comparable); "single" is order-identical.

Testing

258 unit tests passing locally, including the cross-geometry alignment invariant (a rank
covering groups {0,1} reads exactly the concatenation of the single-group ranks' windows, for
"cyclic" in both data_sharding modes) and a library-path end-to-end test with DP {2,3}
(grid = 6). Live PP=2 runs (5 GPUs of an 8xA100 node, language=tp1,pp2,dp2 + images=dp1, Qwen3.5-0.8B random
init, cord_v2, mbs 4, 10 iters): cyclic+scalable completes 10/10 where module-DP sharding
crashes; single scalable matches the default losses within bf16 noise (max |diff| 0.0043);
the default path matches prior losses within run-to-run noise (max |diff| 0.0024); packing on/off parity under scalable ~0.001.
(One drive-by repair: the example-config test's arg namespace was already failing on main —
#5131 added pack_sequences_in_batch to _build_dataset_config without updating it — so this
PR adds both missing kwargs there.)

Known limitations

ETP > 1 and "batch"/"external" loaders are rejected for scalable reads; MegatronMIMO still
cannot run CP (pre-existing); checkpoint resume is unit-tested but not yet exercised in a live
run, and multi-node validation is pending, as with #4608.

Signed-off-by: kayeon.song kayeon.song@navercorp.com
Signed-off-by: Yoonsik Kim yoonsik.kim90@navercorp.com
Signed-off-by: Chanwoo Park chanwoo.park98@navercorp.com

@copy-pr-bot

copy-pr-bot Bot commented Aug 12, 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.

if scalable_dp:
module_parallelism = megatron_mimo_cfg.module_parallelisms[my_module]
if module_parallelism.expert_tensor_parallel_size != 1:
raise NotImplementedError(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default slicing path relies on the same assumption — should this live in MegatronMIMOParallelismConfig validation instead?

# Truncate to whole global micro-batches: the flat samplers round their active range at
# per-group granularity, which diverges per group for a non-multiple dataset size (the
# cyclic data_sharding=False stride would give groups unequal window counts).
total_samples = (len(dataset) // micro_batch_size) * micro_batch_size

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prevents unequal per-group window counts on ragged dataset sizes (cyclic + data_sharding=False); regression-tested.

dataloader_type="single",
trust_remote_code=False,
do_validation=do_validation,
pack_sequences_in_batch=False,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test has been failing on main since #5131 (missing pack_sequences_in_batch); adding both kwargs.

@yaoyu-33 yaoyu-33 added area:data Dataset builders, preprocessing, and samplers feature New capabilities, enhancements, or enablement work full-test-suite needs-more-tests Requires additional L0 and L1 test coverage before merge needs-review PR is ready for code review and waiting on a reviewer labels Aug 12, 2026
@liding-nv
liding-nv self-requested a review August 15, 2026 03:16
@liding-nv

Copy link
Copy Markdown
Contributor

/ok to test 958575f

By default every MegatronMIMO data-loading rank reads the full global
micro-batch and forward_step slices out its module-DP shard, so per-rank
read and preprocessing cost scales with the DP degree. This adds an opt-in
dataset switch, `megatron_mimo_scalable_dp`, that shards reads at the
sampler instead and skips the forward-step slice.

Reads are sharded on a canonical grid: the LCM of the module DP sizes.
Each rank covers `grid // dp` consecutive canonical groups and
concatenates one flat Megatron sampler per group, all built with the same
`(micro_batch // grid, grid)` geometry and the global consumed_samples.
Group streams depend only on module-independent inputs, so every module
materializes the identical ordered global micro-batch under both "single"
and the default "cyclic" sampler, keeping the BridgeCommunicator's
positional batch-dim routing aligned. (Sharding by each module's own DP -
the original mechanism - only aligns under "single"; "cyclic" became the
direct-HF-SFT default in NVIDIA-NeMo#5048.) Group geometries are truncated to whole
global micro-batches so ragged dataset sizes cannot desynchronize the
groups' window counts.

Split (2/3) of NVIDIA-NeMo#4608 as agreed in NVIDIA-NeMo#4609. Original work by Chanwoo Park.

Validated: unit tests for cross-geometry alignment, resume across epoch
boundaries, and non-multiple dataset sizes; live PP=2 runs (language
tp1/pp2/dp2 + images dp1): cyclic+scalable completes where module-DP
sharding crashed on embedding-count mismatch, single scalable matches
non-scalable losses within bf16 noise, and the default path matches
prior losses within run-to-run noise.

Signed-off-by: kayeon.song <kayeon.song@navercorp.com>
Signed-off-by: Yoonsik Kim <yoonsik.kim90@navercorp.com>
Signed-off-by: Chanwoo Park <chanwoo.park98@navercorp.com>
Co-authored-by: Yoonsik Kim <yoonsik.kim90@navercorp.com>
Co-authored-by: Chanwoo Park <chanwoo.park98@navercorp.com>
@going-song
going-song force-pushed the feat/megatron-mimo-canonical-reads branch from 958575f to 2f6f339 Compare August 18, 2026 11:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:data Dataset builders, preprocessing, and samplers community-request feature New capabilities, enhancements, or enablement work full-test-suite needs-more-tests Requires additional L0 and L1 test coverage before merge needs-review PR is ready for code review and waiting on a reviewer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants