Skip to content

Commit 9319463

Browse files
authored
[data] fix: Keep cyclic sampler ranks epoch-aligned (#5944)
Signed-off-by: Yu Yao <yaoyu.094@gmail.com>
1 parent c24c9ed commit 9319463

2 files changed

Lines changed: 30 additions & 3 deletions

File tree

‎src/megatron/bridge/data/samplers.py‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -506,12 +506,11 @@ def __iter__(self) -> Iterator[list[int]]:
506506
random_idx = torch.randperm(bucket_size, generator=g).tolist()
507507
idx_range = [start_idx + x for x in random_idx[bucket_offset:]]
508508
else:
509-
full_bucket_size = (self.total_samples // self.micro_batch_size) * self.micro_batch_size
510509
full_bucket_offset = current_epoch_samples
511510
g = torch.Generator()
512511
g.manual_seed(self.epoch)
513-
idx_range_total = torch.randperm(full_bucket_size, generator=g).tolist()
514-
idx_range_active = idx_range_total[full_bucket_offset:]
512+
idx_range_total = torch.randperm(self.total_samples, generator=g).tolist()
513+
idx_range_active = idx_range_total[full_bucket_offset:active_total_samples]
515514
idx_range = idx_range_active[self.data_parallel_rank :: self.data_parallel_size]
516515

517516
batch = []

‎tests/unit_tests/data/test_samplers.py‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from torch.utils.data import Dataset
66

77
from megatron.bridge.data.samplers import (
8+
MegatronPretrainingRandomSampler,
89
MegatronPretrainingSampler,
910
RandomSeedDataset,
1011
build_pretraining_data_loader,
@@ -108,3 +109,30 @@ def test_cyclic_sampler_resume_seeds_worker_dataset_for_current_epoch(
108109
expected_value = torch.randint(0, 1_000_000, (1,), generator=generator).item()
109110

110111
assert actual_value.item() == expected_value
112+
113+
114+
def test_cyclic_sampler_non_sharded_tail_keeps_data_parallel_epochs_aligned() -> None:
115+
"""Every DP rank must cross a truncated cyclic epoch on the same step."""
116+
117+
def make_sampler(rank: int, consumed_samples: int = 0) -> MegatronPretrainingRandomSampler:
118+
return MegatronPretrainingRandomSampler(
119+
list(range(10)),
120+
total_samples=10,
121+
consumed_samples=consumed_samples,
122+
micro_batch_size=2,
123+
data_parallel_rank=rank,
124+
data_parallel_size=3,
125+
data_sharding=False,
126+
)
127+
128+
def cyclic_batches(sampler: MegatronPretrainingRandomSampler):
129+
while True:
130+
yield from sampler
131+
132+
uninterrupted = [cyclic_batches(make_sampler(rank)) for rank in range(3)]
133+
_ = [next(rank_batches) for rank_batches in uninterrupted]
134+
second_step = [next(rank_batches) for rank_batches in uninterrupted]
135+
resumed_step = [next(iter(make_sampler(rank, consumed_samples=6))) for rank in range(3)]
136+
137+
assert len({index for batch in second_step for index in batch}) == 6
138+
assert second_step == resumed_step

0 commit comments

Comments
 (0)