fix(qdp-core): link cudart locally and stub FFI when toolkit is absent - #1321
Merged
Conversation
andrewmusselman
requested review from
400Ping,
guan404ming and
ryankert01
as code owners
May 17, 2026 15:35
Member
|
Like the high-level idea:
|
ryankert01
reviewed
May 19, 2026
| /// This function: | ||
| /// * emits `cargo:rustc-link-lib=cudart` and the appropriate | ||
| /// `cargo:rustc-link-search` path when nvcc is found, and | ||
| /// * emits `cargo:rustc-cfg=qdp_no_cuda` when it is not, gating the |
Member
There was a problem hiding this comment.
output().is_ok() is true even if nvcc exits non-zero — a half-installed nvcc would set has_cuda = true and fall through to a link error. Suggest .map(|o| o.status.success()).unwrap_or(false). Same idiom in qdp-kernels/build.rs:177; fix both so they can't disagree.
| /// | ||
| /// `qdp-core` declares CUDA Runtime API extern symbols in `src/gpu/cuda_ffi.rs` | ||
| /// (cudaHostAlloc, cudaMemGetInfo, cudaEventCreateWithFlags, ...). Those symbols | ||
| /// must be resolved at link time, which requires `libcudart` from the CUDA |
Member
There was a problem hiding this comment.
Missing cargo:rerun-if-env-changed=PATH. The whole decision hinges on nvcc-on-PATH, so installing CUDA after a stub build won't re-trigger the script until cargo clean. Same gap in qdp-kernels.
ryankert01
force-pushed
the
qdp-core-cudart-linkage
branch
from
June 22, 2026 17:14
059cb3d to
1ff2f68
Compare
ryankert01
force-pushed
the
qdp-core-cudart-linkage
branch
from
June 26, 2026 18:05
1ff2f68 to
8b1f340
Compare
ryankert01
approved these changes
Jun 26, 2026
Address review feedback on the CUDA-detection probe shared by qdp-core and qdp-kernels build scripts: - output().is_ok() was true even when nvcc exited non-zero, so a half-installed toolkit would set has_cuda = true and fall through to a link error. Use .map(|o| o.status.success()).unwrap_or(false) in both scripts so the two probes can't disagree. - Add cargo:rerun-if-env-changed=PATH so installing the toolkit after a stub build re-triggers detection without `cargo clean`. Also run cargo fmt on the no_cuda_stubs module in cuda_ffi.rs to clear the pre-commit fmt hook (was failing the test job).
This PR lets _qdp build and import without the CUDA toolkit (stub CUDA Runtime symbols), so "extension importable" no longer implies a usable GPU. The conftest auto-skip only checked extension availability, so on a GPU-less runner the @pytest.mark.gpu tests ran against the stub engine and aborted the pytest worker (SIGABRT) instead of being skipped. - conftest: add a torch.cuda.is_available() probe and skip @pytest.mark.gpu tests when no device is present, matching the inline torch.cuda.is_available() guards already used across the QDP modules. The existing "extension missing" skip behaviour is unchanged. - mark test_synthetic_loader_batch_count @pytest.mark.gpu: it iterates a synthetic loader, which encodes on the GPU. Verified locally: with CUDA hidden the previously-crashing tests skip cleanly (0 failures across testing/qdp + testing/qdp_python); with a GPU present they run and pass.
ryankert01
added a commit
that referenced
this pull request
Jun 26, 2026
Follow-up to #1321 (addresses #1414). After #1321, `_qdp` can build and import without the CUDA toolkit (stub runtime), so "extension importable" no longer implies a usable GPU. The test suite gated GPU tests on `torch.cuda.is_available()`, a proxy that is wrong on this PR's headline scenario -- a GPU host with PyTorch but no toolkit, where `_qdp` is a stub build yet torch still reports a device. - qdp-core: add `cuda_runtime_available()`, which queries `cudaGetDeviceCount` (false in a stub build via the existing 999 sentinel stub, and on hosts with no device). Re-exported from the crate root. - _qdp: expose it as `_qdp.cuda_available()`. - qumat_qdp: add `is_cuda_available()`, mirroring `is_triton_amd_available()`, as the single Python source of truth. - testing/conftest: gate the `@pytest.mark.gpu` auto-skip on the native signal (falling back to torch only if the helper is absent). - test_fallback: coverage that runs on a stub build too, guarding that querying availability returns a bool without aborting. Verified on GPU (tests run and pass) and with CUDA hidden (tests skip); full Rust suite, clippy --all-features, ruff, and ty all clean.
ryankert01
added a commit
that referenced
this pull request
Jun 28, 2026
Follow-up to #1321 (addresses #1414). After #1321, `_qdp` can build and import without the CUDA toolkit (stub runtime), so "extension importable" no longer implies a usable GPU. The test suite gated GPU tests on `torch.cuda.is_available()`, a proxy that is wrong on this PR's headline scenario -- a GPU host with PyTorch but no toolkit, where `_qdp` is a stub build yet torch still reports a device. - qdp-core: add `cuda_runtime_available()`, which queries `cudaGetDeviceCount` (false in a stub build via the existing 999 sentinel stub, and on hosts with no device). Re-exported from the crate root. - _qdp: expose it as `_qdp.cuda_available()`. - qumat_qdp: add `is_cuda_available()`, mirroring `is_triton_amd_available()`, as the single Python source of truth. - testing/conftest: gate the `@pytest.mark.gpu` auto-skip on the native signal (falling back to torch only if the helper is absent). - test_fallback: coverage that runs on a stub build too, guarding that querying availability returns a bool without aborting. Verified on GPU (tests run and pass) and with CUDA hidden (tests skip); full Rust suite, clippy --all-features, ruff, and ty all clean.
400Ping
pushed a commit
that referenced
this pull request
Jul 5, 2026
…on it (#1416) Follow-up to #1321 (addresses #1414). After #1321, `_qdp` can build and import without the CUDA toolkit (stub runtime), so "extension importable" no longer implies a usable GPU. The test suite gated GPU tests on `torch.cuda.is_available()`, a proxy that is wrong on this PR's headline scenario -- a GPU host with PyTorch but no toolkit, where `_qdp` is a stub build yet torch still reports a device. - qdp-core: add `cuda_runtime_available()`, which queries `cudaGetDeviceCount` (false in a stub build via the existing 999 sentinel stub, and on hosts with no device). Re-exported from the crate root. - _qdp: expose it as `_qdp.cuda_available()`. - qumat_qdp: add `is_cuda_available()`, mirroring `is_triton_amd_available()`, as the single Python source of truth. - testing/conftest: gate the `@pytest.mark.gpu` auto-skip on the native signal (falling back to torch only if the helper is absent). - test_fallback: coverage that runs on a stub build too, guarding that querying availability returns a bool without aborting. Verified on GPU (tests run and pass) and with CUDA hidden (tests skip); full Rust suite, clippy --all-features, ruff, and ty all clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1318 .
qdp-core/build.rs(extended; it already existed for protoc) now:nvccwith the same logic asqdp-kernels/build.rs,cargo:rustc-link-search=native=$CUDA_PATH/lib64andcargo:rustc-link-lib=cudartwhen found,cargo:rustc-cfg=qdp_no_cudawhen not found, with a clearcargo:warningpointing at the toolkit install,QDP_NO_CUDA=1for explicit forcing (matchingqdp-kernels).qdp-core/src/gpu/cuda_ffi.rswraps theextern "C"block in#[cfg(not(qdp_no_cuda))]and adds matching#[cfg(qdp_no_cuda)] mod no_cuda_stubs { ... }withpub(crate) unsafe fnstubs for all 14declared functions. Each stub returns
999— the same sentinelqdp-kernelsuses for its kernel-launcher stubs — so existing callererror paths (
if ret != 0 { return Err(...) }) surface a clean runtimeerror if anyone calls a CUDA function on a no-toolkit build, instead of
failing at link time. The whole stub module is wrapped in a single
#[allow(non_snake_case)]since the originals are camelCase to matchthe real CUDA Runtime API.
Cross-crate behaviour after this PR:
QDP_NO_CUDA=1Verified on Linux + CUDA 12.4:
cargo build --workspace --tests --exclude qdp-pythonsucceeds both with and withoutQDP_NO_CUDA=1;make test_rustruns full integration tests on the GPU; 12 lintwarnings introduced by the stubs are silenced.