Skip to content

Generalized Tensor Parallelism (GTP) #3005

Open
fanshiqing wants to merge 17 commits into
NVIDIA:mainfrom
fanshiqing:gtp_release
Open

Generalized Tensor Parallelism (GTP) #3005
fanshiqing wants to merge 17 commits into
NVIDIA:mainfrom
fanshiqing:gtp_release

Conversation

@fanshiqing

@fanshiqing fanshiqing commented May 18, 2026

Copy link
Copy Markdown
Member

Description

Core-idea: add Generalized Tensor Parallelism (GTP), which is a flexible fine-grained sharding/just-in time materialization of both activations and parameters with efficient computation-communication overlap.

Mission: improve LLM pretraining efficiency through generalized tensor parallelism, enabling high performance, memory efficiency, ease of use, and strong scalability.

GTP introduction

GTP_introduction.md

How Mcore interacts with TE

• TE ships self-contained: Protocol + dispatcher no-op.
• GTP lives 100% in Mcore as one protocol implementer.

image

Type of change

  • Documentation change (change only to the documentation, either a fix or a new content)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Infra/Build change
  • Code refactoring

Changes

Please list the changes introduced in this PR:

  • distributed_weight.py (NEW): the DistributedWeight protocol + dispatchers — TE's GTP-agnostic extension point.
  • module/linear.py: rewired fwd/bwd to is_distributed_weight + dispatchers;
  • module/layernorm_linear.py: same protocol rewiring as linear.
  • module/grouped_linear.py: same, for the grouped N-weight case.
  • ops/fused/grouped_mlp.py: protocol rewiring; FC2 gather split; FC1 NVFP4 dgrad materialized before the precision dispatch.
  • distributed.py: added general external_coalescing flag; reduce_scatter output=; async NVFP4 handle.
  • tests/pytorch/test_distributed_weight.py (NEW): 7 GPU-free protocol/dispatcher tests.

Checklist:

  • I have read and followed the contributing guidelines
  • The functionality is complete
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

@greptile-apps

greptile-apps Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a GTP-agnostic DistributedWeight protocol and dispatcher layer to TE, allowing external frameworks (e.g., Megatron GTP) to inject sharded weight implementations without TE changes. The protocol is wired into Linear, LayerNormLinear, GroupedLinear, and the fused GroupedMLP, covering both forward materialization and backward reduce-scatter finalization.

  • New file distributed_weight.py: Defines the DistributedWeight structural protocol (materialize_group_for_forward, materialize_group_for_backward, finalize_group_grads, grad_buffer) and three dispatchers that are no-ops on plain tensors.
  • Module rewiring: All four module paths now detect distributed weights via is_distributed_weight, gather in forward, re-gather in backward, and call finalize_weight_grads for the RS step; the quantized workspace is intentionally not saved, deferring re-quantization to backward.
  • distributed.py additions: output_tensor and external_coalescing args for CUDA-graph replay and batched gathers; null-safety guard on _NVFP4AllGatherAsyncHandle.wait(); _post_process_nvfp4_gather now uses attribute rebinding instead of .copy_(), fixing previously-flagged shape mismatches.

Confidence Score: 3/5

The grouped MLP backward path silently discards the return value of finalize_weight_grads and relies on an undocumented in-place side-effect; linear.py still lacks the GTP guard before the dummy-wgrad section that layernorm_linear.py has.

Two issues on the changed backward paths lower confidence. In _compute_grad_params, discarding finalize_weight_grads's return value and returning dummy wgrads is only correct if finalize_group_grads writes the RS result into main_grad in-place — a contract not documented in the protocol or exercised by any test. In linear.py, the custom-DDP dummy-wgrad branch still runs for GTP weights that expose grad_added_to_main_grad, overwriting the reduce-scattered shard; layernorm_linear.py has the guard but linear.py does not. Several of the fixes in this version (FC1 nvfp4 regather ordering, _post_process_nvfp4_gather rebinding vs copy, null-guard on async_handle) are meaningful improvements, but the unresolved asymmetry in linear.py and the undocumented side-effect contract in the grouped MLP path mean this needs another review pass before merging.

transformer_engine/pytorch/ops/fused/grouped_mlp.py (_compute_grad_params finalize_weight_grads call) and transformer_engine/pytorch/module/linear.py (missing GTP guard before the dummy-wgrad block)

Important Files Changed

Filename Overview
transformer_engine/pytorch/distributed_weight.py New file: clean protocol definition and three dispatchers; is_distributed_weight enforces the Tensor-subclass contract loudly. Protocol docstring should mention whether finalize_group_grads is expected to write to main_grad in-place (required by the grouped-MLP path).
transformer_engine/pytorch/distributed.py Adds output_tensor/external_coalescing args and null-safety fix in _NVFP4AllGatherAsyncHandle.wait(); _post_process_nvfp4_gather now uses = rebinding instead of .copy_(), resolving the previously-flagged shape-mismatch on unaligned K dimensions.
transformer_engine/pytorch/module/linear.py GTP forward/backward wiring is mostly correct, but the custom-DDP dummy-wgrad branch (lines 1332–1350) still lacks a GTP guard; a GTPShardedParam with grad_added_to_main_grad will overwrite the finalized reduce-scattered wgrad with a dummy (flagged in multiple previous reviews, unresolved).
transformer_engine/pytorch/module/layernorm_linear.py GTP wiring follows the same pattern as linear.py; correctly skips the dummy-wgrad branch for distributed weights and guards origin_weight.main_grad assignment.
transformer_engine/pytorch/module/grouped_linear.py GTP integration follows the two-path structure (grouped-tensor path vs main path); GTP is correctly restricted to the non-grouped-tensor path via use_grouped_tensor_path=False. The new is_dist_weight gate before finalize_weight_grads in the main backward correctly assigns the result.
transformer_engine/pytorch/ops/fused/grouped_mlp.py FC1 backward materialization now happens before the use_nvfp4 branch (fixing a previously flagged bug); FC2 gather is correctly deferred past the FC1 kernel for overlap. However, finalize_weight_grads return value is silently discarded in _compute_grad_params, creating an undocumented in-place side-effect dependency on the DistributedWeight implementation.
tests/pytorch/test_distributed_weight.py New GPU-free test covering protocol detection, forward/backward dispatch, and finalize-grad dispatch. FakeDistributedWeight.finalize_group_grads creates new tensors without any in-place main_grad write, which means the side-effect behavior required by _compute_grad_params in grouped_mlp.py is not exercised by any test.
qa/L0_pytorch_unittest/test.sh Adds the new test suite to the CI script. Straightforward one-line addition.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Module as TE Module (Linear/LNL/GL)
    participant DW as distributed_weight.py
    participant Impl as DistributedWeight impl (e.g. GTPShardedParam)
    participant GEMM as CUDA GEMM kernel
    participant Dist as distributed.py

    Note over Module,Dist: Forward pass
    Module->>DW: materialize_weight_for_forward(weight)
    DW->>Impl: leader.materialize_group_for_forward()
    Impl->>Dist: "gather_along_first_dim(shard, group, external_coalescing=True)"
    Dist-->>Impl: gathered_weight, handle
    Impl-->>DW: gathered_weight(s)
    DW-->>Module: [gathered_weight, ...]
    Module->>GEMM: GEMM(input, gathered_weight)
    GEMM-->>Module: output

    Note over Module,Dist: Backward pass
    Module->>DW: materialize_weight_for_backward(saved_sharded_weight)
    DW->>Impl: leader.materialize_group_for_backward()
    Impl->>Dist: gather_along_first_dim(shard, group)
    Dist-->>Impl: gathered_weight
    Impl-->>DW: gathered_weight(s)
    DW-->>Module: [gathered_weight, ...]
    Module->>GEMM: wgrad_GEMM(input, grad_output)
    GEMM-->>Module: wgrad
    Module->>DW: finalize_weight_grads(saved_weight, [wgrad])
    DW->>Impl: leader.finalize_group_grads(wgrad)
    Impl->>Dist: "reduce_scatter_along_first_dim(wgrad, group, output=main_grad)"
    Dist-->>Impl: sharded_wgrad, handle
    Impl-->>DW: sharded_wgrad
    DW-->>Module: [sharded_wgrad]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Module as TE Module (Linear/LNL/GL)
    participant DW as distributed_weight.py
    participant Impl as DistributedWeight impl (e.g. GTPShardedParam)
    participant GEMM as CUDA GEMM kernel
    participant Dist as distributed.py

    Note over Module,Dist: Forward pass
    Module->>DW: materialize_weight_for_forward(weight)
    DW->>Impl: leader.materialize_group_for_forward()
    Impl->>Dist: "gather_along_first_dim(shard, group, external_coalescing=True)"
    Dist-->>Impl: gathered_weight, handle
    Impl-->>DW: gathered_weight(s)
    DW-->>Module: [gathered_weight, ...]
    Module->>GEMM: GEMM(input, gathered_weight)
    GEMM-->>Module: output

    Note over Module,Dist: Backward pass
    Module->>DW: materialize_weight_for_backward(saved_sharded_weight)
    DW->>Impl: leader.materialize_group_for_backward()
    Impl->>Dist: gather_along_first_dim(shard, group)
    Dist-->>Impl: gathered_weight
    Impl-->>DW: gathered_weight(s)
    DW-->>Module: [gathered_weight, ...]
    Module->>GEMM: wgrad_GEMM(input, grad_output)
    GEMM-->>Module: wgrad
    Module->>DW: finalize_weight_grads(saved_weight, [wgrad])
    DW->>Impl: leader.finalize_group_grads(wgrad)
    Impl->>Dist: "reduce_scatter_along_first_dim(wgrad, group, output=main_grad)"
    Dist-->>Impl: sharded_wgrad, handle
    Impl-->>DW: sharded_wgrad
    DW-->>Module: [sharded_wgrad]
Loading

