Skip to content

[GTP] NCCL Symmetric Memory Registration Support - #6155

Draft
prajwal1210 wants to merge 2 commits into
NVIDIA:mainfrom
prajwal1210:gtp_symm_on_main
Draft

[GTP] NCCL Symmetric Memory Registration Support#6155
prajwal1210 wants to merge 2 commits into
NVIDIA:mainfrom
prajwal1210:gtp_symm_on_main

Conversation

@prajwal1210

@prajwal1210 prajwal1210 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor
  • I, the PR author, have personally reviewed every line of this PR.

What does this PR do?

Adds NCCL symmetric memory/user buffer registration support for GTP

Summary

Symmetric memory registration allows for the use of faster LSA/GIN based and CE based collectives in NCCL to improve all-gather and reduce-scatter communication performance for GTP. To enable these new kernels, NCCL requirements state:

  • All-gathers: Input and Output both need to backed by symmetric memory registration
  • Reduce-scatter: Input needs to be backed by symmetric memory registration but output does not.

Based on these requirements, this MR enables symmetric memory backed tensors gated by two flags:

Flag Scope
--gtp-nccl-ub dense GTP remat group
--egtp-nccl-ub routed-expert (EGTP) group
These are independent of independent of --use-nccl-ub and off by default.

Design

One ncclMemAlloc-backed torch.cuda.MemPool per GTP process group, registered once with
register_mem_pool(pool, symm=True). PyTorch's ProcessGroupNCCL segment hook then auto-registers
every later allocation made under gtp_mem_pool_ctx(group), so the context manager itself is
collective-free and capture-safe.

Every buffer either collective touches, where it comes from, and what puts it in the pool:

Buffer Where it comes from What puts it in the pool
AG send — MXFP8 + --fp8-param-gather the param's own TE quantized storage pool context wrapping the TE constructor
AG send — BF16 + distributed optimizer DDP param_data register_ddp_buffers_on_gtp_groups
AG send — BF16, no distributed optimizer the sliced shard's own storage _gtp_slice_one_param
AG recv GTPWeightCache ticket buffer _allocate_buffer(rs=False) under the pool context
RS send RegisteredLifoPool, padded full shape alloc() under the pool context
RS recv GTPWeightCache ticket buffer nothing — _graphed_alloc, stays out of the window
Screenshot 2026-07-30 at 10 09 39 AM

Contribution process

Pre-checks

  • I have added relevant unit tests
  • I have added relevant functional tests
  • I have added proper typing to my code Typing guidelines
  • I have added relevant documentation
  • I have run the autoformatter.sh on my PR

prajwal1210 and others added 2 commits July 29, 2026 09:52
Register the GTP all-gather buffers and the DDP param buffer that feeds them
into NCCL symmetric-memory pools on the GTP remat group, so both ends of the
gather sit in the same window and NCCL can select NVLS. Gated by
--gtp-nccl-ub / --egtp-nccl-ub, independent of --use-nccl-ub, which covers the
DP group only.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Prajwal Singhania <psinghania@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Jul 30, 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.

@prajwal1210

Copy link
Copy Markdown
Contributor Author

/claude strict-review

default=False, help='Register dense GTP param buffers with NCCL symmetric memory on the GTP '
'group, independent of --use-nccl-ub (which covers the DP group).')
group.add_argument('--egtp-nccl-ub', action='store_true', dest='egtp_nccl_ub',
default=False, help='Like --gtp-nccl-ub but for routed-expert (EGTP) groups.')

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.

[IMPORTANT Compatibility] The new --gtp-nccl-ub / --egtp-nccl-ub flags create torch.cuda.MemPool objects (via create_nccl_mem_pool(symmetric=True) in gtp_symm.get_gtp_pool and the new distopt branch in param_and_grad_buffer.py), but they bypass the compatibility guard that --use-nccl-ub gets in DistributedDataParallelConfig.__post_init__:

if self.nccl_ub and not is_torch_min_version("2.11.0a0"):
    if 'expandable_segments:True' in os.getenv('PYTORCH_CUDA_ALLOC_CONF', '').split(','):
        raise ValueError("PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True is currently not supported with nccl_ub ...")

Why it matters: A user running --gtp-nccl-ub on torch < 2.11 with PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True hits the same underlying torch.cuda.MemPool incompatibility, but instead of the friendly ValueError gets a confusing low-level crash. Since the gtp_symm module docstring already tells users to export NVLS env vars, this combination is realistic.

Suggestion: Add an equivalent validation for the GTP flags (in validate_args, since these live in the arg namespace rather than the DDP config) that raises the same clear error when gtp_nccl_ub or egtp_nccl_ub is set with expandable_segments:True on an unsupported torch version.

# Expert vs dense by group identity check
_expert_group = ProcessGroupCollection.use_mpu_process_groups(
required_pgs=["expt_gtp_remat"]
).expt_gtp_remat

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.

[SUGGESTION Simplification] _gtp_attach_attrs runs once per GTP param, and each call constructs a fresh ProcessGroupCollection via use_mpu_process_groups(required_pgs=["expt_gtp_remat"]) just to read the constant expert group and do an identity compare. This rebuilds the collection and re-reads the MPU global for every parameter in the model.

Why it matters: It's construction-time (not per-forward), so throughput is unaffected — but it's redundant object churn and an extra parallel_state global read per param. The expert group is invariant across the whole model build.

Suggestion: Resolve the expert group once (e.g. cache it in GTP_CONFIG at configure_gtp_remat_from_recipe time, where a ProcessGroupCollection is already materialized) and have _gtp_attach_attrs compare against the cached group. This also aligns with the CLAUDE.md guidance to prefer passing groups through over repeated use_mpu_process_groups reads in megatron/core.

@claude

claude Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Strict Review Summary — [GTP] NCCL Symmetric Memory Registration Support

Findings by severity: CRITICAL: 0 · IMPORTANT: 1 · SUGGESTION: 1

Overview

This PR adds NCCL symmetric-memory / user-buffer registration for GTP all-gather and reduce-scatter, gated behind two off-by-default flags (--gtp-nccl-ub, --egtp-nccl-ub). The design is careful and unusually well-documented — the buffer-ownership table in the PR description matches the code, and the tricky spots are all reasoned about in comments.

I traced the correctness-critical paths and found them sound:

  • Symm-RS send-buffer aliasing (_reduce_scatter): the direct-send path is correctly gated on a per-weight untyped_storage().data_ptr() identity check against _wgrad_padded_buf, so it only skips F.pad when the GEMM actually wrote into the registered buffer; otherwise it falls back to the unregistered F.pad copy. Pad-tail zeroing on fresh allocation prevents stale rows from the numel-keyed LIFO from scattering into the last rank's main_grad.
  • Register/deregister lifecycle: pools are registered pre-construction (collective warmup done once per group, gated by _warmed_groups), gtp_mem_pool_ctx is collective-free and capture-safe, and deregistration happens before PG teardown to avoid the ncclCommDeregister "Could not find handle" abort. Deregister order is name-sorted to stay consistent across ranks.
  • Backward compatibility: the shutdown path now guards the DP-group deregister with getattr(buf, 'nccl_ub', False), correctly avoiding a spurious DP deregister when only --gtp-nccl-ub (GTP-group registration) was used. All new behavior is behind off-by-default flags.
  • LIFO capture-safety: RegisteredLifoPool.alloc raises rather than allocating (which would re-register the pool mid-graph) during CUDA-graph capture — good.

Findings

  1. [IMPORTANT Compatibility] The new flags create torch.cuda.MemPool objects but bypass the expandable_segments:True + torch<2.11 guard that --use-nccl-ub gets, so that combination will produce a low-level crash instead of a clear error. Suggest mirroring the existing validation.
  2. [SUGGESTION Simplification] _gtp_attach_attrs rebuilds a ProcessGroupCollection per param just to read the constant expert group; cache it once at configure time.

Risk assessment: Low

Feature is entirely opt-in and off by default; no change to existing training paths beyond the correctly-guarded shutdown deregister. The one IMPORTANT item is a UX/error-message concern, not a numerical or data-integrity risk. Note the PR checklist marks unit tests as not added — given the intricate buffer-lifecycle logic, a small unit test around RegisteredLifoPool recycling / pad-tail zeroing would be valuable, though functional tests are noted as added.

default=False, help='Register dense GTP param buffers with NCCL symmetric memory on the GTP '
'group, independent of --use-nccl-ub (which covers the DP group).')
group.add_argument('--egtp-nccl-ub', action='store_true', dest='egtp_nccl_ub',
default=False, help='Like --gtp-nccl-ub but for routed-expert (EGTP) groups.')

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.

[IMPORTANT Compatibility] The new --gtp-nccl-ub / --egtp-nccl-ub flags create torch.cuda.MemPool objects (via create_nccl_mem_pool(symmetric=True) in gtp_symm.get_gtp_pool and the new distopt branch in param_and_grad_buffer.py), but they bypass the compatibility guard that --use-nccl-ub gets in DistributedDataParallelConfig.__post_init__:

if self.nccl_ub and not is_torch_min_version("2.11.0a0"):
    if 'expandable_segments:True' in os.getenv('PYTORCH_CUDA_ALLOC_CONF', '').split(','):
        raise ValueError("PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True is currently not supported with nccl_ub ...")

Why it matters: A user running --gtp-nccl-ub on torch < 2.11 with PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True will hit the same underlying torch.cuda.MemPool incompatibility, but instead of the friendly ValueError they get a confusing low-level crash. The --gtp-nccl-ub docs elsewhere in this MR also recommend NVLS env vars, so this combination is realistic.

Suggestion: Add an equivalent validation for the GTP flags (in validate_args / arguments.py, since these live in the arg namespace rather than the DDP config) that raises the same clear error when gtp_nccl_ub or egtp_nccl_ub is set with expandable_segments:True on an unsupported torch version.

# Expert vs dense by group identity check
_expert_group = ProcessGroupCollection.use_mpu_process_groups(
required_pgs=["expt_gtp_remat"]
).expt_gtp_remat

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.

[SUGGESTION Simplification] _gtp_attach_attrs runs once per GTP param, and each call constructs a fresh ProcessGroupCollection via use_mpu_process_groups(required_pgs=["expt_gtp_remat"]) just to read the constant expert group and compare identity. This rebuilds the collection object and re-reads the MPU global for every parameter in the model.

Why it matters: It's construction-time (not per-forward), so throughput is unaffected, but it's redundant object churn and an extra parallel_state global read per param. The expert group is invariant across the whole model build.

Suggestion: Resolve the expert group once (e.g. cache it in GTP_CONFIG alongside the egtp_nccl_ub/gtp_nccl_ub flags at configure_gtp_remat_from_recipe time, where the collection is already materialized) and have _gtp_attach_attrs compare against that cached group. This also matches the CLAUDE.md guidance to prefer passing the group through over repeated use_mpu_process_groups reads in core code.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants