Skip to content

Unmanaged gradient accumulation: ZeRO offload support - #8225

Open
sfc-gh-truwase wants to merge 5 commits into
masterfrom
sfc-gh-truwase/gas_mgmt_offload
Open

Unmanaged gradient accumulation: ZeRO offload support#8225
sfc-gh-truwase wants to merge 5 commits into
masterfrom
sfc-gh-truwase/gas_mgmt_offload

Conversation

@sfc-gh-truwase

Copy link
Copy Markdown
Collaborator

Summary

  • Extends unmanaged gradient accumulation (managed_gradient_accumulation=false) to ZeRO optimizer-state and parameter offload (CPU/NVMe). Follow-up to Unmanaged gradient accumulation: ZeRO stage 3 support #8217 (ZeRO stage 3, now merged).
  • Stage 2/3: grads still reduce/partition every backward(); step() finalizes deferred offload boundary work (grad norms + FP32/NVMe copy) via finalize_gradient_accumulation_boundary().
  • Stage 1: continues to reduce at step() via allreduce_gradients(), which already performs offload boundary finalization when the boundary flag is true.
  • Pipeline parallelism, DeepCompile, Apex AMP, and stage-0/1 overlap_comm remain unsupported.

Test plan

Validated on a 2-GPU node:

  • Full -k Unmanaged suite (31 passed), including:
    • test_unmanaged_matches_managed_optimizer_offload[1|2|3]
    • test_unmanaged_matches_managed_param_offload (stage 3)
    • existing non-offload unmanaged equivalence / varying-GAS / rejection tests
  • Docs updated (config-json.md, training.rst); previewable on rtd-staging

Made with Cursor

Allow managed_gradient_accumulation=False with ZeRO optimizer-state and
parameter offload (CPU/NVMe). Stage 2/3 defer boundary norm and FP32/NVMe
gradient copy into finalize_gradient_accumulation_boundary(); stage 1
continues to finalize via allreduce_gradients() at step(). Docs and
equivalence tests cover stages 1-3 optimizer offload and stage-3 param
offload.

Validated on a 2-GPU node (full -k Unmanaged suite, 31 passed) incl.
optimizer offload [1,2,3] and param+optimizer offload stage 3.

Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d3dcf9ad4e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread docs/_pages/config-json.md
Comment thread deepspeed/runtime/zero/stage_1_and_2.py Outdated
Comment thread deepspeed/runtime/zero/stage3.py
@sfc-gh-truwase
sfc-gh-truwase requested review from delock and stas00 August 6, 2026 16:11
sfc-gh-truwase and others added 3 commits August 7, 2026 01:31
Track params reduced in the current accumulation window and only finalize
those at step(), matching managed offload (which only copies grads that
flow through the boundary backward). Prevents inactive ZeRO-2/3 params
from being updated from leftover CPU/partition buffers. Adds a regression
test that alternates active heads across windows.

Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Cover optimizer NVMe and param+optimizer NVMe paths against managed mode,
skipping when async-io is unavailable. Shared helper drives CPU and NVMe
offload cases; NVMe uses a large enough model to exercise swap_out_gradients.

Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Resolve stage3.py conflict with async grad offload (#8207): keep
unmanaged boundary helper and retain non_blocking FP32 grad copy.

Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread deepspeed/runtime/zero/stage3.py Outdated
assert not self.offload_optimizer and not self.offload_param, \
"unmanaged gradient accumulation does not support ZeRO offload"
# Unmanaged mode: partitions already accumulate in __param_id_to_grad_partition; offload still needs deferred boundary copy.
self.is_gradient_accumulation_boundary = True

@delock delock Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It looks like self.is_gradient_accumulation_boundary is not consumed in this path, is it necessary to set it to true? Maybe add a comments in initilization of this flag is for managed code only will help understanding.

continue
grad_buffer = self.__param_id_to_grad_partition[param.ds_id]
if not get_accelerator().on_accelerator(grad_buffer):
grad_buffer = grad_buffer.to(get_accelerator().current_device_name(), non_blocking=True)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think whether moving grads from host to device for normalization and fp32 conversion depends on host capability. Do we consider make it an option in the furture?

@delock

delock commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Hi @sfc-gh-truwase , I have left my comments. One impression is self.is_gradient_accumulation_boundary might need some refactor. Its a flag variable that could be injected from engine, and no longer used in stage 2/3 unmanaged code. Proper comments and set/get interface would be better than expose as variable.

Comment thread deepspeed/runtime/zero/stage_1_and_2.py Outdated
# Unmanaged mode: grads were reduced/accumulated into all_grad_tensors each backward; finalize averaged_gradients for step().
assert not self.cpu_offload, "unmanaged gradient accumulation does not support ZeRO optimizer state offload"
# Unmanaged mode: grads accumulated each backward; finalize for step() (averaged_gradients or offload fp32 copy).
self.is_gradient_accumulation_boundary = True

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Same here, this variable seems not used in following path.

Move optimizer is_gradient_accumulation_boundary behind
ZeROOptimizer get/set methods so the engine mirrors boundary
state through an explicit API, with comments clarifying managed
vs unmanaged use.

Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
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.

2 participants