Skip to content

Avoid shape-negotiation all_to_all in ShardTensor redistribute when shapes are known (ShardTensor Optimization #1) - #1779

Merged
coreyjadams merged 9 commits into
NVIDIA:mainfrom
negin513:fix/shardtensor-redistribute-alltoall
Jul 10, 2026
Merged

Avoid shape-negotiation all_to_all in ShardTensor redistribute when shapes are known (ShardTensor Optimization #1)#1779
coreyjadams merged 9 commits into
NVIDIA:mainfrom
negin513:fix/shardtensor-redistribute-alltoall

Conversation

@negin513

@negin513 negin513 commented Jul 2, 2026

Copy link
Copy Markdown
Member

Summary

TLDR: avoid an extra all_to_all when shapes are know! 2.5x speed-up.

While working on merging HealDA, we realized redistribute's Shard->Shard transpose does two all_to_all collectives instead of one -- the first just asks other ranks "what shape is your chunk?" before receiving anything.... The overhead from the extra all_to_all adds up fast for cases where we reshard repeatedly, such as HealDA where we do two reshards per block.

In most cases it doesn't need to ask at all: each rank's own chunk size is deterministic, and the sender's shape is already sitting on current_spec from when the tensor was constructed.

This PR calculates the recv-buffer shape locally instead of asking over the network: the target-dim chunk size is deterministic (every rank computes its own the same way), and the sender's current-dim extent is already recorded on current_spec from when the tensor was constructed. It only falls back to the original negotiation when that info genuinely isn't available.

Why the staleness check is added?

current_spec is fixed for the whole (possibly multi-hop) redistribute call, but a 2D mesh can chain multiple transform steps in one call (e.g. Shard/Shard -> Shard/Replicate gathers one mesh dim, then transposes the other). An earlier hop can already have reshaped local_tensor by the time a later hop runs, making the recorded shapes describe a state that no longer exists. Added a check that local_tensor.shape still matches what current_spec has on record before trusting the shortcut -- caught by test_shard_tensor_redistribute2d's S3+R case, which failed with a shape mismatch before this guard was added.

Compatibility

No caller changes needed -- this helps existing code as-is, including sharding_shapes="infer" (its default mode already records the needed shapes eagerly).

Performance

Isolated the redistribute primitive (4xGB200, T=16, X=12288, C=1536, bf16, even sharding, caller already passing known sharding_shapes so this measures only this fix's effect):

before after
fwd 1.849 ms/iter 0.763 ms/iter
fwd+bwd 5.131 ms/iter 3.131 ms/iter

Test plan

  • torchrun --nproc_per_node=4 -m pytest test/domain_parallel/test_redistribute.py --multigpu-static -q -- 11 passed (1D/2D mesh, even/uneven sharding), 11 skipped (dynamic-mode variants)
  • torchrun --nproc_per_node=4 -m pytest test/domain_parallel/ --multigpu-static -q -- confirmed identical set of 8 pre-existing, unrelated failures with and without this change
  • pre-commit (ruff check, ruff format, interrogate, license, large-file check) -- all pass on the changed file

Resolves NVIDIA/physicsnemo-roadmap#2761

Related

  • Found while profiling the context-parallel reshard benchmark in HealDA v2 Architecture #1758
  • Independent of ShardTensor Refactor #1556 (ShardTensor refactor for torch.compile support) -- that PR doesn't touch _shard_redistribute.py/_shard_tensor_spec.py, so this applies cleanly on top either way
  • Complements a follow-up fix (exposing global_shape on from_local to unlock its existing no-comm "chunk" path) -- that addresses a separate all_gather in from_local; this PR's fast path only activates once callers can avoid "infer", which that follow-up enables through the public API

Checklist

Review Process

All PRs are reviewed by the PhysicsNeMo team before merging.

Depending on which files are changed, GitHub may automatically assign a maintainer for review.

We are also testing AI-based code review tools (e.g., Greptile), which may add automated comments with a confidence score.
This score reflects the AI’s assessment of merge readiness and is not a qualitative judgment of your work, nor is
it an indication that the PR will be accepted / rejected.

AI-generated feedback should be reviewed critically for usefulness.
You are not required to respond to every AI comment, but they are intended to help both authors and reviewers.
Please react to Greptile comments with 👍 or 👎 to provide feedback on their accuracy.

negin513 added 3 commits July 1, 2026 17:31
Redistribute's Shard->Shard transpose negotiates recv-buffer shapes
with a collective every call, even though current_spec already
records each rank's shard shape whenever it was built with known
(non-"infer") sharding shapes. Passing current_spec through is prep
for computing those shapes analytically instead of over the network.
_to_new_shard_dim ran a full all_to_all just to learn recv-buffer
shapes before the real data-movement all_to_all -- doubling the
collective count for every Shard->Shard transpose. When current_spec
already has known per-rank shard shapes (and they're still fresh,
i.e. not stale from an earlier hop in the same multi-hop redistribute
call), derive recv shapes analytically instead: the target-dim chunk
size is rank-independent (every rank computes it the same way), and
the current-dim extent per sender is already on current_spec. Falls
back to the original negotiation otherwise.

Fixes a 2D-mesh multi-hop staleness case (test_shard_tensor_redistribute2d
S3+R) by verifying local_tensor's actual shape still matches what's
recorded before trusting it.
@negin513
negin513 requested a review from coreyjadams as a code owner July 2, 2026 01:35
@copy-pr-bot

copy-pr-bot Bot commented Jul 2, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coreyjadams

Copy link
Copy Markdown
Collaborator

Nice!

@greptile-apps

greptile-apps Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR eliminates a shape-negotiation all_to_all that preceded the actual data-movement all_to_all in the Shard→Shard reshape path (_to_new_shard_dim). When current_spec._sharding_shapes is populated and passes a freshness check, recv buffer shapes are now derived analytically; the original collective negotiation is preserved as a fallback.

  • The staleness guard (is_fresh) correctly catches multi-hop 2D-mesh scenarios where an earlier hop has already reshaped local_tensor, preventing stale spec data from poisoning the fast path.
  • The fast path unconditionally activates whenever _sharding_shapes is not None, which includes specs built via sharding_shapes=\"chunk\" (compute_sharding_shapes_from_chunking_global_shape). That helper has a pre-existing bug — it stores this rank's chunk size for every rank — so for uneven sharding (N % P != 0) the non-self entries are wrong, and the fast path would produce incorrect recv buffer sizes, leading to a runtime failure or data corruption in the subsequent all_to_all.
  • The fallback variable rename (recv_shapesrecv_shape_tensors) is a clean improvement that avoids shadowing the outer recv_shapes.

Important Files Changed

Filename Overview
physicsnemo/domain_parallel/_shard_redistribute.py Adds analytical recv-shape computation to _to_new_shard_dim, skipping a shape-negotiation all_to_all when current_spec._sharding_shapes is populated and passes a freshness check; fallback preserved for the infer/unknown-shapes case.

Reviews (1): Last reviewed commit: "Apply ruff format to the redistribute al..." | Re-trigger Greptile

Comment thread physicsnemo/domain_parallel/_shard_redistribute.py Outdated
Comment thread physicsnemo/domain_parallel/_shard_redistribute.py Outdated
@negin513 negin513 mentioned this pull request Jul 2, 2026
6 tasks

@coreyjadams coreyjadams left a comment

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.

This is, I think, the most complicated of the redistribute paths in ShardTensor. with dynamic shapes along multiple axes, this can get really complicated. I 1000% see the performance benefit of the fast path here and you are right to push for it, and I agree. What do you think about getting a little more aggressive on the testing for this particular op, and covering more scenarios? I think our static tests are completing in only a few minutes currently, we have a ton of runtime budget for more tests in multigpu-static and this is a great place to add logic checking.

Comment thread physicsnemo/domain_parallel/_shard_redistribute.py Outdated
negin513 added 3 commits July 6, 2026 21:10
… shapes

Address review: the recv-shape fast path was gated on a rank-local
comparison (recorded spec shape vs local_tensor.shape), which can
diverge across ranks (e.g. empty shards under uneven chunking) and
mismatch collectives. The staleness signal is now an explicit flag
threaded from the redistribute hop loop -- true only until the first
transform mutates the local tensors -- which depends only on the hop
sequence and is therefore identical on every rank. The shape
comparison is kept as a loud error for corrupt specs.

Also fixes compute_sharding_shapes_from_chunking_global_shape to
record each rank's true chunk size along the varying mesh dim; it
previously wrote this rank's chunk size into every per-rank entry,
which was only correct for evenly divisible sharding.

Adds multigpu-static coverage: even and uneven sharding for the 1D
and multi-hop 2D transpose paths, empty-shard redistribution, and a
fast-path vs negotiated-fallback equivalence test.
@negin513
negin513 requested a review from coreyjadams July 9, 2026 23:25
@negin513 negin513 changed the title Avoid shape-negotiation all_to_all in ShardTensor redistribute when shapes are known Avoid shape-negotiation all_to_all in ShardTensor redistribute when shapes are known (ShardTensor Optimization #1) Jul 9, 2026
@negin513

negin513 commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

Re-ran the isolated redistribute benchmark against the final branch state (209b1801, after the rank-uniform refactor), true main-vs-branch:

Platform / benchmark main PR #1779 speedup
4×GB200, fwd 1.849 0.763 2.42×
4×GB200, fwd+bwd 5.131 3.131 1.64×
8×H100, fwd 1.138 0.415 2.74×
8×H100, fwd+bwd 3.538 2.608 1.36×

Per review: use the available runtime budget for more aggressive logic
checking on the most complicated redistribute path.

- Fast-path vs negotiated-fallback bitwise equivalence on 2D multi-hop
  paths (pure transpose, transpose chain, transpose-then-gather), with
  both tensor dims rank-dependent, even and uneven.
- Empty shard along one mesh dim combined with uneven sharding along
  the other, through the transpose chain and double-gather paths.
- Backward through the transpose: fast path and fallback must produce
  bitwise-identical gradients (covers the autograd reverse path).
- Adds a skip-marked regression test documenting a pre-existing crash
  (reproduced on main @ 8e76840): resharding onto a tensor dim with
  extent smaller than the mesh size segfaults in the all_to_all.
@negin513

Copy link
Copy Markdown
Member Author

Resolves NVIDIA/physicsnemo-roadmap#2761 and NVIDIA/physicsnemo-roadmap#828 | relevant for HealDA (NVIDIA/physicsnemo-roadmap#2172)

@negin513

Copy link
Copy Markdown
Member Author

Added more tests in 356c400. @coreyjadams I think this should be ready for re-review.

@negin513
negin513 enabled auto-merge July 10, 2026 00:32
@negin513

Copy link
Copy Markdown
Member Author

/blossom-ci

@negin513

Copy link
Copy Markdown
Member Author

/ok to test 356c400

@coreyjadams coreyjadams added the ci:multi-gpu Run this PR on multiGPU ci label Jul 10, 2026
@coreyjadams
coreyjadams disabled auto-merge July 10, 2026 01:11
@coreyjadams

Copy link
Copy Markdown
Collaborator

Disabling auto merge exclusively so we can refresh the CI label and run multi-gpu CI on this. Looks good to go, let's make sure it passes the CI here before it merges. :)

@coreyjadams

Copy link
Copy Markdown
Collaborator

Once another PR lands, and we update this one, it will refresh and we can queue it.

@coreyjadams

Copy link
Copy Markdown
Collaborator

/ok to test f522d04

@coreyjadams
coreyjadams enabled auto-merge July 10, 2026 01:56
@coreyjadams
coreyjadams added this pull request to the merge queue Jul 10, 2026
Merged via the queue into NVIDIA:main with commit 22ec11b Jul 10, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:multi-gpu Run this PR on multiGPU ci

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants