cuda
Typed bindings to the CUDA Runtime and cuBLAS for NVIDIA GPU compute. This is plain C FFI over the vendor SDK: C+ stays a consumer of CUDA, with no kernel language and nothing to reimplement. For the wider GPU/numerics picture and the backend matrix, see GPU & numerics.
Three sub-modules, plus a cuda/cuda facade that re-exports the types:
cuda/runtime— device management (device_count,set_device,synchronize) and theCudaErrortype (.code(),.message()).cuda/buffer— aDeviceBufferowns device memory and frees it onDrop(cudaFree), so device allocations follow the same ownership rules as host memory. Allocate withalloc(bytes:); move bytes with.write(from:, bytes:)/.read(to:, bytes:).cuda/cublas— a cuBLASHandle(created viahandle(), freed onDropviacublasDestroy_v2) exposing the dense Level-3 / Level-2 routinessgemmandsgemv, column-major, matching the cuBLAS ABI exactly.
import "cuda/runtime" as rt;
import "cuda/buffer" as buf;
import "cuda/cublas" as blas;
import "stdlib/result" as result;
// hostA, hostB, hostC are host *u8 buffers laid out column-major.
// A cuBLAS context, created once; Drop = cublasDestroy_v2.
guard let result::Result[blas::Handle, rt::CudaError]::Ok(h) = blas::handle() else { return 1 as i32; };
// `alloc` returns a value-or-reason Result; each DeviceBuffer frees its
// device memory on scope exit (Drop = cudaFree).
guard let result::Result[buf::DeviceBuffer, rt::CudaError]::Ok(dA) = buf::alloc(bytes: 16 as usize) else { return 2 as i32; };
guard let result::Result[buf::DeviceBuffer, rt::CudaError]::Ok(dB) = buf::alloc(bytes: 16 as usize) else { return 3 as i32; };
guard let result::Result[buf::DeviceBuffer, rt::CudaError]::Ok(dC) = buf::alloc(bytes: 16 as usize) else { return 4 as i32; };
// Stage inputs on the device. write/read return Option[CudaError] (None = ok).
let _ = dA.write(from: hostA, bytes: 16 as usize);
let _ = dB.write(from: hostB, bytes: 16 as usize);
// Column-major C = alpha*op(A)*op(B) + beta*C. Operands are device pointers;
// the call returns Option[CudaError] instead of trapping.
let _ = h.sgemm(
blas::Op::N, blas::Op::N,
2 as i32, 2 as i32, 2 as i32,
1.0f32, dA.device_ptr(), 2 as i32, dB.device_ptr(), 2 as i32,
0.0f32, dC.device_ptr(), 2 as i32);
let _ = rt::synchronize();
let _ = dC.read(to: hostC, bytes: 16 as usize);
Because cuBLAS is column-major, lay out matrices column-major (or pass the
transpose flags) exactly as you would from C. The Handle and every
DeviceBuffer release their resources deterministically at scope exit; there is
no explicit teardown to forget.
Linking
The CUDA libraries usually live in lib64, outside the linker's default search
path. List the directory in your manifest so it resolves at both link and run
time without LD_LIBRARY_PATH:
[link]
search-paths = ["/usr/local/cuda/lib64"]
For the CPU fallback and a result-checking reference, see cblas and accelerate.