CUDA-oxide 开源实测:NVIDIA 官方用 Rust 写 CUDA 内核
Published: 2026-05-12 Reading: 7 min AI / GPU / Rust
2026 年 5 月 11 日 NVIDIA Labs 在 GitHub 上开源了 CUDA-oxide — 一个实验性的 Rust-to-CUDA 编译器。这不是又一个 DSL 也不是 Rust 绑定的 CUDA 库,而是一个真正的 rustc 代码生成后端,让你用纯 Rust 写 GPU 内核,编译出来就是 PTX(NVIDIA GPU 的底层指令集),直接跑在 CUDA 上。
消息一出两小时内登上 Hacker News 首页拿到 130+ 分,社区反应非常热烈。对 Rust 社区来说这是里程碑式的一步 — 从此 GPU 编程可以和 Rust 的类型系统、所有权模型、无惧并发的承诺直接挂钩了。
CUDA-oxide 到底是什么
很多人看到"Rust-to-CUDA"第一反应是"哦,又一个 Rust bindings for CUDA"。完全不对。
CUDA-oxide 的工作原理是这样的:
- 你写一个纯 Rust 函数加上
#[kernel]属性 - 整个模块包在
#[cuda_module]里 - CUDA-oxide 的自定义 rustc codegen 后端把这个 Rust 函数编译成 PTX
- Host 端通过
cuda-corecrate 加载并启动内核 - 所有代码在同一个文件里 — 真正的 single-source 编译
关键区别:你没有写一行 C/C++,没有调 nvcc,没有 FFI 绑定。Rust 代码直接变成了 GPU 指令。
编译管线:用 Rust 自己搓了一套 MLIR
CUDA-oxide 的编译管线很有意思。它没有直接走 LLVM 就完事了,而是用了 Pliron — 一个用 Rust 实现的 MLIR 风格的中间表示框架。完整路径是:
Rust 源码 → Rust MIR → Pliron IR → LLVM IR → PTX
中间那层 Pliron 起到了类似 MLIR 的多级降级作用,让编译器可以做各种 GPU 特定的优化(内存合并、共享内存分配、warp 级操作)。
有意思的是这也意味着 CUDA-oxide 理论上可以输出到其他后端。不过在现阶段 NVIDIA 的目标很明确:先把 CUDA 生态做好。
上手体验:写一个向量加法
来看代码。这是 CUDA-oxide 的基本写法:
use cuda_device::{cuda_module, kernel, thread, DisjointSlice};
use cuda_core::{CudaContext, DeviceBuffer, LaunchConfig};
#[cuda_module]
mod kernels {
use super::*;
#[kernel]
fn vecadd(a: &[f32], b: &[f32], mut c: DisjointSlice<f32>) {
let idx = thread::index_1d();
let i = idx.get();
if let Some(c_elem) = c.get_mut(idx) {
*c_elem = a[i] + b[i];
}
}
}
fn main() {
let ctx = CudaContext::new(0).unwrap();
let stream = ctx.default_stream();
let module = kernels::load(&ctx).unwrap();
// ...
module
.vecadd(&stream, LaunchConfig::for_num_elems(1024),
&a, &b, &mut c)
.unwrap();
}
如果你是 Rust 开发者,这段代码读起来应该非常自然。注意几个设计细节:
DisjointSlice— 用于输出缓冲区,确保不会出现多个线程同时写同一个位置的问题thread::index_1d()— 类型安全的线程索引#[cuda_module]自动把 device artifact 嵌入到 host 二进制中- 模块名
kernels::load()的调用签名是类型安全的 — 参数数量、类型不匹配会在编译时报错
最高光的功能:闭包也能当内核用
CUDA-oxide 支持泛型内核和闭包捕获,这是 C++ CUDA 里非常麻烦的事情:
#[kernel]
pub fn map<T: Copy, F: Fn(T) -> T + Copy>(
f: F, input: &[T], mut out: DisjointSlice<T>
) { /* ... */ }
let factor = 2.5f32;
module
.map::<f32, _>(
&stream,
LaunchConfig::for_num_elems(1024),
move |x: f32| x * factor,
&input, &mut output,
)
.unwrap();
闭包的捕获变量(这里 factor)会被自动标量化并作为内核参数传递。Rust 的泛型单态化在这里帮了大忙 — 每个闭包都是一个独特的类型,编译器可以完美优化。
异步 GPU 编程:DeviceOperation 组合
CUDA-oxide 的 cuda-async crate 提供了懒加载的异步操作图:
use cuda_async::device_operation::DeviceOperation;
module
.map_async::<f32, _>(
LaunchConfig::for_num_elems(1024),
move |x: f32| x * factor,
&input, &mut output,
)?
.sync()?;
// 或者 .await?
多个 DeviceOperation 可以组合成图,跨 stream pool 调度,最后通过 .await 异步等结果。这对需要频繁启动小内核的训练或推理场景非常实用。
安全模型:GPU 内核的 safe Rust 有多远
CUDA-oxide 的安全模型是项目最值得关注的部分。Rust 的所有权模型在 CPU 上保证了内存安全,但 GPU 有它的特殊性:
- 多个线程共享内存 — 需要显式同步(barrier、fence)
- 缓冲区别名 —
DisjointSlice类型防止写写冲突 - Shared memory 分配 — 必须在编译期确定大小或通过动态分配
- Warp 级操作(shuffle、reduce)— 需要特定的属性标注
目前 CUDA-oxide 还在 alpha 阶段,安全模型在 官方文档中有详细讨论。NVIDIA 团队明确说"安全是一等目标",但也承认 GPU 有"细微之处"。
安装和环境要求
目前只支持 Linux(Ubuntu 24.04 已验证),而且要求比较多:
- Rust nightly(toolchain 已 pin 在 2026-04-03)
- CUDA Toolkit 12.x+
- LLVM 21+ 带 NVPTX 后端 — 因为用了 TMA/tcgen05/WGMMA 这些 Hopper/Blackwell 指令
- Clang 21(用于 bindgen)
安装方法:
cargo install --git https://github.com/NVlabs/cuda-oxide.git cargo-oxide
cargo oxide doctor # 检查环境
cargo oxide run vecadd # 跑第一个例子
项目提供了 46 个示例从 vecadd 到矩阵乘法到 MLP 推理,覆盖了大部分常用模式。
社区怎么看
HN 上的讨论集中在几个方面:
- 对 cudarc 的替代性 — 很多人之前用
cudarccrate 做 Rust-CUDA FFI,觉得 CUDA-oxide 可能是"几乎 drop-in 的替代" - 编译器性能 — 不用 nvcc 和 cmake 意味着构建时间可能大幅改善
- 闭源担忧 — 有评论指出最终还是跑在 NVIDIA 的闭源驱动上,对开源纯粹主义者来说没有本质改变
- LLM 代码生成 — 有人讨论 Rust + CUDA 对 AI 代码生成的适配性,强类型 + 编译器约束让 LLM 生成的质量更高
也有人指出了一个关键局限:CUDA-oxide 仍然依赖 NVIDIA 闭源的 CUDA 运行时和驱动。Mojo 虽然画了开源的饼但 1.0 连 Windows 都不支持。从"实用"角度 CUDA-oxide 是当下最现实的 Rust GPU 方案。
总结
CUDA-oxide v0.1.0 是 alpha 质量 — 有 bug、功能不全、API 会变。但它是 NVIDIA 第一次官方出手把 Rust 纳入 CUDA 生态的第一步,方向和执行力都是对的。
如果你是 Rust 开发者想玩 GPU 编程,现在就可以上手体验。如果你在写 AI 推理库或高性能计算框架,这个项目值得关注 — 未来某个版本可能就 production-ready 了。
项目地址:github.com/NVlabs/cuda-oxide