Skip to content

Fix lost TP all-reduce results for non-contiguous tensors - #8837

Open
jinyouzhi wants to merge 2 commits into
deepspeedai:masterfrom
jinyouzhi:fix-tp-contiguous-allreduce
Open

jinyouzhi wants to merge 2 commits into
deepspeedai:masterfrom
jinyouzhi:fix-tp-contiguous-allreduce

Conversation

@jinyouzhi

@jinyouzhi jinyouzhi commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Problem

dist.all_reduce reduces its argument in place. Three tensor-parallel autograd functions in deepspeed/module_inject/layers.py call dist.all_reduce(x.contiguous(), ...) and then return x. When x is not contiguous, .contiguous() returns a copy. The reduction lands in that copy and is discarded, so the TP sum is silently skipped:

  • RowParallel.forward (training path): the forward output is not reduced.
  • ColumnParallel.backward: the input gradient is not reduced, e.g. when the output is transposed before the loss.
  • AsyncColumnParallel.backward: grad_input comes from matmul and is normally contiguous, but it uses the same pattern, so it is fixed the same way.

In-tree AutoTP layers only hand these functions matmul / embedding outputs and their gradients, which are contiguous, so the bug is latent there today. It is hit by any caller that passes a non-contiguous tensor, such as a custom TP layer. A training-loop test through LinearLayer passes with or without this fix, so the regression tests target the autograd functions directly.

Fix

Assign the contiguous tensor first, reduce it, and return it.

Tests

tests/unit/module_inject/test_tp_noncontiguous_reduce.py (2 ranks) feeds a transposed input to RowParallel and a transposed gradient to ColumnParallel, and checks that the result is the sum over ranks (1 + 2 = 3).

  • Before the fix: both tests fail; each rank keeps only its own value.
  • After the fix: passes, together with test_tp_shard.py (17 passed).

Run on 2× Intel Arc Pro B70 (XPU, XCCL), torch 2.14.0.dev20260804+xpu.

Contribution checklist

Related work. A search of open and closed PRs found no existing fix for this.

Sibling paths checked. git grep 'all_reduce(<x>.contiguous()' over deepspeed/ finds only the three runtime sites fixed here, all in module_inject/layers.py. The fourth match, ColumnParallel.symbolic, only runs during ONNX tracing and is left unchanged. The inference_all_reduce branch of RowParallel passes the tensor itself, so it is not affected.

Commands and results. Hardware and software: 2× Intel Arc Pro B70 (XPU, XCCL), Level Zero driver 1.15.39122 (compute runtime 26.27.39122.11), torch 2.14.0.dev20260804+xpu.

pytest -p no:randomly tests/unit/module_inject/test_tp_noncontiguous_reduce.py tests/unit/module_inject/test_tp_shard.py
  • Before the fix, both new tests fail.
  • After the fix, everything passes (test_tp_shard.py: 17 passed).

Numerics, performance, memory. Nothing changes for contiguous inputs, which is the case for every in-tree AutoTP layer: .contiguous() returns the same tensor. For non-contiguous inputs, the result is now the reduced tensor, which is the intended behavior.

Compatibility matrix. Validated: TP=2, fp32, at the autograd-function level. ZeRO stage and offload do not interact with this code. Not validated: CUDA, HPU.

AI assistance. This PR was developed with AI assistance (GitHub Copilot CLI). I reviewed every changed line and ran the tests above.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-16T10:09:52.990367Z 0b7f6d4 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

dist.all_reduce(x.contiguous()) reduces a temporary copy when x is not
contiguous, so the result is lost. Keep the contiguous tensor and return it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Jin, Youzhi <youzhi.jin@intel.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: 0b7f6d4d96

ℹ️ 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".

x = torch.ones(3, 2, device=get_accelerator().device_name(), requires_grad=True)

# The transpose makes the incoming gradient non-contiguous.
out = ColumnParallel.apply(group, x).t()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Exercise the reduction through an AutoTP training loop

test_column_parallel_backward calls ColumnParallel.apply directly, but actual AutoTP training reaches this reduction through LinearLayer.forward's process-group, overlap, and deferred-collective routing, so this test can pass without showing that a transposed downstream loss receives the fix through the user-facing path. Add a minimal injected-model forward/backward test whose layer output is transposed before the loss; training-loop contract changes require integration coverage rather than only primitive unit coverage.

AGENTS.md reference: AGENTS.md:L76-L81

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks. I tried this, but an AutoTP-level test cannot reach the bug, so it would not guard the fix.

In-tree, these primitives only receive freshly computed tensors: LinearLayer / SubParamLinearLayer wrap their input in ColumnParallel, whose backward gradient comes from matmul's backward, and LinearAllreduce / VocabParallelEmbedding / SubParamLinearAllreduce feed RowParallel a matmul / embedding output. Those are contiguous whatever the caller does with the layer's input or output. I checked that transposed, expanded and permuted inputs and a transposed loss all still deliver a contiguous gradient to ColumnParallel.backward.

To confirm, I ran the suggested test (a LinearLayer -> LinearAllreduce stack over a 2-rank TP group, output transposed before the loss, input gradient compared with an unsharded reference) against the code before this fix:

  • integration test: passes (cannot detect the bug)
  • test_tp_noncontiguous_reduce.py: both tests fail (each rank keeps only its own value)

So the bug is latent for AutoTP layers today. It is reachable whenever these autograd functions get a non-contiguous tensor, e.g. a custom TP layer or a future caller, which is exactly the contract the primitive tests pin down. I kept the regression tests at that level, since a training-loop test would pass with or without the fix.

@jinyouzhi

Copy link
Copy Markdown
Contributor Author

@delock It's a small bug found during comm refactor #8815, please have a look when you available. Thanks

This branch has not been deployed

No deployments
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