-
Notifications
You must be signed in to change notification settings - Fork 810
Use dlopen to load NCCL EP
#3434
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
243222e
da23121
fdded06
f3861ec
c530dd8
da09014
0576af6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| # Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # | ||
| # See LICENSE for license information. | ||
|
|
||
| """Verify that packaged NCCL EP JIT headers contain all local quoted includes.""" | ||
|
|
||
| import re | ||
| import sys | ||
| from pathlib import Path | ||
|
|
||
|
|
||
| _INCLUDE_PATTERN = re.compile(r'^\s*#\s*include\s*"([^"]+)"') | ||
| _EXTERNAL_HEADERS = {"nccl.h", "nccl_device.h"} | ||
| _HEADER_SUFFIXES = {".cuh", ".h", ".hh", ".hpp", ".inc", ".inl"} | ||
|
|
||
|
|
||
| def main() -> None: | ||
| include_root = Path(sys.argv[1]) | ||
| jit_root = include_root / "nccl_ep" | ||
| public_header = include_root / "nccl_ep.h" | ||
| if not public_header.is_file(): | ||
| raise RuntimeError(f"Missing NCCL EP public header: {public_header}") | ||
| if not jit_root.is_dir(): | ||
| raise RuntimeError(f"Missing NCCL EP JIT header directory: {jit_root}") | ||
|
|
||
| failures = [] | ||
|
|
||
| headers = [public_header, *jit_root.rglob("*")] | ||
| for header in headers: | ||
| if not header.is_file() or header.suffix not in _HEADER_SUFFIXES: | ||
| continue | ||
| for line_number, line in enumerate(header.read_text().splitlines(), 1): | ||
| match = _INCLUDE_PATTERN.match(line) | ||
| if match is None: | ||
| continue | ||
| include = match.group(1) | ||
| if include in _EXTERNAL_HEADERS: | ||
| continue | ||
| candidates = ( | ||
| header.parent / include, | ||
| include_root / include, | ||
| jit_root / include, | ||
| jit_root / "device" / include, | ||
| ) | ||
| if not any(candidate.is_file() for candidate in candidates): | ||
| failures.append(f"{header.relative_to(include_root)}:{line_number}: {include}") | ||
|
|
||
| if failures: | ||
| raise RuntimeError("Missing local NCCL EP JIT headers:\n" + "\n".join(failures)) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| # Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # | ||
| # See LICENSE for license information. | ||
|
|
||
| import transformer_engine | ||
|
|
||
|
|
||
| def _hide_packaged_library(monkeypatch): | ||
| def _missing_library(_): | ||
| raise FileNotFoundError | ||
|
|
||
| monkeypatch.setattr( | ||
| transformer_engine.common, | ||
| "_get_shared_object_file", | ||
| _missing_library, | ||
| ) | ||
|
|
||
|
|
||
| def test_nccl_ep_library_found_from_home(monkeypatch, tmp_path): | ||
| home = tmp_path / "nccl_ep" | ||
| library_dir = home / "lib" | ||
| library_dir.mkdir(parents=True) | ||
| (library_dir / "libnccl_ep.so.0.1").touch() | ||
|
|
||
| monkeypatch.setenv("NCCL_EP_HOME", str(home)) | ||
| _hide_packaged_library(monkeypatch) | ||
| monkeypatch.setattr(transformer_engine, "find_library", lambda _: None) | ||
|
|
||
| assert transformer_engine._nccl_ep_library_installed() | ||
|
|
||
|
|
||
| def test_nccl_ep_library_found_in_package(monkeypatch, tmp_path): | ||
| library = tmp_path / "libnccl_ep.so" | ||
| library.touch() | ||
|
|
||
| monkeypatch.delenv("NCCL_EP_HOME", raising=False) | ||
| monkeypatch.setattr( | ||
| transformer_engine.common, | ||
| "_get_shared_object_file", | ||
| lambda _: library, | ||
| ) | ||
| monkeypatch.setattr(transformer_engine, "find_library", lambda _: None) | ||
|
|
||
| assert transformer_engine._nccl_ep_library_installed() | ||
|
|
||
|
|
||
| def test_nccl_ep_library_found_by_dynamic_loader(monkeypatch): | ||
| monkeypatch.delenv("NCCL_EP_HOME", raising=False) | ||
| _hide_packaged_library(monkeypatch) | ||
| monkeypatch.setattr(transformer_engine, "find_library", lambda _: "libnccl_ep.so.0") | ||
|
|
||
| assert transformer_engine._nccl_ep_library_installed() | ||
|
|
||
|
|
||
| def test_nccl_ep_library_not_found(monkeypatch): | ||
| monkeypatch.delenv("NCCL_EP_HOME", raising=False) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it mean NCCL_EP_HOME needs to be set at runtime?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does, but it's set automatically via |
||
| _hide_packaged_library(monkeypatch) | ||
| monkeypatch.setattr(transformer_engine, "find_library", lambda _: None) | ||
|
|
||
| assert not transformer_engine._nccl_ep_library_installed() | ||
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The whole built dir, but not the entire source repo. Here's what should be shipped: