deepspeedai / deepspeedai/DeepSpeed

[REQUEST] Dynamic/Adaptive Prefetching Window for ZeRO-3

Open
#8,193 3 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
Python
Stars
43.1k
Forks
5k
Avg merge
4d 15h
Merged PRs (30d)
112

Description

Problem I have Faced
I am frustrated by the fact that __prefetch_bucket_sz in ZeRO-3 is a static, user-configured value. In large-scale training, network bandwidth can fluctuate and computation step times vary. A static bucket size creates two distinct bottlenecks:

Compute Starvation: If the bucket is too small, the network cannot fetch parameters fast enough, leading to CPU/GPU blocking when param.wait() is called.
Memory Pressure: If the bucket is too large, it aggressively materializes too many parameters in advance, causing memory spikes, eviction thrashing, or OOM errors.

Solution I thought:
I would like to introduce an Adaptive/Dynamic Prefetching Window to the PartitionedParameterCoordinator.

The proposed solution involves:

Stall Tracking: Using host-side time.time() around the .wait() loop to measure communication wait time, and tracking compute time as the duration between consecutive module fetches.
Dynamic Scaling: Calculating a moving average of the stall_ratio (wait time / compute time).
Adjustment Policy:
Scale Up: If stall_ratio > 0.05, we scale __prefetch_bucket_sz by a factor (e.g., 1.2x), bounding it to a maximum of __max_n_available_params * 0.5. This aggressively prefetches further ahead in the trace to hide latency.
Scale Down: If stall_ratio is near 0.0, we slowly decay the bucket size down to the baseline size to conserve memory pressure when the network is fast enough.

Alternatives I saw
The only current alternative is manually tuning prefetch_bucket_size for every specific hardware configuration and model, which does not account for real-time network fluctuations during training. We also considered tying prefetch scaling to CUDA events directly, but determined host-side timing avoids injecting unnecessary synchronization overhead.

Additional context Per the new feature contribution guidelines, I am planning the following experiments to demonstrate effectiveness before submitting the Pull Request:

Task: Fine-tuning BERT on SQuAD (NLP) and ResNet on CIFAR (CV) using the DeepSpeedExamples repo.
Metrics to validate:
Total throughput improvements (samples/sec) compared to the static baseline.
Peak GPU memory allocation (to ensure the dynamic scaling respects memory limits).
Validation loss over a subset of steps to verify convergence correctness is maintained.
I have already prototyped this logic locally and would appreciate feedback from the core team on this design before opening the Pull Requests!

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the PartitionedParameterCoordinator and inspect the __prefetch_bucket_sz, __max_n_available_params, and .wait() loop mentioned in the proposal. Evaluate the host-side stall and compute timing approach, then validate the design with BERT on SQuAD and ResNet on CIFAR in DeepSpeedExamples. Done means demonstrating throughput and memory effects while preserving validation loss and convergence correctness.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
distributed-systems, machine-learning, performance
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.