API Quick Reference#
This appendix is a condensed reference for the cuda-oxide device and host APIs.
For full documentation, run cargo doc --no-deps --open from the workspace
root.
Attributes and Macros#
Kernel and Device Attributes#
use cuda_device::{kernel, device, launch_bounds, cluster_launch, cooperative_launch};
#[kernel]
pub fn vecadd(a: &[f32], b: &[f32], mut c: DisjointSlice<f32>) { /* ... */ }
#[kernel]
pub fn unrolled(mut data: DisjointSlice<f32>) {
let mut i = 0;
#[unroll(4)]
while i < 16 { /* ... */ i += 1; }
}
#[kernel]
#[launch_bounds(256, 2)]
pub fn tuned_kernel(data: &mut [f32]) { /* ... */ }
#[kernel]
#[cluster_launch(4, 1, 1)]
pub fn cluster_kernel(data: &mut [f32]) { /* ... */ }
#[kernel]
#[cooperative_launch]
pub fn grid_sync_kernel(data: &mut [f32]) { /* ... */ }
#[device]
fn helper(x: f32) -> f32 { x * x }
Attribute |
Purpose |
|---|---|
|
Mark a function as a GPU kernel entry point ( |
|
Mark a helper function or |
|
Request full unrolling, or unrolling by a factor |
|
Occupancy hints for register allocation |
|
Set compile-time cluster dimensions (Hopper+) |
|
Launch cooperatively via |
|
Mark as convergent (barrier semantics) |
|
Mark as side-effect free |
|
Mark as read-only |
Use these annotations only on an explicit counted while loop inside a
#[kernel] or #[device] function. Range-based for loops are not yet
recognized by the unroll pass. Nested loops and multiple continue paths are
supported. Full #[unroll] preserves break paths and multiple exit targets.
Partial #[unroll(N)] requires a positive step, a < or <= test, an
unchanging limit, and no exit besides the normal header test. Other requests
warn and are not unrolled.
One annotation may create at most 1,024 body copies, 8,192 cloned basic blocks, and 65,536 cloned operations. Larger requests warn and are not unrolled.
Debug and PTX Macros#
use cuda_device::{gpu_printf, gpu_assert, ptx_asm};
gpu_printf!("thread %d: val = %f\n", idx as i32, val as f64);
gpu_assert!(val.is_finite());
gpu_assert!(val >= 0.0, "expected non-negative value");
let y: u32;
unsafe {
ptx_asm!("add.u32 %0, %1, %1;", out("=r") y, in("r") x, options(register_only));
}
Macro |
Purpose |
|---|---|
|
Device-side formatted output (lowers to |
|
Runtime assertion; calls |
|
Runtime assertion with CUDA diagnostic; message must be a literal |
|
Unsafe CUDA inline PTX |
The message form lowers to CUDA’s device-side __assertfail system call.
The driver reports the message and call-site metadata, and synchronization
returns CUDA_ERROR_ASSERT.
Compile-time policy configuration#
use cuda_device::config::{
Atom, AtomKind, AtomSpec, Block, Cluster, ColumnMajor, Global, Layout,
MemorySpace, Policy, PolicyId, Register, RowMajor, Scope, Shape, Shape1,
Shape2, Shape3, Shared, TensorMemory, Thread, Tile, TileSpec, Warp,
WarpGroup,
};
Policies describe compile-time kernel configurations using zero-sized Rust types. A generic kernel is monomorphized once for every concrete policy type; the policy is not passed as a runtime kernel argument.
trait VectorPolicy: Policy {
type BlockTile: TileSpec;
type ElementAtom: AtomSpec;
const MAX_THREADS: u32;
const MIN_BLOCKS: u32;
const UNROLL: u32;
}
enum SmallTilePolicy {}
impl Policy for SmallTilePolicy {
const ID: PolicyId =
PolicyId::new(0x706f_6c69_6379_5f63, 1);
}
API |
Description |
|---|---|
|
Minimal base trait for a named compile-time kernel policy |
|
Explicit stable identity containing a project-specific namespace and policy-local value |
|
Trait exposing a static shape’s rank, extents, and checked element count |
|
One-dimensional compile-time shape |
|
Two-dimensional compile-time shape |
|
Three-dimensional compile-time shape |
|
Metadata-only description combining shape, layout, memory space, and execution scope |
|
Type-level access to the components of a tile description |
|
Metadata-only description of an operation, logical footprint, and participating threads |
|
Open marker trait identifying a domain-specific operation |
|
Type-level access to an atom’s operation kind, shape, and scope |
|
Open trait for memory-order metadata |
|
Layout whose rightmost coordinate is contiguous |
|
Layout whose leftmost coordinate is contiguous |
|
Open trait describing a CUDA storage location |
|
Device global-memory marker |
|
Per-block shared-memory marker |
|
Thread-local register-storage marker |
|
Hardware tensor-memory marker |
|
Open trait describing the threads cooperating on an operation |
|
Single-thread execution scope |
|
Single-warp execution scope |
|
Hardware warpgroup execution scope |
|
Thread-block execution scope |
|
Thread-block-cluster execution scope |
Tile and Atom are descriptions only. They do not allocate storage, provide
pointer access, emit GPU instructions, synchronize threads, or establish a
safety property. A domain-specific policy trait gives those descriptors
meaning and validates supported combinations.
PolicyId values are supplied explicitly by the policy library. They are not
derived from Rust TypeId, type names, compiler mangling, or hashes. Keep an ID
stable while the policy’s generated behavior remains unchanged and allocate a
new value when that behavior changes.
Policy-associated constants can currently be used in compile-time
launch_bounds and partial-loop unroll expressions:
use cuda_device::{kernel, launch_bounds};
#[kernel]
#[launch_bounds(P::MAX_THREADS, P::MIN_BLOCKS)]
pub unsafe fn transform<P: VectorPolicy>(
input: *const u32,
output: *mut u32,
count: u32,
) {
let mut lane = 0;
#[unroll(P::UNROLL)]
while lane < count {
// ...
lane += 1;
}
}
Generic policy expressions require #![feature(generic_const_exprs)].
launch_contract fields, cluster dimensions, and dynamic shared-memory sizes
currently remain literal.
See the
policy_config
example for two concrete policies that generate independent PTX
specializations and policy-specific prepared launches.
Thread Identification#
use cuda_device::thread;
let idx = thread::index_1d(); // ThreadIndex<'_, Index1D>
let idx2d = thread::index_2d::<128>(); // Option<ThreadIndex<'_, Index2D<128>>>
let idx2d_r = unsafe { thread::index_2d_runtime(stride) }; // Option<ThreadIndex<'_, Runtime2DIndex>>
let idx32 = thread::index_1d_u32(launch_context); // ThreadIndex32<'_>
let pos32 = thread::coord_2d_u32(launch_context); // ThreadCoord2D32<'_>
let tid_x = thread::threadIdx_x(); // u32
let bid_x = thread::blockIdx_x(); // u32
let bdim_x = thread::blockDim_x(); // u32
Function |
Returns |
Description |
|---|---|---|
|
|
Unique linear index (1D grids) |
|
|
Const-stride 2D index; mismatched strides are a type error |
|
|
Runtime-stride 2D index; caller asserts |
|
|
1-D index as |
|
|
2-D row/column as |
|
|
2D row index |
|
|
2D column index |
|
|
Thread index within block |
|
|
Block index within grid |
|
|
Block dimensions |
thread::index_2d::<S>() and thread::index_2d_runtime(s) return None
when the computed column exceeds the stride — use it to skip the
right-edge tail in non-aligned 2D kernels.
index_2d::<S> is the safe const-stride form; the const generic encodes the
stride in the witness type so threads cannot use different strides.
index_1d requires inactive Y/Z dimensions, and 2D indices require inactive
Z. A matching PreparedLaunch<K> proves this without device checks. Otherwise,
the device rejects the wrong rank: index_1d creates an invalid witness and
2D helpers return None. A raw launch remains unsafe because its other memory
and launch obligations are unchecked. index_2d_runtime is the escape hatch
for launches whose stride is only known at runtime; the caller takes on the
“every thread used the same stride” obligation by writing unsafe. Full
discussion in The Safety Model.
Safe Parallel Writes — DisjointSlice#
use cuda_device::{DisjointSlice, kernel};
#[kernel]
pub fn vecadd(a: &[f32], b: &[f32], mut c: DisjointSlice<f32>) {
if let Some((c_elem, idx)) = c.get_mut_indexed() {
let i = idx.get();
*c_elem = a[i] + b[i];
}
}
Method |
Signature |
Description |
|---|---|---|
|
|
One-call form: mints the witness and resolves it. Index1D / Index2D. |
|
|
Bounds-checked mutable access from an explicit witness |
|
|
Unsafe, unchecked access |
|
|
Number of elements |
get_mut_indexed is gated on IndexSpace: IndexFormula (impl’d by
Index1D and Index2D<S>). For Runtime2DIndex slices, use the
explicit unsafe { thread::index_2d_runtime(s) } + get_mut(idx) pair.
For fixed-size tiles, use DisjointSlice<T, LinearTiles<N>>::tile_thread32
or DisjointSlice<T, RowMajorTiles<R, C, S>>::tile_2d32. Each method checks a
complete tile once, then at_const accesses known positions without another
runtime bounds check. S is the caller-declared logical row pitch and must
match the buffer layout. See Check a tile once.
Synchronization#
Block-Level#
thread::sync_threads(); // __syncthreads() equivalent
Managed Barriers (Hopper+)#
use cuda_device::{ManagedBarrier, TmaBarrierHandle, Uninit, Ready};
// Typestate lifecycle: Uninit → Ready → Invalidated
let bar: TmaBarrierHandle<Uninit> = TmaBarrierHandle::from_static(ptr);
let bar: TmaBarrierHandle<Ready> = unsafe { bar.init(thread_count) };
let token = bar.arrive();
bar.wait(token);
unsafe { bar.inval() };
Operation |
Description |
|---|---|
|
Initialize barrier with expected arrival count |
|
Signal arrival, returns |
|
Arrive and set expected TX byte count (for TMA) |
|
Block until all arrivals + TX complete |
|
Invalidate barrier (cleanup) |
Warp Primitives#
use cuda_device::warp;
let lane = warp::lane_id(); // 0–31
let wid = warp::warp_id();
// Shuffle
let partner = warp::shuffle_xor_f32(val, mask);
let from_above = warp::shuffle_down_f32(val, delta);
let from_below = warp::shuffle_up_f32(val, delta);
let from_lane = warp::shuffle_f32(val, src_lane);
// i32 variants
let partner_i = warp::shuffle_xor_i32(val, mask);
// Vote
let all_true = warp::all(predicate);
let any_true = warp::any(predicate);
let mask = warp::ballot(predicate);
let count = warp::popc(mask);
Shuffle Operations#
Function |
Description |
|---|---|
|
Exchange with lane |
|
Read from lane |
|
Read from lane |
|
Read from specific lane |
Vote Operations#
Function |
Returns |
Description |
|---|---|---|
|
|
True if predicate holds for all lanes |
|
|
True if predicate holds for any lane |
|
|
Bitmask of lanes where predicate is true |
|
|
Population count of set bits |
Atomics#
Scoped GPU Atomics#
use cuda_device::atomic::{DeviceAtomicU32, AtomicOrdering};
static COUNTER: DeviceAtomicU32 = DeviceAtomicU32::new(0);
// In kernel:
COUNTER.fetch_add(1, AtomicOrdering::Relaxed);
let old = COUNTER.load(AtomicOrdering::Acquire);
Scope |
Types |
|---|---|
|
|
|
|
|
|
core::sync::atomic types (AtomicU32, AtomicBool, etc.) also compile to
GPU code, defaulting to system scope.
TMA — Tensor Memory Accelerator (Hopper+)#
use cuda_device::tma::TmaDescriptor;
use cuda_device::tma::{cp_async_bulk_tensor_2d_g2s, cp_async_bulk_commit_group};
// Host: build descriptor (128 bytes, opaque)
// Device: issue async bulk copy
cp_async_bulk_tensor_2d_g2s(smem_ptr, &desc, coord_x, coord_y, barrier_ptr);
cp_async_bulk_commit_group();
Function |
Description |
|---|---|
|
Global → shared async bulk copy |
|
Shared → global async bulk copy |
|
Multicast to all CTAs in cluster |
|
Commit outstanding copies |
|
Wait until ≤ n groups remain |
Cluster Programming (Hopper+)#
use cuda_device::cluster;
let rank = cluster::block_rank(); // This block's rank in the cluster
let size = cluster::cluster_size(); // Number of blocks in cluster
cluster::cluster_sync(); // Barrier across all cluster blocks
// Distributed Shared Memory
let remote_ptr = cluster::map_shared_rank(local_ptr, target_rank);
let val = cluster::dsmem_read_u32(remote_ptr);
Tensor Cores — WGMMA (Hopper, SM 90)#
use cuda_device::wgmma;
wgmma::wgmma_fence();
wgmma::wgmma_commit_group();
wgmma::wgmma_wait_group::<0>();
Warpgroup MMA: 4 warps (128 threads) issue matrix multiply-accumulate from shared memory. Operands described by SMEM descriptors; accumulator in registers.
Tensor Cores — tcgen05 (Blackwell, SM 100+)#
use cuda_device::tcgen05::{TmemGuard, TmemUninit, TmemReady};
use cuda_device::SharedArray;
static mut TMEM_SLOT: SharedArray<u32, 1, 4> = SharedArray::UNINIT;
let guard = TmemGuard::<TmemUninit, 512>::from_static(&raw mut TMEM_SLOT as *mut u32);
let guard = unsafe { guard.alloc() }; // TmemUninit → TmemReady
// ... issue MMA, read results via guard.address() ...
let _guard = unsafe { guard.dealloc() }; // TmemReady → TmemDeallocated
Single-thread MMA issue into dedicated Tensor Memory (TMEM). TmemGuard
manages TMEM lifetime with typestate: TmemUninit → TmemReady → TmemDeallocated.
N_COLS must be a power of 2 in the range [32, 512].
Host-Side: Kernel Launch#
Typed Synchronous#
use cuda_core::{CudaContext, DeviceBuffer, LaunchConfig};
let ctx = CudaContext::new(0).unwrap();
let stream = ctx.default_stream();
let module = kernels::load(&ctx).unwrap();
let a = DeviceBuffer::from_host(&stream, &a_host).unwrap();
let b = DeviceBuffer::from_host(&stream, &b_host).unwrap();
let mut output = DeviceBuffer::<f32>::zeroed(&stream, n).unwrap();
// SAFETY: this is a 1D launch and all buffers contain n elements.
unsafe {
module.vecadd(&stream, LaunchConfig::for_num_elems(n), &a, &b, &mut output)
}
.unwrap();
Typed Async#
use cuda_async::device_operation::DeviceOperation;
let module = kernels::load_async(0)?;
// SAFETY: this is 1D, buffers contain n elements, and module/scheduler share a context.
let op = unsafe {
module.vecadd_async(LaunchConfig::for_num_elems(n), &a, &b, &mut output)
}?;
op.sync()?; // blocking
// or: op.await?; // async with tokio
Raw generated calls are unsafe because LaunchConfig is not tied to a kernel.
A kernel with #[launch_contract] instead uses LaunchConfig1D/2D/3D to create
a checked PreparedLaunch<K>, then launches safely. cuda_launch! and
cuda_launch_async! remain unsafe lower-level APIs for explicit module loading
and custom launch code.
LaunchConfig#
Method |
Description |
|---|---|
|
Auto-configure grid/block for |
|
Direct struct construction |
Host-Side: Virtual Memory Management#
VMM lifecycle#
API |
Purpose |
|---|---|
|
Query the required allocation granularity |
|
Round a size to the required granularity |
|
Allocate physical memory |
|
Reserve a virtual address range |
|
Map physical memory into a VA range |
|
Grant read/write access to selected devices |
Mappings must be dropped before their virtual reservations and physical allocations.
Host-Side: Peer Access#
API |
Purpose |
|---|---|
|
Query whether the topology supports direct access |
|
Enable one-directional peer access |
|
Disable one-directional peer access |
Peer access is directional. Enable both directions when both devices must initiate accesses.
Host-Side: Kernel Families#
Type |
Purpose |
|---|---|
|
Fixed set of ahead-of-time compiled variants |
|
Stable ID, callable entry, and policy metadata |
|
Validates whether a variant is eligible |
|
Chooses among already eligible variants |
|
Stores stable selection IDs |
|
Disables caching |
|
Uses validated cache results or invokes the selector |
|
Bypasses cache and selector but still validates eligibility |
|
Returns the selected variant and its provenance |
|
Reports override, cache, or selector provenance |
KernelFamily::try_new rejects empty families, blank family names, and
duplicate variant IDs. The family name and revision form the cache namespace.
Increment the revision whenever variant semantics, membership, ordering, or
selection policy changes.
See Kernel Families for the complete selection model and example.
Debug Facilities#
use cuda_device::debug;
let t = debug::clock64(); // Cycle counter
debug::trap(); // Abort kernel
debug::breakpoint(); // cuda-gdb breakpoint
cuda_device::barrier::nanosleep(1000); // Sleep ~1μs
debug::prof_trigger::<7>(); // Nsight profiler trigger
Quick Reference Tables#
cuda-device Modules#
Module |
Description |
Min SM |
|---|---|---|
|
Thread/block IDs, |
All |
|
Compile-time policies, shapes, tiles, atoms, layouts, memory spaces, and scopes |
All |
|
|
All |
|
|
All |
|
Shuffle, vote, match, lane/warp ID |
All |
|
Scoped atomics (device/block/system) |
sm_70+ |
|
|
All |
|
|
All |
|
Grid-scoped |
sm_70+ |
|
Typed handles, warp/block reductions and scans |
All |
|
|
sm_90+ |
|
Thread block clusters, DSMEM |
sm_90+ |
|
|
sm_90+ |
|
Warpgroup MMA (fence/commit/wait) |
sm_90 |
|
5th-gen tensor cores, TMEM, |
sm_100+ |
|
|
All |
|
Cluster Launch Control |
sm_100+ |
Crate Map#
Crate |
Role |
|---|---|
|
Device intrinsics and types ( |
|
Proc macros ( |
|
Typed module loading plus low-level launch helpers |
|
Safe RAII wrappers for contexts, streams, buffers, VMM, and P2P |
|
|
|
Raw |
|
Cargo subcommand ( |