Navigation
πŸ’Ύ Data & StorageΒ· 8 min read

Data Loading at Scale

Coordinating thousands of workers reading from shared storage, shuffle buffers, avoiding I/O hotspots, and ensuring deterministic training.

data-loadingscaledistributedshuffleshardingcoordination

At 1000+ GPU scale, data loading becomes a distributed systems problem: thousands of workers reading from shared storage simultaneously, needing global shuffling without communication, and maintaining determinism for reproducibility.


1. The Scale Challenge

ScaleGPUsWorkers (8/GPU)Data throughput neededStorage pressure
Small (1 node)864~50 MB/sLocal NVMe handles it easily
Medium (8 nodes)64512~400 MB/sParallel FS or cloud streaming
Large (64 nodes)5124,096~3 GB/sShared storage under load
Mega (512 nodes)4,09632,768~25 GB/sDistributed design critical

How these numbers are derived: At ~3000 tokens/sec per GPU (typical LLM training throughput), each GPU consumes ~6 KB/s of pre-tokenized data (3000 tokens Γ— 2 bytes). Even 4,096 GPUs only need ~25 MB/s of raw token data. The actual I/O demand is higher due to metadata, shuffle reads, and reading ahead β€” but still far below raw storage bandwidth. The bottleneck at scale is metadata operations and concurrent file opens, not raw read bandwidth.

At mega-scale, thousands of workers opening files simultaneously creates metadata storms β€” file system metadata servers overwhelmed, directory listings stall, training hangs.


2. Shard-Based Data Distribution

Split the dataset into thousands of shards, assign disjoint shard sets to each worker:

Dataset: 100,000 shards (256 MB each = 25 TB total)

Node 0 (8 GPUs, 64 workers):  shards 0-99 (worker 0 reads shards 0-1, etc.)
Node 1 (8 GPUs, 64 workers):  shards 100-199
...
Node 99 (8 GPUs, 64 workers): shards 9900-9999

β†’ No two workers read the same shard simultaneously
β†’ No I/O contention on any single file

Deterministic Shard Assignment

# Each rank gets a deterministic subset of shards
def get_shards_for_rank(total_shards, rank, world_size, seed=42):
    rng = np.random.RandomState(seed)
    all_shards = rng.permutation(total_shards)
    return all_shards[rank::world_size]  # interleaved assignment

Deterministic: Same seed + same world_size β†’ same shard assignment. Enables reproducible training.


3. Global Shuffle Without Communication

True global shuffle across all shards would require all-to-all communication β€” impractical at scale. Instead, use hierarchical shuffling:

Level 1: Shuffle shard order per worker (each epoch, different order)
Level 2: Shuffle samples within each shard (in-memory)
Level 3: Shuffle within the local batch buffer

Result: approximate global shuffle with zero cross-node communication

Quality: This isn’t perfectly uniform, but empirically, shard-level + within-shard shuffle produces similar training quality to true global shuffle.


4. Avoiding I/O Hotspots

ProblemCauseFix
Metadata storm32K workers listing files simultaneouslyPre-compute file lists, distribute as config
Sequential reads of same fileMultiple workers assigned same shardDisjoint shard assignment
Bursty readsAll workers start new shards at same timeStagger shard boundaries (random offset)
Cache thrashingLocal cache smaller than working setIncrease cache or use streaming with eviction
Network congestionToo many concurrent S3 requestsRate-limit requests, use caching layer (Alluxio)

5. Checkpointing Data State

When training restarts from a checkpoint, the data loader must resume from the exact position:

# Save data state with training checkpoint
data_state = {
    'epoch': current_epoch,
    'shard_index': current_shard,
    'sample_index': current_sample_in_shard,
    'rng_state': dataloader.rng_state,
}
torch.save({**model_state, **data_state}, checkpoint_path)

# Resume: skip to saved position
dataloader.load_state_dict(data_state)

Without data state checkpointing: After restart, the model may re-train on data it already saw, or skip data β€” breaking the intended curriculum and wasting compute.

For the storage systems that serve this data, see Parallel File Systems and Cloud Object Storage.


Self-Check

  • Calculate total read bandwidth needed for a given GPU count and throughput
  • Design shard-based data distribution for 512 GPUs
  • Explain hierarchical shuffling and why it approximates global shuffle
  • Identify and fix 3 common I/O hotspot causes
  • Implement data state checkpointing for resumable training

References