Skip to content

Make Windows pathfinder dynamic library searches architecture-aware - #2393

Open
isVoid wants to merge 15 commits into
NVIDIA:mainfrom
isVoid:codex/pathfinder-windows-arch-search-ctk-next
Open

Make Windows pathfinder dynamic library searches architecture-aware#2393
isVoid wants to merge 15 commits into
NVIDIA:mainfrom
isVoid:codex/pathfinder-windows-arch-search-ctk-next

Conversation

@isVoid

@isVoid isVoid commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

What changed

This PR makes both Windows dynamic-library path sources architecture-aware:

  • site_packages_windows describes wheel locations separately for x64 and ARM64. CUDA 13 aggregate-wheel directories are searched before CUDA 12 component-wheel directories, and CUDA 12 remains an x64-only fallback.
  • anchor_rel_dirs_windows describes locations relative to a Conda, CUDA_PATH/CUDA_HOME, or discovered system-CTK root separately for x64 and ARM64.

The change introduces the immutable WindowsSearchDirs dataclass. It stores independent, ordered x64 and arm64 path tuples so the architectures can have different path counts and architecture-only locations without synthetic common entries. for_arch() returns the paths for the requested Windows Python architecture, while x64_only() and arm64_only() make single-architecture layouts explicit.

The default local-CTK search uses bin/x64 followed by legacy bin for x64, and bin/arm64 for ARM64. Wheel search uses nvidia/cu13/bin/x86_64 or nvidia/cu13/bin/arm64 before any applicable CUDA 12 component-wheel location.

Special library layouts

CUPTI

CUPTI does not use the default local-CTK layout. Its x64 anchor order is extras/CUPTI/lib/x64, legacy extras/CUPTI/lib64, then bin. Its ARM64 anchor is only extras/CUPTI/lib/arm64. Wheel paths continue to use the CUDA 13 aggregate-wheel directory first, with the CUDA 12 component-wheel directory available only to x64.

cuDLA

Windows cuDLA is explicitly ARM64-only. It is searched only at the local-CTK anchor bin/arm64; x64 has no anchor path. No Windows wheel currently ships cudla.dll, so site_packages_windows is intentionally empty.

NVVM

NVVM uses nvvm/bin/x64 followed by legacy nvvm/bin for x64 local-CTK searches, while ARM64 uses nvvm/bin. Its wheel search uses the CUDA 13 architecture-qualified aggregate-wheel directory first and retains the CUDA 12 NVCC component-wheel location only as an x64 fallback.

The unqualified nvvm/bin directory is architecture-ambiguous across toolkit layouts: it contains x64 NVVM before CUDA 13.4 and ARM64 NVVM in the CUDA 13.4 Windows-on-Arm toolkit. For NVVM anchor searches only, pathfinder therefore inspects the candidate DLL's PE Machine field and accepts it only when it matches the Python process architecture. This avoids adding CTK-version detection and prevents x64 Python from selecting an ARM64 NVVM DLL, or ARM64 Python from selecting a legacy x64 DLL.

Additional changes

  • Detect the Windows Python architecture explicitly and reject unsupported platform tags.
  • Derive legacy lookup tables from the architecture-aware descriptor catalog, with Windows compatibility projections remaining x64 by default.
  • Remove toolshed/make_site_packages_libdirs.py and its related catalog-writer round-trip test instead of extending and maintaining that generator for architecture-aware paths. The site_packages_windows mappings are authored directly in descriptor_catalog.py.

Why

CUDA Toolkit 13.4 installs Windows binaries in architecture-specific directories such as bin/x64 and bin/arm64. CUDA 13 wheels likewise use architecture-specific directories under nvidia/cu13/bin, while CUDA 12 component wheels retain their existing package-specific layouts. Because Windows installers can install cross-architecture packages, pathfinder selects paths using the Python process architecture rather than the host architecture.

Validation

  • Full cuda-pathfinder suite on Windows: 1,243 passed, 8 skipped.
  • ruff check passed for all changed Python files.
  • ruff format --check passed for all changed Python files.

@isVoid isVoid added this to the cuda.pathfinder next milestone Jul 20, 2026
@isVoid isVoid added the cuda.pathfinder Everything related to the cuda.pathfinder module label Jul 20, 2026
@copy-pr-bot

copy-pr-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

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.

@isVoid isVoid self-assigned this Jul 20, 2026
@isVoid isVoid added the enhancement Any code-related improvements label Jul 20, 2026
@isVoid
isVoid requested review from leofang and rwgk July 20, 2026 21:22
@isVoid

isVoid commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test

@copy-pr-bot

copy-pr-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

/ok to test

@isVoid, there was an error processing your request: E1

See the following link for more information: https://docs.gha-runners.nvidia.com/cpr/e/1/

@isVoid

isVoid commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test 1d0a111

@github-actions

Copy link
Copy Markdown

@isVoid
isVoid force-pushed the codex/pathfinder-windows-arch-search-ctk-next branch from 1d0a111 to 3247072 Compare July 21, 2026 16:38
@isVoid

isVoid commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test 3247072

}
SITE_PACKAGES_LIBDIRS_LINUX = SITE_PACKAGES_LIBDIRS_LINUX_CTK | SITE_PACKAGES_LIBDIRS_LINUX_OTHER

# Historical table exports represent the original x64 catalog.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Q: What should we list here? Historically it's only listing the x64 catalog. But should we instead list all available items?

