cuda-oxide: 一款实验性的 Rust-to-CUDA 编译器
摘要
cuda-oxide 是 NVIDIA 发布的一款实验性 Rust-to-CUDA 编译器后端,支持纯 Rust GPU 内核开发,无需外部语言绑定。
查看缓存全文
缓存时间: 2026/05/08 09:31
NVlabs/cuda-oxide 源码:https://github.com/NVlabs/cuda-oxide
cuda-oxide
cuda-oxide 是一个用于在纯 Rust 中编译 GPU 内核的自定义 rustc 后端。该工作空间包含:
- 单源编译 — 主机代码和设备代码位于同一文件,通过一条
cargo oxide build命令构建 - 一个 rustc 代码生成后端,将
#[kernel]函数编译为 CUDA PTX - 设备端抽象(类型安全索引、共享内存、作用域原子操作、屏障、TMA、warp/cluster 操作)
- 用于内存管理和内核启动的主机端运行时(
cuda-core、cuda-async) - 基于 Pliron(https://github.com/vaivaswatha/pliron)的 Rust 原生编译流水线 —— 一个 Rust 中的类 MLIR IR 框架(Rust → Rust MIR → Pliron IR → LLVM IR → PTX)
项目状态
cuda-oxide 是一个实验性编译器,展示了如何以纯 Rust 原生编写 CUDA SIMT 内核 —— 无需 DSL,无需外部语言绑定 —— 并将其提供给更广泛的 Rust 社区。
该项目处于早期阶段(alpha),正在积极开发中:您可能会遇到 bug、功能不完整和 API 破坏性变更,因为我们正在努力改进它。尽管如此,我们希望您能在自己的工作中尝试它,并通过分享使用体验来帮助塑造其发展方向。如果您有兴趣为该项目做出贡献,请参阅 CONTRIBUTING.md。
快速开始
use cuda_device::{kernel, thread, DisjointSlice};
use cuda_core::{CudaContext, DeviceBuffer, LaunchConfig};
use cuda_host::{cuda_launch, load_kernel_module};
// 设备端:泛型内核,对每个元素应用任意函数。
// F 可以是带捕获的闭包 —— rustc 将其单态化为具体类型。
#[kernel]
pub fn map<F: Fn(T) -> T, T: Copy>(f: F, input: &[T], mut out: DisjointSlice<T>) {
let idx = thread::index_1d();
if let Some(out_elem) = out.get_mut(idx) {
*out_elem = f(input[idx.get()]);
}
}
fn main() {
let ctx = CudaContext::new(0).unwrap();
let stream = ctx.default_stream();
let data: Vec<f32> = (0..1024).map(|i| i as f32).collect();
let input = DeviceBuffer::from_host(&stream, &data).unwrap();
let mut output = DeviceBuffer::<f32>::zeroed(&stream, 1024).unwrap();
let module = load_kernel_module(&ctx, "host_closure").unwrap();
// 使用闭包启动 —— factor 被自动捕获并传递到 GPU
let factor = 2.5f32;
cuda_launch! {
kernel: map::<_, f32>,
stream: stream,
module: module,
config: LaunchConfig::for_num_elems(1024),
args: [move |x: f32| x * factor, slice(input), slice_mut(output)]
}.unwrap();
let result = output.to_host_vec(&stream).unwrap();
assert!((result[1] - 2.5).abs() < 1e-5);
}
上述示例定义了一个泛型 #[kernel] 函数 map,它接受任意 Fn(T) -> T 闭包。在主机端,CudaContext 和 DeviceBuffer 管理 GPU 上下文和内存,cuda_launch! 将内核分派到 GPU。闭包 move |x| x * factor 被捕获、标量化,并作为 PTX 内核参数自动传递。PTX 在单次 cargo build 调用中与主机二进制文件一起生成。
对于可组合的异步 GPU 工作,相同的启动点看起来几乎相同:stream: 消失,cuda_launch_async! 返回一个惰性 DeviceOperation,执行在调用 .sync() 或 .await 时发生。
use cuda_async::device_operation::DeviceOperation;
use cuda_host::cuda_launch_async;
// 假设 `module`、`input` 和 `output` 来自 cuda-async 设置:
let factor = 2.5f32;
cuda_launch_async! {
kernel: map::<_, f32>,
module: module,
config: LaunchConfig::for_num_elems(1024),
args: [move |x: f32| x * factor, slice(input), slice_mut(output)]
}
.sync()?; // 或:.await?;
有关完整的异步设置,请参阅 async_mlp 示例和 crates/cuda-async/README.md。
# 构建并运行示例
cargo oxide run host_closure
# 显示完整编译流水线(Rust MIR → dialect-mir → mem2reg → dialect-llvm → LLVM IR → PTX)
cargo oxide pipeline vecadd
# 使用 cuda-gdb 调试
cargo oxide debug vecadd --tui
环境配置
前置要求
- cargo-oxide —— 驱动构建流水线的 cargo 子命令(
cargo oxide run、build、debug等) - Rust nightly,含
rust-src和rustc-dev组件(在rust-toolchain.toml中固定) - CUDA Toolkit(12.x+)
- LLVM 21+,含 NVPTX 后端(
llc必须在 PATH 中) - Clang + libclang 开发头文件(
clang-21/libclang-common-21-dev)—— 构建主机cuda-bindingscrate 时bindgen需要 - Linux(在 Ubuntu 24.04 上测试)
为什么需要 LLVM 21? 我们生成的 TMA / tcgen05 / WGMMA 内联函数,LLVM 20 及更早版本的
llc无法处理。简单内核可能仍可在旧版llc上运行,但任何 Hopper / Blackwell 相关功能都需要 21+。
安装
cargo-oxide
在 cuda-oxide 仓库内,cargo oxide 通过工作空间别名即可直接使用。要在仓库外使用(您自己的项目):
cargo install --git https://github.com/NVlabs/cuda-oxide.git cargo-oxide
首次运行时,cargo-oxide 将自动获取并构建代码生成后端。
Rust
# 工具链通过 rust-toolchain.toml 自动安装
# 如需手动安装:
rustup toolchain install nightly-2026-04-03
rustup component add rust-src rustc-dev --toolchain nightly-2026-04-03
CUDA
export PATH="/usr/local/cuda/bin:$PATH"
nvcc --version
LLVM
# Ubuntu/Debian
sudo apt install llvm-21
# 验证 NVPTX 支持
llc-21 --version | grep nvptx
流水线会自动在 PATH 中查找 llc-22 和 llc-21(按此顺序)。要固定特定二进制文件,请设置 CUDA_OXIDE_LLC=/usr/bin/llc-21。
Clang(主机 cuda-bindings)
主机 cuda-bindings crate 运行 bindgen,它会加载 libclang 并需要 clang 自身的 resource-dir stddef.h —— 仅 libclang1-* 运行时是不够的。
sudo apt install clang-21 # 或 libclang-common-21-dev
cargo oxide doctor 会提前检测此问题;否则症状是在主机构建期间出现晦涩的 'stddef.h' file not found 错误。
验证安装
# 检查所有前置条件是否就绪
cargo oxide doctor
# 端到端构建并运行示例
cargo oxide run vecadd
cargo oxide doctor 会验证您的 Rust 工具链、CUDA 工具包、LLVM 和代码生成后端。如果一切配置正确,cargo oxide run vecadd 会将 Rust 内核编译为 PTX,在 GPU 上启动它,并打印 ✓ SUCCESS: All 1024 elements correct!。
示例
crates/rustc-codegen-cuda/examples/ 中有 46 个示例。亮点:
| 示例 | 描述 |
|---|---|
vecadd | 向量加法 —— 经典的入门示例 |
host_closure | 从主机传递闭包的泛型内核 |
generic | 带单态化的泛型内核(scale) |
gemm_sol | GEMM SoL:868 TFLOPS(B200 上达到 cuBLAS 的 58%),4 个阶段共 8 个内核 |
tcgen05 | Blackwell 张量核心(sm_100a):TMEM、MMA、cta_group::2 |
atomics | GPU 原子操作:6 种类型 × 3 种作用域 × 5 种顺序(20 个测试) |
cluster | 线程块簇 + DSMEM 环形交换(Hopper+) |
async_mlp | 异步 MLP 流水线:GEMM → MatVec → ReLU,跨并发流执行 |
mathdx_ffi_test | cuFFTDx 线程级 FFT + cuBLASDx 块级 GEMM |
async_vecadd | 使用 cuda-async 和 DeviceOperation 的异步 GPU 执行 |
cross_crate_kernel | 定义内核的库 crate,打包到二进制文件中 |
cargo oxide run vecadd
cargo oxide run gemm_sol
Crate 概览
面向用户的 Crate
| Crate | 描述 |
|---|---|
cuda-device | 设备内联函数(thread::*、warp::*、屏障) |
cuda-host | 主机工具(cuda_launch!、cuda_launch_async!、ltoir 辅助函数) |
cuda-macros | 过程宏(#[kernel]、#[device]、gpu_printf!) |
cuda-bindings | cuda.h 的原始 bindgen FFI 绑定 |
cuda-core | 安全的 RAII 包装器(CudaContext、CudaStream、DeviceBuffer) |
cuda-async | 异步执行层(DeviceOperation、DeviceFuture、DeviceBox) |
libnvvm-sys | libNVVM 的 dlopen 绑定(由 cuda-host::ltoir 使用) |
nvjitlink-sys | nvJitLink 的 dlopen 绑定(由 cuda-host::ltoir 使用) |
编译器 Crate
| Crate | 描述 |
|---|---|
rustc-codegen-cuda | 自定义 rustc 后端 |
mir-importer | Rust MIR -> dialect-mir 转换 + 流水线 |
mir-lower | dialect-mir -> dialect-llvm 降级 |
dialect-mir | 建模 Rust MIR 的 pliron 方言 |
dialect-llvm | 建模 LLVM IR 的 pliron 方言(+ 导出到 .ll) |
dialect-nvvm | 建模 NVVM 内联函数的 pliron 方言 |
构建工具
| Crate | 描述 |
|---|---|
cargo-oxide | Cargo 子命令(cargo oxide run 等) |
文档
| 目录 | 描述 |
|---|---|
cuda-oxide-book | 项目书籍(Sphinx + MyST)—— 指南、编译器内部原理、API 参考 |
状态
亮点:
- 端到端 Rust -> PTX 编译
- 统一的单源编译(主机 + 设备在同一文件中)
- 带单态化的泛型函数
- 带捕获的闭包(move 和非 move,通过 HMM)
- 用户自定义结构体、枚举、模式匹配
- 完整的 GPU 内联函数支持(线程、warp、共享内存、屏障、TMA、簇、原子操作)
- 跨 crate 内核
- Blackwell+ 的 LTOIR 生成(设备端 LTO)
- 设备 FFI:通过 LTOIR 实现 Rust <-> C++/CCCL 互操作
- MathDx 集成:cuFFTDx 线程级 FFT、cuBLASDx 块级 GEMM
- 主机运行时:
cuda-core(显式控制)和cuda-async(可组合异步操作) - GEMM SoL:在 B200 上达到 868 TFLOPS(cuBLAS SoL 的 58%),使用 cta_group::2、CLC、4 阶段流水线
文档
进行中: 🚧 cuda-oxide book (https://nvlabs.github.io/cuda-oxide/) 是该项目的主要参考。它涵盖了 Rust 中的 SIMT 内核编写、同步和异步 GPU 编程、编译器架构等内容。
要在本地构建并提供书籍服务,请参阅 cuda-oxide-book/README.md。
生态
cuda-oxide 是众多正在积极开发中的 Rust + GPU 项目之一。该领域的项目解决不同部分的问题 —— 用于图形的 Vulkan/SPIR-V、通过 LLVM 的隐式卸载、第三方 CUDA 后端、安全驱动绑定 —— 我们一直在与更广泛的 Rust GPU 社区维护者合作,探讨如何共同推动 Rust 中的 GPU 计算发展。
关于 cuda-oxide 相对于其他项目的定位,请参阅书籍的 Ecosystem 附录(https://nvlabs.github.io/cuda-oxide/appendix/ecosystem.html)。
许可证
cuda-bindings crate 基于 NVIDIA 软件许可证授权:LICENSE-NVIDIA。
所有其他 crate 基于 Apache License, Version 2.0 授权:LICENSE-APACHE。
相似文章
CUDA-oxide:NVIDIA 官方 Rust 转 CUDA 编译器
CUDA-oxide 是由 NVIDIA 开发的实验性 Rust 转 CUDA 编译器,支持使用地道的 Rust 编写安全的 GPU 核函数,可直接编译为 PTX,无需借助领域特定语言或外部绑定。
cuda-oxide 手册
cuda-oxide 是一个实验性的 Rust 到 CUDA 编译器,允许开发者编写安全、符合 Rust 惯用法的 GPU 内核,并直接编译为 PTX。
@npashi: 终于可以谈谈过去6个月我在@nvidia一直埋头做的事了。我们刚刚开源了cuda-oxide——一个实验性…
NVIDIA 已开源 cuda-oxide,这是一个实验性的 rustc 后端,允许开发者直接用纯 Rust 编写 CUDA 内核,无需 DSL、FFI 或源码到源码的转换。
Show HN: cuTile Rust:在Rust中编写安全、无数据竞争的GPU内核
NVIDIA Labs发布了cuTile Rust,这是一个基于瓦片的系统,用于用地道的Rust编写内存安全、无数据竞争的GPU内核。它将Rust的所有权模型扩展到GPU内核,通过JIT将Rust的AST编译为GPU代码,并实现接近原生CUDA的性能。
Rust 中的 GPU 卸载:可移植、安全且快速
本文介绍了一种零开销、多厂商的 GPU 编译框架,该框架内置于 Rust 编译器中,利用 Rust 的所有权模型确保内存安全,并实现与原生 CUDA 和 HIP 基准相媲美的性能。