Repository navigation
numr 0.8.0
Destination-passing ops and a capture-safe CUDA stream for graph replay. CUDA kernels ship as multi-arch fatbins inside the binary. New activation, padding, grouped-matmul, and transform ops.
Breaking
CapturedGraph::new_with_arenatakes a fifth parameter,arena_bytes_used: usize: the arena's peak footprint at the end of capture.CudaClient::stream()returns&GuardedStreaminstead of&CudaStream. Build launches withGuardedStream::launch_builder.GuardedStream::raw()returns the bareCudaStream.- New required trait methods —
BinaryOps::copy_into,ShapeOps::pad_mode, andMatmulOps::matmul_widehave no default body. A downstream implementor of these traits must add them. numr::runtime::cuda::kernels::launch_gemv_kernel_btis removed. F32 and integer small-M matmul go throughMatmulOps::matmul.kernel_names::NORM_MODULEandFUSED_ADD_NORM_MODULEare removed. Norm kernels ship as one module per op:NORM_RMS_MODULE,NORM_LAYER_MODULE,NORM_GROUP_MODULE,FUSED_ADD_RMS_NORM_MODULE,FUSED_ADD_LAYER_NORM_MODULE.kernel_names::GEMV_INT_MODULEis removed with the integer GEMV kernel.
Added
- Destination-passing ops —
BinaryOps::copy_into,HostCopyOps::write_host_slice, andRandomOps::{rand_into, rand_seeded_into, randn_into, randn_seeded_into}write into a caller-owned tensor. The buffer keeps its device address, so a captured CUDA graph replays against it. On CUDA each one records as a graph node. - CUDA graph capture lock —
numr::runtime::cuda::captureaddsDeviceCaptureLock,CapturePermit,EnqueuePermit,thread_is_capturing,GuardedStream, andGuardedLaunchBuilder. A capture holds the device's write lock for its whole region. Each enqueue holds the read side for that one enqueue. Threads that share a device no longer leak launches into another thread's graph. - Capture queries —
CudaClient::is_capturing()answers for the calling thread.CudaClient::stream_capture_active()reports the driver's capture state on the stream. - Typed arena exhaustion —
Error::ArenaExhausted { requested, used, capacity }replaces a formatted string.Error::as_arena_exhausted()returns the three byte counts, unwrapping oneAllocFailedlayer.CapturedGraph::arena_bytes_used()reports the arena's peak, so a caller can size the next capture. - Activations —
ActivationOps::gelu_erf,gelu_erf_mul, andgelu_erf_mul_bwdcompute exact erf GELU, matching PyTorchF.gelu(approximate="none").snake_betaandsnake_beta_bwdcompute Snake with per-channelalphaandbeta. All five run on CPU, CUDA, and WebGPU, with autograd wrappers. - Padding —
PadMode::{Constant, Reflect}andShapeOps::pad_mode.Reflectmatches PyTorchF.pad(mode="reflect"). CPU, CUDA, and WebGPU each have a native reflect kernel. - Matmul —
MatmulOps::matmul_widereturns the F32 accumulator for F16 and BF16 inputs, so a caller that keeps summing rounds once.GroupedMatmulOps::{grouped_matmul, grouped_matmul_activation}run one matmul per row group, with group offsets held on the device. Grouped matmul covers F32, F16, and BF16 on CPU and CUDA. - Transforms and norms —
FwhtOps::fwhtis a block-wise, normalized Walsh-Hadamard transform on CPU, CUDA, and WebGPU.NormalizationOps::l2_normalizedivides bymax(‖x‖₂, eps)along one dim. - Device profiles —
DeviceProfile,DeviceArch, andDeviceCapsdescribe SM count, shared memory, and tensor-core support (bf16,int8,fp8, and the int8 MMA shape).Device::profile()defaults to a conservative unknown profile.CudaDevicequeries the driver once per device and caches the result. - Context-free CUDA queries —
CudaDevice::count(),CudaDevice::product_name(), andCudaDevice::total_memory()need no CUDA context.count()returns 0 when the driver library is absent. - CUDA schedule tuning —
numr::runtime::cuda::tune(tuned,time_launches) picks between launch alternatives that produce identical bits. It probes once per device and key, then caches the winner. Probes never run inside a graph capture. SetNUMR_CUDA_TUNEto0,false, oroffto use the fixed defaults. - Embedded multi-arch fatbins —
build.rscompiles every kernel to a fatbin with native SASS per arch, pluscompute_75andcompute_120PTX for JIT. The fatbins are embedded withinclude_bytes!, so the binary reads no kernel file at run time. NUMR_CUDA_ARCH— selects the SASS archs: unset builds for the host GPU, a list such as86,89builds those archs, andallbuilds every supported arch. Useallfor redistributable binaries.- CUDA kernels — tensor-core WMMA GEMM with fused bias, activation, and residual epilogues, at several tile sizes. im2col + GEMM paths for
conv1d,conv2d, andconv_transpose1d. A column-blockeddepthwise_conv2d. A tiled transpose for strided copies. Serial and split kernels for dim reductions. A small-M F32 kernel for transposed weights.
Changed
- CUDA requirement — the
cudafeature needs CUDA 12.8 or newer, because the build always emitscompute_120PTX. CUDA 12.8+ and 13.x both work. - CPU transcendental accuracy — f64
exp,exp2,expm1,sin,cos,tan, and the hyperbolic family reach double precision. The f64 log family stays within 2 ulps. f32exp,exp2,expm1,cbrt,log, and the hyperbolic family reach single precision. f32sin,cos,tan,atan,asin, andacosstay within 2 ulps. f64exp,sinh, andcoshcover the full representable domain. - CPU performance — Rayon dispatch is gated on work size. Broadcast binary ops take a row-wise SIMD path. Strided copies coalesce dims and walk rows. FFT twiddle tables are cached. Strided
conv1dis vectorized. The tiled F32 matmul reads a transposed B in place instead of copying it. - Dependencies — the archived
pastemacro crate is replaced by its maintained forkpastey. The benchmarks use fluxbench 0.2. - CUDA performance — RMSNorm uses a register-cached single pass with quad-packed loads. Norm kernels compile one fatbin per op. Softmax uses a flattened grid. Batched F32 GEMM and grouped F16/BF16 GEMM run on tiled and tensor-core kernels.
Fixed
- Determinism — CPU element-wise kernels run per row, so a row's result no longer depends on how many rows share a call. The CPU matmul column split depends on shape only, never on thread count. CUDA conv kernel selection no longer depends on batch size.
- Graph capture — the allocator routes only the capturing thread to the arena. A failed
begin_captureunfreezes the allocator. The CUDA index-bounds check and the Sobol warmup check no longer synchronize or misread state during another thread's capture. A missing Sobol cache entry returns an error instead of panicking. - Tuning races — concurrent probes for one key run once, and the first cached value stays.
- CUDA launch limits — element-wise launches that overflow the grid and matmul launches over the shared-memory budget return an error instead of computing wrong elements.
- Batch broadcasting —
matmul_bias,matmul_bias_residual,fp8_matmul, andsemiring_matmulbroadcast each operand's batch dims per index instead of wrapping. - Narrow-float accumulation — CPU fused GEMM epilogues and every direct convolution kernel accumulate F16 and BF16 in F32, and F64 in F64.
- Norm stability —
layer_norm,group_norm, andfused_add_layer_normshift by a per-row reference before accumulating, on every backend. The WebGPU shaders keep that shift through compilation. - Special functions —
bessel_i0andbessel_i1no longer overflow early in the asymptotic branch. SIMDpowhonors IEEE 754 identities for non-finite exponent products. f64powfalls back to scalar for non-positive bases. CPU f64 softmax and logsumexp, and the CUDA softmax merge, handle all-masked rows. NEON f64gelu_mulsaturatestanhinstead of dividingexp(2x). - SIMD dispatch — f64 AVX-512 kernels that use bitwise ops require AVX-512DQ, so AVX-512F-only CPUs no longer trap.
- Autograd —
conv1dandconv2dinput gradients are correct when input and output channel counts differ. - WMMA selection — BF16 WMMA runs only on devices with native BF16 tensor cores (sm_80+). Padding and launch read the same predicate.
dispatch_dtype!— a wildcard arm returnsError::UnsupportedDType, so downstream crates compile against the#[non_exhaustive]DType.- Feature gating — builds without
f16keep the non-half code in the clamp andwherekernels.
Full diff: v0.7.0...v0.8.0