std1.0.0-beta-7 SafeC docs

Module ml::device

#include <std/ml/device.h> source

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

DeviceStatsCounters 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
DevGemmA 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

DevAvailFnThe 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_compiled1 if the program was built with the backend (always for DEVICE_CPU).
device_available1 if the backend is compiled in and a GPU is present (initializes it).
device_autoThe 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_syncWaits for the device's queued work (no-op on the CPU). 1 on success.
device_bytes_reservedDevice memory the tensor cache holds (in use + cached free), in bytes.
device_cache_clearReturns the device's cached free blocks to the driver.
device_stats
device_stats_reset
device_set_precisionSets 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_scaleThe 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_AUTOWhat 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_PTRSA 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_FP32Precisions (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_KEEPTrainConfig.precision: the device's own setting
DEV_TAG_SHIFT16-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_FWDA 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_TIMEDHow 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