Reviews (24): Last reviewed commit: "fix comments" | Re-trigger Greptile

Comment thread transformer_engine/pytorch/module/generalized_tensor_parallelism.py Outdated
Comment thread transformer_engine/pytorch/module/grouped_linear.py Outdated
Comment thread transformer_engine/pytorch/module/grouped_linear.py Outdated
Comment thread transformer_engine/pytorch/module/generalized_tensor_parallelism.py Outdated
Comment thread transformer_engine/pytorch/module/generalized_tensor_parallelism.py Outdated
Comment thread transformer_engine/pytorch/module/generalized_tensor_parallelism.py Outdated
@fanshiqing

Copy link
Copy Markdown
Member Author

/te-ci L1 pytorch

Comment thread transformer_engine/pytorch/distributed.py
Comment thread transformer_engine/pytorch/csrc/extensions/cast.cpp Outdated
Comment thread transformer_engine/pytorch/module/grouped_linear.py Outdated
Co-authored-by: Jieming Zhang <jiemingz@nvidia.com>
Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
Comment thread transformer_engine/pytorch/distributed.py
Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
Comment thread transformer_engine/pytorch/module/base.py Outdated
Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
Comment thread transformer_engine/pytorch/distributed.py Outdated
Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
@greptile-apps

greptile-apps Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Want your agent to iterate on Greptile's feedback? Try greploops.

Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
pre-commit-ci Bot and others added 3 commits June 27, 2026 15:31
…ped MLP"

This reverts commit 8bb26f0.

Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
…red weights

Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
Comment thread transformer_engine/pytorch/module/linear.py
Comment thread transformer_engine/pytorch/distributed.py Outdated
Comment thread transformer_engine/pytorch/distributed.py Outdated
Comment thread transformer_engine/pytorch/ops/fused/grouped_mlp.py Outdated
Comment thread transformer_engine/pytorch/distributed.py Outdated
Comment thread transformer_engine/pytorch/distributed.py Outdated
Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
Comment thread transformer_engine/pytorch/ops/fused/grouped_mlp.py Outdated
Comment thread transformer_engine/pytorch/distributed.py Outdated
Comment thread transformer_engine/pytorch/distributed.py Outdated
- Take a single leader weight in the DistributedWeight dispatchers
- Gather the FC2 grouped weight late in the fused grouped MLP

Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
…ht before the NVFP4 dgrad dispatch

Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
- Rename gather coalescing flag grouped -> external_coalescing;
- Clean up DistributedWeight wiring in TE modules
- Restructure _all_gather_nvfp4

Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We also need to support GTP in ops.GroupedLinear. If the op fusion fails (e.g. if we run with a new activation function or on an unsupport GPU arch), we should gracefully fall back to the unfused implementation.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes you are right, we haven't support GTP in ops.GroupedLinear yet because for now I just focus on the critical path for Nemotron3/Nemotron4 pretraining on GB200/GB300.

I suggest keep the current changes minimal for this turn's review convenience and surely we can that support once any user fall back into the gtp-supported branch. How do you think?

Comment thread transformer_engine/pytorch/distributed_weight.py
Comment thread transformer_engine/pytorch/module/grouped_linear.py Outdated
Comment thread transformer_engine/pytorch/module/grouped_linear.py Outdated
Comment thread tests/pytorch/test_distributed_weight.py
@timmoon10

Copy link
Copy Markdown
Member

/te-ci pytorch L1

Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
Comment on lines +641 to 649
# Distributed weight: reduce-scatter the full per-rank wgrads into each sharded
# main_grad (also fires the Megatron grad-accum hook).
if is_dist_weight:
finalize_weight_grads(weights[0], w_list)

# Need to return dummy wgrads for Megatron-LM wgrad fusion if grad is already added
if fc_op._accumulate_into_main_grad:
# (wgrad fusion, or the distributed-weight reduce-scatter above) so it doesn't double-add.
if fc_op._accumulate_into_main_grad or is_dist_weight:
w_list = get_dummy_wgrads_for_params(weights)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 finalize_weight_grads return value silently discarded — undocumented side-effect contract

finalize_weight_grads(weights[0], w_list) is called for its side effect (reduce-scatter into main_grad) and its return value is immediately replaced by dummy wgrads at line 649. Every other call site (linear.py, layernorm_linear.py, grouped_linear.py) assigns the return value and uses it as the actual wgrad.

This pattern is correct only if finalize_group_grads also writes the reduce-scattered result into main_grad in-place (the "Megatron grad-accum hook" path). Nothing in the DistributedWeight protocol spec or FakeDistributedWeight test stub documents or exercises this in-place side-effect requirement. A DistributedWeight implementation that follows the documented return-value-only contract (as FakeDistributedWeight does — it creates new tensors without touching any main_grad buffer) would have its per-expert gradients silently discarded on the grouped MLP path while the other module paths continue to work correctly. The discrepancy is invisible because _compute_grad_params returns dummy tensors regardless.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@fanshiqing

Copy link
Copy Markdown
Member Author

/te-ci pytorch L1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants