Skip to content

Use dlopen to load NCCL EP - #3434

Open
fheinecke wants to merge 4 commits into
NVIDIA:mainfrom
fheinecke:fred/dlopen-nccl-ep-1
Open

Use dlopen to load NCCL EP#3434
fheinecke wants to merge 4 commits into
NVIDIA:mainfrom
fheinecke:fred/dlopen-nccl-ep-1

Conversation

@fheinecke

Copy link
Copy Markdown
Collaborator

Description

NCCL EP is currently linked statically into the TE core shared library. This PR moves from statically linking to dlopening the file at runtime, but only when NCCL EP features are needed.

This solves two problems:

  • Unblocks BOLT optimization. BOLT optimization requires setting the -z now flag, which resolves all symbols at dynamic load time. This means that even if NCCL EP isn't used, it's dependencies symbols are still resolved. Because NCCL EP features require a newer version of NCCL than the rest of TE, this means that enabling BOLT optimization raises the minimum NCCL version. By dlopening NCCL EP instead, resolution of these symbols controlled by TE, and can be limited to only be loaded when NCCL EP features are used.
  • NCCL EP JIT header files are missing, causing JIT compilation to fail. NCCL lib and include are now shipped with TE as a part of this work.

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:

  • Ship NCCL EP dynamic library with TE
  • Fix missing NCCL EP JIT headers not being shipped with TE

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

Signed-off-by: Fred Heinecke <fheinecke@nvidia.com>
Signed-off-by: Fred Heinecke <fheinecke@nvidia.com>
@fheinecke
fheinecke requested a review from ptrendx as a code owner August 27, 2026 21:53
@fheinecke
fheinecke requested a review from phu0ngng August 27, 2026 21:53
Signed-off-by: Fred Heinecke <fheinecke@nvidia.com>
@fheinecke
fheinecke force-pushed the fred/dlopen-nccl-ep-1 branch from 1873e7b to fdded06 Compare August 27, 2026 21:54
@fheinecke fheinecke added the 2.19 label Aug 27, 2026
@greptile-apps

greptile-apps Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR moves NCCL EP behind runtime loading and packages its shared library and JIT headers with Transformer Engine.

  • Adds lazy NCCL EP library and symbol resolution after runtime compatibility checks.
  • Packages the NCCL EP shared object and required JIT headers in core wheels.
  • Adds discovery, packaging, and distributed JIT validation coverage.

Confidence Score: 4/5

The PR is not yet safe to merge because availability detection can approve an NCCL EP installation that subsequently fails during native initialization.

The Python check accepts any libnccl_ep.so* file under NCCL_EP_HOME, while the native loader tries only libnccl_ep.so and the major-version name, so an installation containing only a fully versioned shared object remains unusable after being reported as available.

Files Needing Attention: transformer_engine/init.py and transformer_engine/common/ep/nccl_ep_provider.cpp

Important Files Changed

Filename Overview
transformer_engine/common/ep/nccl_ep_provider.cpp Implements lazy NCCL EP discovery, loading, version validation, JIT environment configuration, and symbol wrappers.
transformer_engine/init.py Adds Python-side library availability detection, but its accepted versioned filenames remain broader than the native loader’s candidates.
transformer_engine/common/CMakeLists.txt Replaces static NCCL EP linkage with provider compilation and installation of the shared library and JIT headers.
build_tools/build_ext.py Moves the installed NCCL EP header tree into the release wheel library directory.
tests/pytorch/test_nccl_ep_discovery.py Covers standard discovery sources but does not invalidate the outstanding fully versioned filename mismatch.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    A[EP initialization] --> B[Validate NCCL runtime version]
    B --> C[Search NCCL_EP_HOME and packaged paths]
    C --> D[dlopen libnccl_ep]
    D --> E[Resolve NCCL EP symbols]
    E --> F[Configure JIT header environment]
    F --> G[Create EP group and handles]
Loading

Reviews (2): Last reviewed commit: "license fix" | Re-trigger Greptile

if any(
library.is_file()
for lib_dir in ("lib", "lib64")
for library in (home / lib_dir).glob("libnccl_ep.so*")

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 Versioned discovery mismatch

When NCCL_EP_HOME contains only a fully versioned file such as libnccl_ep.so.0.1, this glob reports NCCL EP as available, but the native loader searches only libnccl_ep.so and libnccl_ep.so.<major>, causing the first EP initialization to fail.

Knowledge Base Used: Build, extensions, and packaging

Signed-off-by: Fred Heinecke <fheinecke@nvidia.com>
Comment thread build_tools/build_ext.py
Comment on lines +179 to +183
shutil.rmtree(target_nccl_ep_dir)
shutil.copytree(
nccl_ep_dir,
target_nccl_ep_dir,
)

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.

Do we copy the whole nccl_ep dir instead of header files only?



def test_nccl_ep_library_not_found(monkeypatch):
monkeypatch.delenv("NCCL_EP_HOME", raising=False)

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.

Does it mean NCCL_EP_HOME needs to be set at runtime?

Comment on lines +221 to +238
ncclResult_t create_group(ncclEpGroup_t* ep_group, ncclComm_t comm,
const ncclEpGroupConfig_t* config) {
using FuncT = decltype(&ncclEpCreateGroup);
static FuncT func = reinterpret_cast<FuncT>(get_symbol("ncclEpCreateGroup"));
return func(ep_group, comm, config);
}

ncclResult_t group_destroy(ncclEpGroup_t ep_group) {
using FuncT = decltype(&ncclEpGroupDestroy);
static FuncT func = reinterpret_cast<FuncT>(get_symbol("ncclEpGroupDestroy"));
return func(ep_group);
}

ncclResult_t handle_destroy(ncclEpHandle_t handle) {
using FuncT = decltype(&ncclEpHandleDestroy);
static FuncT func = reinterpret_cast<FuncT>(get_symbol("ncclEpHandleDestroy"));
return func(handle);
}

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 we could use std::forward here to consolidate these:

template <auto FuncPtr, typename... Args>
auto call_symbol(const char* name, Args&&... args) {
    using FuncT = decltype(FuncPtr);
    static FuncT func = reinterpret_cast<FuncT>(get_symbol(name));
    return func(std::forward<Args>(args)...);
}

Path to shared object file for a Transformer Engine library.

TE libraries are 'core', 'torch', or 'jax'. This function first
TE libraries are 'core', 'torch', 'jax', or 'nccl_ep'. This function first

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.

Not sure if nccl_ep listed here is appropriate as it's a 3rd-level dependency rather than a TE's extension.

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