Skip to content

Use cyclic distributed padding for oversubscribed datasets - #2903

Closed
hjinnkim wants to merge 6 commits into
bghira:mainfrom
hjinnkim:enhancement/diverse-oversubscribed-step-padding
Closed

Use cyclic distributed padding for oversubscribed datasets#2903
hjinnkim wants to merge 6 commits into
bghira:mainfrom
hjinnkim:enhancement/diverse-oversubscribed-step-padding

Conversation

@hjinnkim

@hjinnkim hjinnkim commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Use cyclic distributed padding for oversubscribed datasets

Dependencies

Summary

  • Problem: last-item padding prevents empty shards but concentrates all added
    entries on one sample; Context Parallel and non-CP paths also padded at
    different stages.
  • Enhancement: cyclically extend one shared shuffled global bucket before
    DDP/FSDP2 or Context Parallel splitting.

Prerequisite behavior

  • simpletuner/helpers/data_backend/factory.py: enable padding for epoch-driven
    runs or explicit oversubscription while preserving ordinary step-bounded
    behavior.
  • simpletuner/helpers/metadata/backends/base.py: establish one deterministic
    dataset-and-bucket order shared by every rank.
  • simpletuner/helpers/training/trainer.py: forward the same padding policy after
    epoch-rollover bucket rebuilds.

Changes in this PR

  • simpletuner/helpers/metadata/backends/base.py: preserve empty buckets without
    entering repeat or padding arithmetic.
  • Cyclically extend the shared shuffled bucket to a multiple of effective DP
    size before splitting.
  • Remove CP tail padding and disable Accelerate per-rank padding after the global
    list is already padded.
Training mode Padding
Epoch-driven Cyclic prefix padding
Step-bounded with oversubscription Cyclic prefix padding
Step-bounded without oversubscription Disabled

Result

  • A non-empty undersized bucket keeps every effective DP rank non-empty when
    padding is enabled.
  • Added entries are distributed across the shuffled prefix instead of one final
    sample; CP and non-CP use the same global policy.
  • Ordinary step-bounded behavior remains unchanged.
  • Example: with eight effective DP ranks and [A, B, C, D], final-item
    padding produces [A, B, C, D, D, D, D, D]: four added copies of D, five
    D occurrences total. A shared shuffled order such as [C, A, D, B] now
    becomes [C, A, D, B, C, A, D, B], spreading the four added entries across
    the bucket prefix.
  • Verified: the focused matrix covered CP and non-CP paths, eight effective
    DP ranks, CP size 2, epoch rollover, empty buckets, and seed reproducibility;
    the related 110-test regression suite passed. PR01 through PR06 followed by
    this stacked PR merged without conflicts, and the two-rank distributed
    integration smoke passed.

Environment

  • Python: 3.13.14
  • PyTorch: 2.13.0+cu130
  • Accelerate: 1.14.0
  • Diffusers: 0.39.0
  • Transformers: 5.13.1
  • GPU: NVIDIA B200, driver 590.48.01

hjinnkim added 6 commits July 26, 2026 09:51
Explicit dataset oversubscription can increase a bucket's repeat count, but
those repeats are not materialized before the bucket is sliced across ranks.
The existing fixed-step padding gate could therefore leave DP ranks empty.

- In data_backend/factory.py, enable padding for epoch-driven runs or when
  allow_dataset_oversubscription is explicit. Ordinary fixed-step runs keep
  their existing no-padding behavior.
- In metadata/backends/base.py, fill short and completely empty Context
  Parallel shards from the final item in the non-empty global bucket. The old
  local-only repetition had no source item for an empty shard; this also
  matches Accelerate's non-CP last-item policy.
- In training/trainer.py, forward the same policy when epoch rollover rebuilds
  and re-splits buckets. Otherwise the omitted argument defaults to false and
  silently drops the initial split policy.
Rank-local RNG state could permute the same bucket differently before each
rank sliced it, causing duplicate and missing samples. Resume could then apply
saved progress to a newly rebuilt partition that did not match the checkpoint.

- In metadata/backends/base.py, derive every rank's bucket order from one
  configured or main-rank-generated seed, broadcast it, and shuffle a copied
  bucket with a dataset-and-bucket-specific RNG. Avoid a second full-list copy
  when the trim limit keeps the complete bucket.
- In multiaspect/sampler.py, save the exact local partition together with world
  size, rank, effective DP size, DP rank, and CP size. Validate that topology
  and restore the partition before applying seen or exhausted progress.
- Reject topology mismatches, and distributed legacy partition state without
  topology metadata, before mutating the live sampler because those partitions
  cannot be mapped safely to the current ranks.
Explicit oversubscription still inherited the fixed-step no-padding gate, and
existing padding concentrated all extra entries on one final sample. Context
Parallel and non-CP paths also applied padding at different stages.

- In data_backend/factory.py, enable padding for epoch-driven or explicitly
  oversubscribed runs while preserving ordinary fixed-step behavior.
- In metadata/backends/base.py, establish one shared bucket seed and use a
  deterministic dataset-and-bucket RNG so every rank pads and slices the same
  order.
- Cyclically extend the global bucket to a multiple of effective DP size before
  splitting, disable Accelerate's final-item padding, and feed the same padded
  list to CP and non-CP paths. Repeats are distributed across the prefix rather
  than concentrated on one sample.
- Skip padding arithmetic for empty buckets because they have no valid source
  item to repeat.
- In training/trainer.py, forward the same policy when epoch rollover rebuilds
  buckets so the post-rebuild distribution matches the initial split.
Skip empty buckets during repeat validation so an empty bucket cannot trigger a division-by-zero calculation.\n\nPad the shuffled global bucket to an effective-DP multiple with a cyclic prefix before rank partitioning. This spreads duplicated samples across the bucket instead of repeating only the final item.\n\nDisable the older CP tail-padding block and Accelerate per-rank padding after global padding, keeping DDP, FSDP2, and context-parallel partitions on the same policy.\n\nThis commit is stacked on the oversubscription policy and deterministic rank-shuffle fixes so the three changes merge without overlapping reimplementations.
Preserve the published PR07 history while merging the exact oversubscription-policy and deterministic-rank-shuffle prerequisite commits.

Resolve the overlapping metadata backend implementation to the prerequisite-aware cyclic-padding version: empty buckets bypass padding arithmetic, one shared shuffled global bucket is extended by a cyclic prefix, and both Context Parallel and ordinary distributed paths consume the same pre-padded list without Accelerate tail padding.

This makes the branch merge cleanly after PR01 through PR06 and leaves only the cyclic-padding enhancement once the prerequisite commits are present upstream.
@hjinnkim

Copy link
Copy Markdown
Contributor Author

Closing this draft for now because it is stacked on #2897 and #2898. After both prerequisites are merged, I will rebuild the cyclic-padding change on the latest main, verify that the PR contains only its own delta, and submit a clean follow-up PR.

@hjinnkim hjinnkim closed this Jul 26, 2026
@hjinnkim
hjinnkim deleted the enhancement/diverse-oversubscribed-step-padding branch July 26, 2026 12:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant