Sparse-checkout from NVIDIA/cccl main branch to complete cccl_upstream: Added: - python/cuda_cccl/ (226 files) — Python bindings for device-level algorithms Critical for muh toolchain: cuda.compute.reduce_into, scan, radix_sort, etc. Includes 204 .py files with full test coverage for all 27 algorithms - ci/ (163 files) — Build/test infrastructure build_cub.sh, test_cub.sh, build_and_test_targets.sh, matrix.yaml Directly maps to our [INFRA-CI] and [INFRA-BUILD] items - .agent/skills/ (7 files) — NVIDIA's own agent skills for CCCL cccl-style/SKILL.md, cccl-test/SKILL.md, sass-diff/SKILL.md - docs/ (491 files) — Official CCCL documentation CI references, CMake guides, Python compute docs, libcudacxx PTX docs - test/ (12 files) — Top-level integration tests (cuda_smoke, stdpar) - Root configs: .clang-format, .clang-tidy, CONTRIBUTING.md, pyproject.toml - CLAUDE.md symlink → AGENTS.md (NVIDIA's standard) cccl_upstream now mirrors full NVIDIA/cccl structure: Before: 42M (cub + thrust + libcudacxx + cudax + c + examples + benchmarks) After: 53M (+python +ci +docs +.agent +test +configs) This completes the CCCL base needed for: - [muh-bench] items: ci/util/build_and_test_targets.sh for targeted builds - [CCCL-verify] items: python/cuda_cccl/tests/ as reference implementations - [CCCL-test] items: ci/test_cub.sh, ci/test_thrust.sh - Agent workflow: .agent/skills/ for consistent style and test patterns
132 lines
5.4 KiB
ReStructuredText
132 lines
5.4 KiB
ReStructuredText
.. _cccl-runtime-cudart-interactions:
|
|
|
|
CUDA Runtime interactions
|
|
=========================
|
|
|
|
Some runtime objects have a non-owning ``_ref`` counterpart (for example, :cpp:struct:`cuda::stream` and
|
|
:cpp:class:`cuda::stream_ref`). Prefer the
|
|
owning type for lifetime management, and use the ``_ref`` type for code that would otherwise accept a C++ reference but
|
|
needs to interoperate with existing CUDA Runtime code.
|
|
|
|
CCCL runtime types that wrap CUDA Runtime handles support interoperating with CUDA Runtime handles via ``get()``,
|
|
constructors that accept native handles, ``release()``, and ``from_native_handle`` helpers. This makes it straightforward
|
|
to bridge between cccl-runtime APIs and existing CUDA Runtime code without losing ownership clarity.
|
|
|
|
Use ``get()`` on both owning and non-owning types. Constructors from native handles are intended for ``_ref`` wrappers,
|
|
while ``release()`` and ``from_native_handle`` are for owning objects that transfer or assume ownership.
|
|
|
|
Example: handle interop patterns
|
|
--------------------------------
|
|
|
|
.. code:: cpp
|
|
|
|
#include <cuda/stream>
|
|
|
|
void use_handle_interop(cuda::device_ref device, cudaStream_t raw_stream) {
|
|
// _ref from native handle (non-owning).
|
|
cuda::stream_ref borrowed{raw_stream};
|
|
|
|
// Universal handle access.
|
|
assert(borrowed.get() == raw_stream);
|
|
|
|
// Owning from native handle (assumes ownership).
|
|
auto owned = cuda::stream::from_native_handle(raw_stream);
|
|
|
|
assert(owned.get() == raw_stream);
|
|
|
|
// Release ownership back to CUDA Runtime.
|
|
cudaStream_t released = owned.release();
|
|
|
|
assert(released == raw_stream);
|
|
}
|
|
|
|
Error handling
|
|
--------------
|
|
|
|
CCCL Runtime APIs use C++ exceptions for error handling. Failures from runtime abstractions are reported by throwing an
|
|
exception, so normal code can be written without manually checking and propagating a status code after each operation.
|
|
|
|
This differs from the traditional CUDA Runtime API, where operations generally return ``cudaError_t`` values that the
|
|
caller must check against ``cudaSuccess`` and propagate or handle. When using CUDA Runtime calls directly, continue to
|
|
check their return values; when using CCCL Runtime wrappers, handle failures with normal C++ exception handling.
|
|
|
|
At a CUDA Runtime-style boundary, catch ``cuda::cuda_error`` and return its stored status.
|
|
|
|
.. code:: cpp
|
|
|
|
#include <cuda/stream>
|
|
|
|
cudaError_t use_stream(cuda::stream_ref stream) noexcept {
|
|
try {
|
|
// stream usage
|
|
stream.sync();
|
|
return cudaSuccess;
|
|
} catch (const cuda::cuda_error& err) {
|
|
return err.status();
|
|
}
|
|
}
|
|
|
|
Device selection
|
|
----------------
|
|
|
|
The Runtime API emphasizes explicit device selection. Most entry points take a :cpp:class:`cuda::device_ref` or a
|
|
device-bound resource (such as :cpp:struct:`cuda::stream`) rather than relying on implicit global state like
|
|
``cudaSetDevice``. This
|
|
makes device ownership and lifetime clearer, especially in multi-GPU code.
|
|
|
|
The current device can still be set via the CUDA Runtime, but cccl-runtime APIs ignore that global state and require an
|
|
explicit device argument. cccl-runtime also does not provide APIs that read or mutate the current device, by design.
|
|
|
|
|
|
.. _cccl-runtime-cudart-default-stream:
|
|
|
|
Default stream interop
|
|
----------------------
|
|
|
|
The CUDA default (NULL) stream is not exposed as a first-class runtime object because it is tied to implicit per-device
|
|
state and encourages hidden dependencies. Instead, it can be wrapped into :cpp:class:`cuda::stream_ref` when needed for
|
|
interop.
|
|
|
|
.. note::
|
|
|
|
When wrapping the NULL stream, the current device must be set explicitly first. CUDA binds the NULL stream to the
|
|
active device, so the wrapper must be created after selecting the correct device.
|
|
|
|
Example: wrapping the default stream
|
|
------------------------------------
|
|
|
|
.. code:: cpp
|
|
|
|
#include <cuda/stream>
|
|
|
|
void use_default_stream(int device_id) {
|
|
cudaSetDevice(device_id);
|
|
|
|
cuda::stream_ref default_stream{cudaStreamPerThread};
|
|
// Use default_stream with cccl-runtime APIs.
|
|
}
|
|
|
|
The above applies to Driver API interop cases as well, where the current context must be managed by the user rather than
|
|
the current device setting.
|
|
|
|
|
|
.. _cccl-runtime-cudart-non-blocking-streams:
|
|
|
|
Non-blocking stream creation
|
|
----------------------------
|
|
|
|
Constructing a new :cpp:struct:`cuda::stream` always creates a stream with CUDA Runtime non-blocking behavior. This is
|
|
the behavior of CCCL Runtime-created streams; wrapping or taking ownership of an existing ``cudaStream_t`` preserves the
|
|
behavior of that handle.
|
|
|
|
In the CUDA Runtime API, the blocking/non-blocking stream creation flag controls synchronization with the CUDA default
|
|
(NULL) stream. Because CCCL Runtime treats the default stream as an interop case rather than a first-class object,
|
|
as described in :ref:`default stream interop <cccl-runtime-cudart-default-stream>`, :cpp:struct:`cuda::stream` does not
|
|
expose a blocking/non-blocking construction option.
|
|
|
|
New Runtime code should express ordering between explicit streams directly, for example by making one
|
|
:cpp:class:`cuda::stream_ref` wait on another. Code that needs legacy CUDA Runtime implicit stream semantics should wrap
|
|
the relevant CUDA Runtime stream handle in :cpp:class:`cuda::stream_ref` (or take ownership with
|
|
``cuda::stream::from_native_handle``); operations submitted through the wrapper use the same native handle and preserve
|
|
that handle's CUDA Runtime semantics, including any default-stream synchronization semantics.
|