โก nuvec
MLX for Rust, written in Rust. Lazy arrays, composable transforms and Metal kernels for Apple Silicon, with no bindings to MLX or any C/C++ library.
Documentation ยท Getting started ยท Benchmarks
nuvec brings MLX's programming model to Rust: arrays that are evaluated lazily on unified memory, grad/vjp/jvp/vmap/compile that compose freely, and the Metal kernels that make MLX fast, ported and tested against MLX itself. On top sits an LLM runtime that loads mlx-community checkpoints and generates as fast as mlx-lm.
๐ง Pre-alpha. The API can still change between releases. Everything listed below works today and is covered by tests.
โจ Highlights
- ๐ฆ Pure Rust. No C++ build, no FFI to MLX. Metal is reached through Apple's system frameworks (
objc2-metal);unsafestays in the Metal runtime (and one CPU matrix-product call), and every other crate forbids it. - ๐ค Lazy arrays on unified memory. Operations build a graph; nothing runs until you read a result. The CPU and GPU share memory, so there are no copies between them.
- ๐ Composable transforms.
grad,value_and_grad,vjp,jvp,vmap,custom_function,checkpointandcompilenest in any order, through complex numbers too. - ๐ฅ MLX's Metal kernels. GEMM and GEMV, quantized products (1โ8 bit), fused attention, RMSNorm, RoPE, sort, scan, mixture-of-experts gather kernels, plus your own kernels from source (
fast::MetalKernel). - ๐งช Tested against MLX. Nearly a thousand cases replay results recorded from real MLX, with random layouts, gradients and every dtype.
- ๐ค LLM runtime. Llama, Mistral and Qwen 2/3/3.5, dense, hybrid (Gated DeltaNet) or mixture-of-experts, straight from mlx-community or Hugging Face checkpoints; speculative decoding included.
- ๐พ Zero-copy weights. safetensors and npy files are memory-mapped and handed to the GPU without a copy.
๐ Performance
Same machine, same day, same checkpoints: nuvec-lm against mlx-lm 0.32.2 (Apple M4 Pro, greedy decoding, medians of four runs; ratios above 1 favour nuvec).
| Model | Kind | Prefill (961 tokens) | Decode |
|---|---|---|---|
| Qwen3-0.6B 4-bit | dense | 1.03ร | 1.03โ1.04ร |
| Llama-3.2-3B 4-bit | dense | 1.01ร | 0.98โ0.99ร |
| Qwen3-8B 4-bit | dense | 1.00ร | 0.97โ1.00ร |
| Qwen3.5-9B 4-bit | hybrid | 0.99ร | 0.97โ1.00ร |
| Qwen3-30B-A3B 4-bit | MoE | 0.99ร | 1.02ร |
| Qwen3.6-35B-A3B 4-bit | hybrid MoE | 0.96ร | 1.00โ1.01ร |
Speculative decoding with prompt-lookup drafts doubles decode speed on text that repeats its input (Qwen3-8B: 53 โ 106 tok/s), at about the same speed otherwise. Full tables and methodology: benchmarks.
๐ Quick start
Arrays are lazy, and gradients are arrays too:
use ;
Generate text with an mlx-community checkpoint:
use ;
Or from the command line (the model must be in the local Hugging Face cache, e.g. hf download mlx-community/Qwen3-8B-4bit):
๐ฆ Crates
| Crate | What it does |
|---|---|
nuvec |
The facade: arrays, transforms, fast kernels, random, I/O and the GPU runtime in one import |
nuvec-core |
Lazy graph, primitives, dtypes, evaluation, derivative and batching rules |
nuvec-compile |
compile: tracing, simplification, element-wise kernel fusion |
nuvec-gpu |
Metal runtime: pipelines, buffers, hazard-tracked command encoding, allocator, wired memory, profiler |
nuvec-kernels |
Metal kernel sources and the precompiled nuvec.metallib |
nuvec-cpu |
Pure-Rust CPU kernels |
nuvec-io |
safetensors, npy and IDX; memory-mapped loading |
nuvec-nn |
Modules, layers (Linear, quantized, LoRA, norms, RoPE), losses, optimizers (SGD, Adam, AdamW, Lion, Muon) |
nuvec-lm |
LLM runtime: models, tokenizer, KV cache, sampling, speculative decoding |
nuvec-distributed |
Reserved for distributed collectives (no functional API yet) |
๐ฅ๏ธ Platforms
| ๐ macOS 15+ on Apple Silicon | The primary target, with the full Metal backend |
| ๐ง Linux | The core and CPU crates build and run; the GPU crates compile as stubs |
๐ ๏ธ Building from source
| Tool | Install |
|---|---|
Rust (pinned in rust-toolchain.toml) |
rustup installs it automatically |
| Metal toolchain | xcodebuild -downloadComponent MetalToolchain |
| just | brew install just |
| cargo-deny, Miri, jq | cargo install cargo-deny --locked, rustup +nightly component add miri, brew install jq |
&&
Without the Metal toolchain the build still succeeds: the shader sources are embedded and compiled at runtime (NUVEC_METALLIB=off selects this mode explicitly).
๐งช Against MLX
Python is not needed to build or test: the fixtures recorded from MLX are checked in. With uv installed you can re-record them and compare speed on your own machine:
| Command | Compares |
|---|---|
just bench-parity |
nuvec-lm vs mlx-lm over a roster of cached models (the table above) |
just bench-gemm / bench-quant / bench-fast |
matrix products, quantized products, fused kernels |
just bench-compile, just bench-load, just mnist |
compiled functions, checkpoint loading, MNIST training |
just fixtures, lm-fixtures, nn-fixtures, io-fixtures |
re-record the reference results |
NUVEC_PROFILE=1 prints the GPU time per kernel of any run (see profiling).
๐ License
Licensed under either of Apache License, Version 2.0 or MIT license, at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this work, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Ported MLX kernels keep their MIT attribution. nuvec is not affiliated with Apple.