Module ml::device
devices: where a tensor's values live.
Every tensor has a device: DEVICE_CPU, or a GPU backend (DEVICE_ROCM for AMD, DEVICE_CUDA for NVIDIA, DEVICE_MPS for Apple Silicon, and the cross-vendor DEVICE_VULKAN and DEVICE_WEBGPU). An op runs on the device of its operands -- if any operand lives on a GPU, the op runs there, uploading the others -- and its result lives there too, so a model whose parameters are on a GPU trains on that GPU through the same code that trains it on the CPU. The backward pass runs each node where its forward pass ran. An op without a kernel on the device computes on the CPU (the operands are copied back first), so every op works on every device; the GPU only changes the speed.
Choosing a device:
explicitly t.to(DEVICE_ROCM) (a copy, a graph node), p.place(DEVICE_ROCM)
(moves a parameter in place), model_to(&net, DEVICE_ROCM),
TrainConfig.device;
by default device_set_default(d) for everything that asks for
device_default();
automatically device_auto(): the first usable GPU among the backends the
program was built with (ROCm, CUDA, Metal, then Vulkan and
WebGPU), else the CPU. SAFEC_DEVICE=cpu|rocm|cuda|mps|vulkan|
webgpu in the environment overrides it.
Backends are enabled at build time by the program's features (safeguard build --features rocm / cuda / mps / vulkan / webgpu, reaching the sources as SAFEC_FEATURE_ROCM, ..._CUDA, ..._MPS, ..._VULKAN, ..._WEBGPU); a program built without any runs everything on the CPU and links no GPU library. Link:
rocm -lamdhip64 -lhiprtc -lrocblas (-lMIOpen for MIOpen convolutions)
cuda -lcuda -lcublas -lnvrtc (-lcudnn for cuDNN convolutions)
mps -framework Metal -framework Foundation -lobjc
vulkan -lvulkan (the Vulkan loader; on macOS Homebrew's vulkan-loader and
molten-vk, -L/opt/homebrew/lib)
Each backend is a struct of static functions (the interface below), resolved at compile time; the runtime dispatch in device.sc calls them once the program's tensors say which device an op runs on.
Structs
| DeviceStats | Counters since the program started (or the last device_stats_reset): what moved between host and device, and how many kernels and GEMMs ran. A training step that keeps everything on the device shows no downloads besides the loss it reads, and no uploads besides its batch. cpuLaunches counts the kernels and GEMMs a backend ran on the CPU (device_ref.sc) for want of a GPU version: 0 when the GPU ran them all. |
| DevArgs | |
| DevGemm | A strided batch of GEMMs: for i < batch, c_i = alpha op(a_i) op(b_i) + beta c_i with x_i = x + i * sx (strides in elements), as gemm(). dtype (DEVICE_FP32 / FP16 / BF16) is a and b's element type; c is fp32 and accumulates in fp32. |
| DevConv | |
| DeviceOps |
Type aliases
| DevAvailFn | The dispatch table device.sc calls through (internal): one entry per GPU device, filled in by the backend's register function when the program links it. |
| DevAllocFn | |
| DevReleaseFn | |
| DevUploadFn | |
| DevDownloadFn | |
| DevCopyFn | |
| DevSyncFn | |
| DevGemmFn | |
| DevGemmBatchedFn | |
| DevConvFn | |
| DevStorageFn | |
| DevLaunchFn | |
| DevOffsetFn |
Functions
| device_compiled | 1 if the program was built with the backend (always for DEVICE_CPU). |
| device_available | 1 if the backend is compiled in and a GPU is present (initializes it). |
| device_auto | The first available GPU (ROCm, CUDA, Metal, Vulkan, then WebGPU), else DEVICE_CPU; SAFEC_DEVICE in the environment picks one explicitly. |
| device_set_default | |
| device_default | |
| device_name | "cpu", "rocm", "cuda", "mps", "vulkan", "webgpu". |
| device_sync | Waits for the device's queued work (no-op on the CPU). 1 on success. |
| device_bytes_reserved | Device memory the tensor cache holds (in use + cached free), in bytes. |
| device_cache_clear | Returns the device's cached free blocks to the driver. |
| device_stats | |
| device_stats_reset | |
| device_set_precision | Sets what convolutions and matrix products on 'device' compute in (see DEVICE_FP32 above); 1 on success. The previous setting comes back from device_precision. |
| device_precision | |
| device_set_precision_pick | |
| device_precision_pick | |
| device_loss_scale | The fp16 loss scale on 'device' (waits): gradients are scaled by it during backward and unscaled when backward ends, a step that overflowed is skipped by the optimizer, and the scale halves on overflow and doubles after 2000 clean steps. 1 when the device isn't in fp16. |
Macros
| DEVICE_CPU | |
| DEVICE_ROCM | |
| DEVICE_CUDA | |
| DEVICE_MPS | |
| DEVICE_VULKAN | |
| DEVICE_WEBGPU | |
| DEVICE_COUNT | |
| DEVICE_AUTO | What device_default() returns: set with device_set_default (a device that isn't available falls back to device_auto()), DEVICE_AUTO (-1) to go back to automatic selection. |
| DEVK_PTRS | A device kernel launch: the kernel (DK_* in device_kernels.h), how many threads (one per element unless the kernel says otherwise), and its arguments: device buffers, integers and floats, passed to the kernel as one struct (the same layout on every backend). |
| DEVK_INTS | |
| DEVK_FLOATS | |
| DEVICE_FP32 | Precisions (device_set_precision): what convolutions and matrix products read on a device. 16-bit modes keep values, weights, gradients and the optimizer state in fp32, feed these ops 16-bit copies and accumulate in fp32; fp16 also scales the loss (device_loss_scale). |
| DEVICE_FP16 | |
| DEVICE_BF16 | |
| DEVICE_PRECISION_KEEP | TrainConfig.precision: the device's own setting |
| DEV_TAG_SHIFT | 16-bit storage: on a backend that takes it (storage16), activations of a 16-bit precision live on the device in fp16 / bf16. A buffer's element type rides in the top two bits of its handle (DEV_TAG_*); every kernel, copy, GEMM and convolution reads it from there. |
| DEV_TAG_MASK | |
| DEV_CONV_FWD | A 2D convolution (transpose 0) or transposed convolution (1) in NCHW, in the layer's terms: x the input [batch, inC, inH, inW], y the output [batch, outC, outH, outW], w [outC, inC, kH, kW] (a transposed one's [inC, outC, kH, kW]). op 0: y = conv(x, w); 1: x = the input gradient from y's; 2: w = the weight gradient from y's and x. The result is overwritten. |
| DEV_CONV_BWD_DATA | |
| DEV_CONV_BWD_WEIGHTS | |
| DEVICE_PICK_TIMED | How a 16-bit precision picks per shape: DEVICE_PICK_TIMED (the default) times each new convolution / matmul shape in 16 bits against fp32 and keeps the faster; DEVICE_PICK_ALWAYS runs every one it can in 16 bits (tests, or to measure). Returns the previous mode. |
| DEVICE_PICK_ALWAYS |