A finished GPU kernel sitting in the same file as the function that should call it — but doesn’t — is the kind of bug that no type checker catches and no user-facing error reports. OxiCUDA 0.5.5 found three of them.
Today we released OxiCUDA 0.5.5 — a correctness release built around two adversarial audits. The first runs the alt-backend audit that a prior production-readiness wave queued but never reached: oxicuda-metal and oxicuda-webgpu, audited and fixed on real Apple Silicon (M3, Metal 4, macOS/arm64). The second closes out a downstream investigation triggered by oxiface, a Rust face-swap CLI, underperforming on Linux+NVIDIA relative to its CoreML path — traced all the way to oxicuda-blas/oxicuda-dnn GEMM and convolution dispatch, and verified end to end on a real RTX A4000.
No CUDA SDK. No nvcc. No C/C++ toolchain. OxiCUDA is a type-safe, memory-safe, pure-Rust replacement for the entire NVIDIA CUDA Toolkit software stack — cuBLAS, cuDNN, cuFFT, cuSPARSE, cuSOLVER, cuRAND, and more, across 74 crates and ~1.31M lines of safe Rust. libcuda.so/nvcuda.dll is loaded dynamically at runtime via libloading; nothing about building or running OxiCUDA requires the CUDA SDK, headers, or a C/C++ compiler. The result compiles to a single static binary and runs everywhere the driver is present.
Why OxiCUDA 0.5.5 is a game changer
The headline finding is the kind of bug that only shows up when you go looking for it, not when you run the test suite:
oxicuda-metal’sconv2d_forwardandattentionwere false completions — pure-CPU scalar loops round-tripping every operand through the host, even though finishedconv2d_msl/attention_mslMSL kernels sat unused in the same crate with no caller anywheresoftmaxinherited the trait’sUnsupporteddefault despite a complete, numerically-stable softmax MSL shader already shipping alongside itoxicuda-webgpuhad no comparable false completion, but the same pass found a device-limits bug that silently capped every allocation and dispatch at the WebGPU conformance baseline — regardless of what the real GPU could actually do- Separately, on the CUDA side, a split-K GEMM path was fully implemented and unit-tested, but
GemmDispatcher::dispatchnever actually called it — every skinny GEMM (small M/N, large K) ran on a grid capped atM*Nthreads, no matter how much parallelism the hardware had to spare - A convolution algorithm selector could route an ordinary 3x3 convolution into
WinogradConv— whose forward kernels were comment-only PTX skeletons — soconv_forwardreturnedOk(())while leaving the output buffer completely untouched
OxiCUDA 0.5.5 ends all of that.
conv2d_forwardandattentionnow dispatch real GPU kernels on Metal, unconditionally for conv2d and via a new single-pass online-softmaxattention_msl_v2kernel for attention (one SIMD-group per query).softmaxdispatches for real (softmax_msl_with_mode, last-axis) instead of returningUnsupported.oxicuda-webgpunow requests the real adapter capability (adapter.limits()) instead of the conformance baseline, installs a non-fatal error handler instead of crashing the process on a validation error, and waits on the specific submission a copy produced instead of discarding poll errors.- Split-K GEMM is now actually wired in. A new
GemmDispatcher::dispatch_skinny_split_kroutes eligible shapes through the two-pass launch — measured ~17.6x on the exact ArcFace embedding-projection shape it was built for (2525µs to 144µs/call, RTX A4000). - A new CTA-tiled implicit-GEMM convolution engine claims most 3x3 shapes ahead of Winograd, measured at 5.7-8.0 TFLOPS against 1.7-3.5 TFLOPS for Winograd on real face-pipeline shapes — and Winograd itself went from a silent no-op to a real, numerically-validated kernel (relative-L2 error
1e-7-scale against an f64 CPU oracle). - Wrong Ampere GA10x hardware constants are fixed.
Sm86(RTX A4000/A5000/A6000/3080/3090) was sharing a match arm withSm80(the datacenter-class A100), overstating both max threads/SM and shared memory/block — live-verified against a real RTX A4000.
Technical Deep Dive
- Metal dispatch layer. A new
backend/nn.rsmodule carries the realconv2d_forward/attentiondispatch; six ad-hoc GPU dispatch paths plus the FFT plan now route through a sharedcommit_and_wait/status_to_resultso a GPU-side failure surfaces asErrinstead of a silently wrong result.gemm/batched_gemm/gemm_f16move to a runtime-parameterised v2 kernel family supporting all four transpose combinations with padded leading dimensions. - WebGPU correctness layer. Real adapter limits, a non-fatal uncaptured-error handler, submission-index-scoped readback waits, checked
u32stride/dispatch conversions forbatched_gemm, and a per-element bound fix inscan_wgsl’s write stage that could write one element pastnon an odd remainder. - GEMM/conv dispatch layer. Split-K’s reduction workspace moved from an alloc-per-call (a device-wide
cuMemFreebarrier, and incompatible with CUDA graph capture) to a bounded, reusable cache keyed on(stream, output_type, element count).compute_gridnow sizes the naive GEMM kernel’s launch from real device occupancy (sm_count * max_threads_per_sm) instead of a CTA-tiling assumption that didn’t match the kernel’s actual grid-stride design — measured 191 → 747 GFLOPS at 1024³ F32 from that fix alone. - Infrastructure layer. A new
oxicuda-dnnkernel_cachemodule turns a repeated kernel-generation call into a hash lookup plus anArcclone instead of a fresh JIT compile (~194µs per call saved on an RTX A4000), now wired into every kernel-generating module in the crate. A newoxicuda-memoryStagingBuffergives hot-path H2D/D2H transfers a reusable page-locked allocation, measured 1.55x-1.75x H2D and 1.77x-2.67x D2H over the driver’s pageable path.
Getting Started
cargo add oxicuda
The new compute module probes every backend compiled into the build and hands back the best one already initialised — no need to know ahead of time whether a machine has an NVIDIA GPU, an Apple GPU, or neither:
use oxicuda::backend::ComputeBackend;
fn main() -> oxicuda::backend::BackendResult<()> {
let backend = oxicuda::compute::default_backend()?;
println!("computing on the {} backend", backend.name());
let ptr = backend.alloc(1024)?;
backend.free(ptr)?;
Ok(())
}
On macOS with the metal feature enabled, this returns a real MetalBackend running on the Apple GPU — the same backend this release’s conv2d/attention/softmax fixes land in. Without a GPU backend compiled in, or where one can’t open, it falls back to CpuBackend rather than erroring:
[dependencies]
oxicuda = { version = "0.5", features = ["metal"] }
To run the on-device validation yourself against a real GPU:
cargo test --features gpu-tests -p oxicuda-metal
cargo test --features gpu-tests -p oxicuda-blas
What’s New in 0.5.5
oxicuda-metal:conv2d_forward,attention, andsoftmaxnow dispatch real GPU kernels instead of silent CPU fallback or anUnsupportederror.oxicuda-metal: six GPU dispatch paths plus the FFT plan now treat a non-Completedcommand buffer status as a real error instead of assumed success.oxicuda-webgpu: real adapter limits, a non-fatal error handler, and correct submission-scoped readback waits — three separate silent-failure modes closed in one pass.oxicuda-blas: split-K GEMM is wired into the dispatcher for skinny shapes (~17.6x measured on the exact ArcFace shape it targets) and its workspace is now a bounded, reusable, CUDA-graph-capturable cache.oxicuda-dnn: a new CTA-tiled implicit-GEMM convolution engine claims most 3x3 shapes;WinogradConvgoes from a silent no-op to a real, numerically-validated kernel for the shapes tiling declines.oxicuda-ptx:Sm86(Ampere GA10x) gets its own hardware-constant match arm instead of sharing A100’s figures — closing a shared-memory over-request that would have failed kernel launch on real GA10x hardware.- New
oxicuda-dnnkernel_cacheandoxicuda-memoryStagingBuffer— JIT-compile caching and reusable pinned staging buffers, built in the course of the GEMM/conv investigation and now wired into every kernel-generating module inoxicuda-dnn. - New
oxicuda::computemodule — one-call backend selection (default_backend) that probes, ranks, and initialises the best compiled-in backend for the current machine. - Test suite expanded to 38,987 passing tests (
--all-features; 37,593 with default features), up from 38,689/37,346 at 0.5.4.
Tips
- Never assume a
ComputeBackendmethod that returnsOkactually ran on the GPU. This whole release exists becauseconv2d_forward/attentionreturnedOkon Metal while running on the CPU. If you’re implementing or auditing a backend, add an on-device oracle-comparison test per operation — a passing unit test that never touches the GPU can’t catch this class of bug. - If you’re on skinny GEMM shapes (small M/N, large K) — think embedding projections — upgrade. Split-K is now actually reachable; the exact ArcFace-style shape this was built for went from 2525µs to 144µs per call.
OXICUDA_DISABLE_TILED_CONVis your kill switch if the new tiled implicit-GEMM convolution engine ever produces a suspect result — it restores the pre-0.5.5 scalar dispatch everywhere at once (the engine itself,algo_select, andoxionnx-cuda’spick_engine) for A/B measurement or bisecting.- Pin an RTX A4000/A5000/A6000/3080/3090 explicitly if you were working around wrong Sm86 shared-memory limits. The old code let a tensor-core tile selector request 147,456 bytes against a wrongly-assumed 163,840-byte budget — legal by the old (wrong) number, illegal against the real 101,376-byte opt-in ceiling. That workaround is no longer necessary.
- Reach for
StagingBuffer(upload_with/download_into) for hot-path transfers that repeat the same tensor shape — a video-inference loop moving the same shape host↔device hundreds of times per run is exactly the case it’s built for, and it auto-selects against the driver’s own pageable path below a 512 KiB threshold by default. - Read the “Known issues” section in
CHANGELOG.mdbefore you build a sustained WebGPU workload.oxicuda-webgpu’sgemm/reduce/conv2d_forward/attentiondon’t synchronise per operation by design; a long dispatch loop with no interleavedsynchronize()can abort the whole process, not just return an error.
This is the foundation
OxiCUDA is the GPU layer beneath the rest of the COOLJAPAN ecosystem. Its architecture diagram puts SciRS2, OxiONNX, TrustformeRS, and ToRSh directly on top of it, with OxiBLAS and OxiFFT rounding out the list of projects that lean on this stack — and the oxicuda crate itself is an umbrella re-export over all 74 crates, so a single cargo add oxicuda pulls in whichever subsystem a dependent project needs. The GEMM/conv fixes in this release were found through exactly that kind of downstream pressure: a real inference pipeline (oxiface) hitting a real performance cliff on real hardware.
Repository: https://github.com/cool-japan/oxicuda
Star the repo if you think “the kernel exists in the codebase” and “the kernel actually runs” deserve to be checked as two separate things. Every star tells us to keep auditing.
The era of trusting a green test suite over an adversarial audit is over. Pure Rust GPU computing is here — and as of 0.5.5, its Metal and WebGPU backends have been checked against real hardware, not just compiled.
— KitaSan at COOLJAPAN OÜ August 13, 2026