CUDA 13.2 samples update (#432)

- Added Python samples for CUDA Python 1.0 release
- Renamed top-level `Samples` directory to `cpp` to accommodate Python samples.
This commit is contained in:
Dheemanth
2026-05-13 15:13:18 -07:00
committed by GitHub
parent a4526d5229
commit aeab82ff30
2072 changed files with 15077 additions and 1970 deletions

View File

@@ -0,0 +1,140 @@
# ipcMemoryPool (Python)
## Description
This sample demonstrates how to share GPU memory between Python
processes using CUDA Inter-Process Communication (IPC) and
`cuda.core`'s IPC-enabled memory pools.
By default each process has its own CUDA virtual address space and
cannot see allocations made by another process. With an IPC-enabled
`DeviceMemoryResource` the parent allocates once, and the child
process maps that same physical GPU memory into its own address space
so both read and write the same bytes. The sample performs a
round-trip test:
1. Parent creates an IPC-enabled `DeviceMemoryResource` and allocates
a `Buffer`.
2. Parent fills the buffer with a known pattern.
3. Parent sends the `Buffer` to a child process through an
`multiprocessing.Queue`. cuda.core's pickle reducers re-create the
memory resource and map the buffer in the child.
4. Child verifies the parent's pattern, writes a new pattern, and
signals completion.
5. Parent verifies the child's writes.
## What You'll Learn
- Enabling IPC on a `DeviceMemoryResource` with `ipc_enabled=True`
- Sending `Buffer` objects across process boundaries via `mp.Queue`
- How cuda.core's pickle reducers rebuild the MR and map the buffer
in the receiving process
- Why `multiprocessing` must use the `"spawn"` start method with CUDA
- Detecting IPC support at runtime (POSIX file-descriptor handle
type, memory-pool support, Linux-only)
## Key Libraries
- [`cuda.core`](https://nvidia.github.io/cuda-python/cuda-core/latest/) - IPC-enabled memory resources and buffer reducers
- `cupy` - zero-copy views over the shared device memory via DLPack
- `multiprocessing` - standard library process management
## Key APIs
### From `cuda.core`
- `DeviceMemoryResource(device, options=DeviceMemoryResourceOptions(ipc_enabled=True))` - create an IPC-enabled memory pool
- `DeviceMemoryResourceOptions(max_size=..., ipc_enabled=True)` - configure the underlying pool
- `mr.allocate(nbytes)` - allocate a `Buffer` from the IPC pool
- `Buffer.is_mapped` - True when the buffer is usable in the current process
- `Device.properties.memory_pools_supported` - runtime feature check
- `Device.properties.handle_type_posix_file_descriptor_supported` - runtime feature check
### From `cuda_samples_utils`
- `print_gpu_info()` - print device name and compute capability
## Requirements
### Hardware
- NVIDIA GPU with Compute Capability 7.0 or higher
- Device that supports CUDA memory pools and POSIX file-descriptor IPC handles (the sample detects and reports this at startup)
- Minimum GPU memory: 512 MB
### Software
- Linux x86_64 (POSIX file-descriptor IPC handles are not available on Windows or macOS)
- CUDA Toolkit 13.0 or newer (matches `cuda-python` 13.x)
- Python 3.10 or newer
- `cuda-python` (>=13.0.0)
- `cuda-core` (>=0.6.0)
- `cupy-cuda13x` (>=13.0.0)
## Installation
Install the required packages from `requirements.txt`:
```bash
cd /path/to/cuda-samples/python/4_DistributedComputing/ipcMemoryPool
pip install -r requirements.txt
```
The `requirements.txt` installs:
- `cuda-python` (>=13.0.0)
- `cuda-core` (>=0.6.0)
- `cupy-cuda13x` (>=13.0.0)
## How to Run
### Basic usage
```bash
cd cuda-samples/python/4_DistributedComputing/ipcMemoryPool
python ipcMemoryPool.py
```
### With custom parameters
```bash
# Larger shared buffer
python ipcMemoryPool.py --elements 65536
# Use a specific GPU
python ipcMemoryPool.py --device 1
```
On platforms or devices that do not support CUDA IPC, the sample
prints a diagnostic and exits cleanly with status 0.
## Expected Output
```
Device: <Your GPU Name>
Compute Capability: <X.Y>
Created IPC-enabled DeviceMemoryResource (is_ipc_enabled=True)
Parent wrote pattern (first 5 values): [100. 101. 102. 103. 104.]
Parent sent buffer to child pid=<pid>; waiting...
[child pid=<pid>] received buffer: is_mapped=True, size=4096
Parent sees child's pattern (first 5 values): [-0. -1. -2. -3. -4.]
IPC round-trip: OK
```
**Note:** Device name, compute capability, and child PID will vary
based on your system.
## Files
- `ipcMemoryPool.py` - Python implementation using `cuda.core` IPC memory pools
- `README.md` - This file
- `requirements.txt` - Sample dependencies
- `../../Utilities/cuda_samples_utils.py` - Common utilities (imported by this sample)
## See Also
- [CUDA Python Documentation](https://nvidia.github.io/cuda-python/)
- [`cuda.core` memory API](https://nvidia.github.io/cuda-python/cuda-core/latest/api.html#memory)
- Upstream `cuda.core` IPC tests: [`test_memory_ipc.py`](https://github.com/NVIDIA/cuda-python/blob/main/cuda_core/tests/memory_ipc/test_memory_ipc.py)
- [CUDA IPC programming guide](https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#interprocess-communication)

View File

@@ -0,0 +1,220 @@
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of NVIDIA CORPORATION nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
IPC Memory Pool with cuda.core
Share GPU memory between Python processes using CUDA Inter-Process
Communication (IPC) and cuda.core's IPC-enabled memory pools. By default
each worker process has its own CUDA virtual address space and cannot see
allocations made by another process. With an IPC-enabled
``DeviceMemoryResource`` the parent can allocate once, and the child
process can map that same physical GPU memory into its own address space
so both read and write the same bytes.
The sample does a round-trip test:
1. Parent creates an IPC-enabled ``DeviceMemoryResource`` and allocates
a ``Buffer``.
2. Parent fills the buffer with a known pattern.
3. Parent sends the ``Buffer`` to a child process through an
``mp.Queue`` - cuda.core's pickle reducers take care of re-creating
the memory resource and mapping the buffer in the child.
4. Child verifies the parent's pattern, writes a new pattern, and
signals completion.
5. Parent verifies the child's writes.
IPC requires Linux (POSIX file-descriptor handles) and device support for
memory pools. On unsupported platforms the sample prints a diagnostic and
exits cleanly.
"""
import multiprocessing as mp
import platform
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "Utilities"))
try:
import cupy as cp
import numpy as np
from cuda.core import (
Device,
DeviceMemoryResource,
DeviceMemoryResourceOptions,
)
from cuda_samples_utils import print_gpu_info # noqa: E402
except ImportError as e:
print(f"Error: Required package not found: {e}")
print("Please install from requirements.txt:")
print(" pip install -r requirements.txt")
sys.exit(1)
CHILD_TIMEOUT_SEC = 30
def check_ipc_support(device) -> bool:
"""Return True if this device/platform supports CUDA IPC memory pools."""
if platform.system() != "Linux":
print(
f"IPC via POSIX file descriptors is only supported on Linux "
f"(detected {platform.system()})."
)
return False
if not device.properties.memory_pools_supported:
print("Device does not support CUDA memory pools.")
return False
if not device.properties.handle_type_posix_file_descriptor_supported:
print("Device/platform does not support POSIX-fd IPC handles.")
return False
return True
def child_worker(q_in, q_out, n_elements, parent_seed, child_seed):
"""Runs in a separate process. Verifies and modifies the shared buffer."""
device = Device(0)
device.set_current()
pid = mp.current_process().pid
# The Buffer (and its MR) are reconstructed and mapped in this process
# when the queued object is unpickled. Both ``is_mapped`` flags are
# True here.
buffer = q_in.get(timeout=CHILD_TIMEOUT_SEC)
print(
f"[child pid={pid}] received buffer: is_mapped={buffer.is_mapped}, "
f"size={buffer.size}"
)
# Build a zero-copy CuPy view of the shared device memory.
arr = cp.from_dlpack(buffer).view(dtype=cp.float32)
# Verify the parent's pattern.
expected_parent = cp.arange(n_elements, dtype=cp.float32) + float(parent_seed)
if not cp.allclose(arr, expected_parent):
print("[child] ERROR: parent's pattern did not match expectation")
buffer.close()
q_out.put("fail")
return
# Write a new pattern for the parent to verify.
arr[:] = cp.arange(n_elements, dtype=cp.float32) * float(child_seed)
device.sync()
buffer.close()
q_out.put("done")
def main() -> int:
import argparse
parser = argparse.ArgumentParser(
description="Share a GPU buffer between two processes via CUDA IPC"
)
parser.add_argument(
"--elements",
type=int,
default=1024,
help="Number of float32 elements in the shared buffer (default: 1024)",
)
parser.add_argument("--device", type=int, default=0, help="CUDA device id")
args = parser.parse_args()
# CUDA is incompatible with the ``fork`` start method because forked
# children inherit a corrupt CUDA state. Always use ``spawn``.
mp.set_start_method("spawn", force=True)
device = Device(args.device)
device.set_current()
print_gpu_info(device)
if not check_ipc_support(device):
print("\nCUDA IPC is not available on this system; exiting cleanly.")
return 0
N = args.elements
nbytes = N * np.dtype(np.float32).itemsize
parent_seed = 100
child_seed = -1.0
# Create an IPC-enabled memory pool. Buffers allocated from this MR
# are picklable and can be shared across processes.
mr = DeviceMemoryResource(
device,
options=DeviceMemoryResourceOptions(
max_size=max(nbytes * 4, 1 << 20),
ipc_enabled=True,
),
)
print(
"Created IPC-enabled DeviceMemoryResource "
f"(is_ipc_enabled={mr.is_ipc_enabled})"
)
buffer = mr.allocate(nbytes)
try:
# Fill the buffer with a known pattern from the parent side.
arr = cp.from_dlpack(buffer).view(dtype=cp.float32)
arr[:] = cp.arange(N, dtype=cp.float32) + float(parent_seed)
device.sync()
print(f"Parent wrote pattern (first 5 values): {arr[:5].get()}")
# Launch the child process and hand the buffer over.
q_to_child = mp.Queue()
q_from_child = mp.Queue()
child = mp.Process(
target=child_worker,
args=(q_to_child, q_from_child, N, parent_seed, child_seed),
)
child.start()
q_to_child.put(buffer)
print(f"Parent sent buffer to child pid={child.pid}; waiting...")
msg = q_from_child.get(timeout=CHILD_TIMEOUT_SEC)
child.join(timeout=CHILD_TIMEOUT_SEC)
if msg != "done" or child.exitcode != 0:
print(f"Child failed: msg={msg!r}, exitcode={child.exitcode}")
return 1
# Verify the child's writes are visible from the parent.
device.sync()
got = arr[:5].get()
expected = (np.arange(N, dtype=np.float32) * child_seed)[:5]
print(f"Parent sees child's pattern (first 5 values): {got}")
if np.allclose(got, expected):
print("IPC round-trip: OK")
return 0
print(f"IPC round-trip: FAILED (expected {expected})")
return 1
finally:
buffer.close()
mr.close()
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,4 @@
cuda-python>=13.0.0
cuda-core>=0.6.0
cupy-cuda13x>=13.0.0
numpy>=1.24.0