From b462a85c70f6184c928a21dff11a19f50bd49d91 Mon Sep 17 00:00:00 2001 From: Mangesh Raut Date: Mon, 26 Oct 2026 03:14:19 +0530 Subject: [PATCH 1/2] fix(samples): respect CUDA_VISIBLE_DEVICES in multi-GPU cuda.core samples --- .../example_tests/test_sample_verification.py | 16 ++++++++++++++++ .../multiGPUGradientAverage.py | 3 +-- samples/cuda_core/simpleMultiGpu/README.md | 2 +- .../cuda_core/simpleMultiGpu/simpleMultiGpu.py | 4 ++-- samples/cuda_core/simpleP2P/README.md | 5 ++--- samples/cuda_core/simpleP2P/simpleP2P.py | 5 ++--- 6 files changed, 24 insertions(+), 11 deletions(-) diff --git a/cuda_core/tests/example_tests/test_sample_verification.py b/cuda_core/tests/example_tests/test_sample_verification.py index 4130632c8d3..8308dc8c3b8 100644 --- a/cuda_core/tests/example_tests/test_sample_verification.py +++ b/cuda_core/tests/example_tests/test_sample_verification.py @@ -99,6 +99,22 @@ def test_unsupported_core_sample_paths_return_waiver( assert len(waiver_returns) == 1 +@pytest.mark.parametrize("sample_name", ("simpleP2P", "simpleMultiGpu", "multiGPUGradientAverage")) +@pytest.mark.agent_authored(model="gpt-6") +def test_multi_gpu_samples_count_cuda_visible_devices(sample_name: str) -> None: + sample_path = REPO_ROOT / "samples" / "cuda_core" / sample_name / f"{sample_name}.py" + tree = ast.parse(sample_path.read_text(encoding="utf-8"), filename=str(sample_path)) + expected_count = ast.parse("len(Device.get_all_devices())", mode="eval").body + + assert any(ast.dump(node) == ast.dump(expected_count) for node in ast.walk(tree)) + assert not any( + isinstance(node, ast.Call) + and isinstance(node.func, ast.Attribute) + and node.func.attr == "get_num_devices" + for node in ast.walk(tree) + ) + + @pytest.mark.agent_authored(model="gpt-5") def test_verify_array_result_or_raise_enforces_match(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.syspath_prepend(str(UTILITIES_DIR)) diff --git a/samples/cuda_core/multiGPUGradientAverage/multiGPUGradientAverage.py b/samples/cuda_core/multiGPUGradientAverage/multiGPUGradientAverage.py index 612ca80a2bf..259c1725b40 100644 --- a/samples/cuda_core/multiGPUGradientAverage/multiGPUGradientAverage.py +++ b/samples/cuda_core/multiGPUGradientAverage/multiGPUGradientAverage.py @@ -77,7 +77,6 @@ Program, ProgramOptions, launch, - system, ) except ImportError as e: print(f"Error: Required package not found: {e}") @@ -103,7 +102,7 @@ def init_device(rank: int): tuple[Device, Stream] CUDA device object and stream object. """ - num_gpus = system.get_num_devices() + num_gpus = len(Device.get_all_devices()) if num_gpus == 0: raise RuntimeError("No CUDA devices available") diff --git a/samples/cuda_core/simpleMultiGpu/README.md b/samples/cuda_core/simpleMultiGpu/README.md index 2250d9e575d..acfe81d343f 100644 --- a/samples/cuda_core/simpleMultiGpu/README.md +++ b/samples/cuda_core/simpleMultiGpu/README.md @@ -33,7 +33,7 @@ input buffers, so the kernels see fully-materialized data. ### From `cuda.core` -- `system.get_num_devices()` - count of visible CUDA devices +- `len(Device.get_all_devices())` - count of devices visible to the CUDA runtime, respecting `CUDA_VISIBLE_DEVICES` - `Device(device_id)` / `Device.set_current()` - select and activate a device - `Device.create_stream()` - create a `cuda.core` stream on the current device - `Device.create_stream(adaptor)` - wrap a foreign stream via the `__cuda_stream__` protocol diff --git a/samples/cuda_core/simpleMultiGpu/simpleMultiGpu.py b/samples/cuda_core/simpleMultiGpu/simpleMultiGpu.py index 490e8b76633..b57316c5808 100644 --- a/samples/cuda_core/simpleMultiGpu/simpleMultiGpu.py +++ b/samples/cuda_core/simpleMultiGpu/simpleMultiGpu.py @@ -55,7 +55,7 @@ try: import cupy as cp - from cuda.core import Device, LaunchConfig, Program, ProgramOptions, launch, system + from cuda.core import Device, LaunchConfig, Program, ProgramOptions, launch except ImportError as e: print(f"Error: Required package not found: {e}") print("Please install from requirements.txt:") @@ -117,7 +117,7 @@ def _compile(device, source): def main(): - num_devices = system.get_num_devices() + num_devices = len(Device.get_all_devices()) if num_devices < 2: print( f"This sample requires at least 2 CUDA-capable devices (found {num_devices}). Waiving.", diff --git a/samples/cuda_core/simpleP2P/README.md b/samples/cuda_core/simpleP2P/README.md index b12ddfc4dc5..ff6cde6bdff 100644 --- a/samples/cuda_core/simpleP2P/README.md +++ b/samples/cuda_core/simpleP2P/README.md @@ -6,7 +6,7 @@ This sample demonstrates peer-to-peer (P2P) memory access between multiple GPUs ## What you will learn -- How to detect multiple CUDA-capable GPUs using `system.get_num_devices()` and `Device(id)` +- How to detect CUDA-visible GPUs using `len(Device.get_all_devices())` and `Device(id)` - How to check P2P capability between GPU pairs using `device.can_access_peer()` - How to enable and disable peer access using `DeviceMemoryResource.peer_accessible_by` - How to allocate device memory on specific GPUs using `DeviceMemoryResource` @@ -26,8 +26,7 @@ This sample demonstrates peer-to-peer (P2P) memory access between multiple GPUs ## Key APIs **From cuda.core:** -- `system` – Pre-instantiated singleton for system-level CUDA information -- `system.get_num_devices()` – Get number of CUDA-capable devices +- `Device.get_all_devices()` – Get the CUDA-visible devices, respecting `CUDA_VISIBLE_DEVICES` - `Device(id)` – Get specific CUDA device handle - `device.can_access_peer(peer)` – Check if this device can access peer device memory - `device.set_current()` – Set active device for subsequent operations diff --git a/samples/cuda_core/simpleP2P/simpleP2P.py b/samples/cuda_core/simpleP2P/simpleP2P.py index d2406487fa5..7dd14c740e7 100644 --- a/samples/cuda_core/simpleP2P/simpleP2P.py +++ b/samples/cuda_core/simpleP2P/simpleP2P.py @@ -45,7 +45,6 @@ Program, ProgramOptions, launch, - system, ) except ImportError as e: print(f"Error: Required package not found: {e}") @@ -99,8 +98,8 @@ def run(num_elements=1024 * 1024 * 16): # Check for multiple GPUs print("\nChecking for multiple GPUs...") - num_devices = system.get_num_devices() - print(f"CUDA-capable device count: {num_devices}") + num_devices = len(Device.get_all_devices()) + print(f"CUDA-visible device count: {num_devices}") if num_devices < 2: print("Two or more GPUs with Peer-to-Peer access capability are required, waiving this sample.") From 10d7010f6a23b7c287995cdfc84b565dee25eb94 Mon Sep 17 00:00:00 2001 From: Mangesh Raut Date: Mon, 26 Oct 2026 06:09:55 +0530 Subject: [PATCH 2/2] Format sample verification test Signed-off-by: Mangesh Raut --- cuda_core/tests/example_tests/test_sample_verification.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cuda_core/tests/example_tests/test_sample_verification.py b/cuda_core/tests/example_tests/test_sample_verification.py index 8308dc8c3b8..226ef67b084 100644 --- a/cuda_core/tests/example_tests/test_sample_verification.py +++ b/cuda_core/tests/example_tests/test_sample_verification.py @@ -108,9 +108,7 @@ def test_multi_gpu_samples_count_cuda_visible_devices(sample_name: str) -> None: assert any(ast.dump(node) == ast.dump(expected_count) for node in ast.walk(tree)) assert not any( - isinstance(node, ast.Call) - and isinstance(node.func, ast.Attribute) - and node.func.attr == "get_num_devices" + isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute) and node.func.attr == "get_num_devices" for node in ast.walk(tree) )