Comment thread cuda_pathfinder/cuda/pathfinder/_utils/windows_arch.py Outdated
@rwgk

rwgk commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
Ultimately, the only thing that matters is that `SUPPORTED_LIBNAMES` in `cuda/pathfinder/__init__.py` correctly reflects what libs are
supported on the current platform.

To define the boundary conditions: we could completely change everything else here and adjust the unit tests accordingly.

I don't want to go to that extreme, but keep this in mind with the goal to cleanly accommodate WoA, without making the code in cuda_pathfinder/cuda/pathfinder/_dynamic_libs/supported_nvidia_libs.py awkward.

So the questions become:
  * What is the `SUPPORTED_LIBNAMES` we are aiming for?
  * What is a healthy way to achieve that?

Could you please analyze and let me know your recommendation?
My recommendation is to make SUPPORTED_NVIDIA_LIBNAMES a capability list for the current process ABI—not an OS-wide union and not an
inventory of installed files.

Target Contract

“Current platform” should mean:

- Linux, following the existing behavior.
- Windows/x64 when the running Python reports win-amd64, including x64 Python emulated on WoA.
- Windows/ARM64 when Python reports win-arm64.

“Supported” should mean Pathfinder knows how to load that library for the process ABI:

- Supported but not installed → present in SUPPORTED_NVIDIA_LIBNAMES; loading raises DynamicLibNotFoundError.
- Known but unsupported on this OS/architecture → absent from the tuple; loading raises DynamicLibNotAvailableError without searching.
- Installed wheels, conda, CUDA_PATH, and the filesystem must not affect the tuple.

For this PR, I recommend preserving the established CTK-only boundary of the public tuple. Expanding it to third-party and driver libraries
would be a separate API-semantic change, although the loader’s broader internal supported set should use the same platform predicate.

Target Sets

Based on the available CTK and prior-PR evidence, I would aim for:

 Process platform    CTK names
━━━━━━━━━━━━━━━━━━  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 Linux               Existing 29-name Linux tuple
──────────────────  ─────────────────────────────────────────────────
 Windows x64         Existing 28-name Windows tuple minus cudla
──────────────────  ─────────────────────────────────────────────────
 Windows ARM64       Existing 28-name Windows tuple, including cudla

The 27-name Windows common set is therefore:

cudart, nvfatbin, nvJitLink, nvrtc, nvvm,
cublas, cublasLt, cufft, cufftw, curand,
cusolver, cusolverMg, cusparse,
nppc, nppial, nppicc, nppidei, nppif, nppig,
nppim, nppist, nppisu, nppitc, npps,
nvblas, nvjpeg, cupti

I would not copy PR 440’s nvjpeg ARM64-only classification. NVIDIA’s current CUDA component matrix lists nvJPEG for x86-64, ARM64, and
Windows, and the current NVIDIA nvJPEG package includes a Windows x86-64 wheel. CUDA release matrix
(https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/), NVIDIA nvJPEG package (https://pypi.org/project/nvidia-nvjpeg/).

cudla is the one boundary I would confirm with Kitmaker before freezing the expected tuple. The private history consistently describes its
Windows addition as ARM64-specific. If an x64 Windows cudla.dll is officially supported, both Windows tuples would instead contain the same
28 names.

Healthy Implementation

I would keep architecture policy out of cuda_pathfinder/cuda/pathfinder/_dynamic_libs/supported_nvidia_libs.py:16. That module should remain
a compatibility projection, not become the place where OS, architecture, paths, and package history are recombined.

A relatively small clean design would be:

1. Add explicit descriptor availability, such as windows_supported_arches.
2. Reshape Windows paths by architecture at the collection level:

@dataclass(frozen=True)
class WindowsSearchDirs:
    x64: tuple[str, ...] = ()
    arm64: tuple[str, ...] = ()

This is healthier than pairing every individual x64 path with an ARM64 path. It naturally supports different path counts and x64-only
entries without fake “common” paths.

3. Add one pure registry query:

supported_libnames(platform, *, packaged_with=None)

4. Use that same query for both:

SUPPORTED_NVIDIA_LIBNAMES = supported_libnames(
    current_process_platform(),
    packaged_with="ctk",
)

_ALL_SUPPORTED_LIBNAMES = frozenset(
    supported_libnames(current_process_platform())
)

This prevents the current divergence between the public CTK tuple and cuda_pathfinder/cuda/pathfinder/_dynamic_libs/
load_nvidia_dynamic_lib.py:45.

5. Keep legacy SITE_PACKAGES_LIBDIRS_WINDOWS* constants as explicitly x64 compatibility projections—or introduce clearly named _X64/_ARM64
   projections. Never concatenate the two architectures.

The tests should assert the exact three capability matrices, nvjpeg on x64, the final cudla decision, unavailable-versus-missing exception
behavior, dependency closure per architecture, and catalog-writer round-tripping. Tests comparing one catalog-derived legacy table to
another catalog-derived table do not establish the desired contract.

I based this on the local branch, the private PR/issue history, and current public CUDA metadata. The internal Glean connector failed during
this analysis, so the final CTK 13.4 cudla x64/ARM64 boundary deserves explicit confirmation.

Comment thread cuda_pathfinder/cuda/pathfinder/_dynamic_libs/descriptor_catalog.py
@isVoid

isVoid commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test a6348e9

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

Labels

cuda.pathfinder Everything related to the cuda.pathfinder module enhancement Any code-related improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants