用 Rust 写 CUDA 核函数,NVIDIA 终于把这事做成正式方向了
在 GPU 编程这个领域,C++ 几乎是不可撼动的霸主,虽然 Python 封装的库很多,但底层还是得靠 C++ 顶着。现在 NVIDIA 明确要把 CUDA Rust 推进到 2027 年以后,这事儿其实挺有意思。对于习惯了内存安全、讨厌处理野指针的开发者来说,这确实是个好消息,但冷静下来看,这究竟是提升了开发效率,还是给开发者增加了学习成本?
目前 CUDA Rust 走的其实是两条截然不同的路线,这决定了你以后怎么写核函数。
第一条路是基于现有 CUDA C++ 工具链的封装,这种方式更像是给 C++ 穿了一层 Rust 的衣服。它的核心逻辑是利用 Rust 的 FFI(外部函数接口)去调用已经写好的 CUDA 库,或者通过某种绑定机制让 Rust 代码能编译成 PTX(并行线程执行)。这种方式的优势在于兼容性极强,如果你手里有大量存量的 .cu 文件,可以通过这种方式在 Rust 项目里快速集成。但缺点也很明显,你依然在处理 C++ 的逻辑,Rust 所谓的“内存安全”在跨语言调用时其实是被削弱的,你还是得小心那些 unsafe 块。
第二条路则是真正的原生 Rust GPU 编程。这意味着 NVIDIA 试图让 Rust 直接接管核函数的编写,让编译器在生成二进制代码之前就通过 Rust 的所有权(Ownership)和借用(Borrowing)检查机制,在编译阶段就把潜在的显存溢出或竞态条件给拦截掉。如果这条路能跑通,那将是革命性的,因为 GPU 编程最痛苦的就是调试那些莫名其妙的内存崩溃。
为了让大家有个直观感受,我对比了一下目前用 Rust 尝试写简单核函数的逻辑(伪代码):
// 假设在一个支持 CUDA Rust 的环境下编写简单的向量加法
#[cuda_kernel]
fn vector_add(a: *const f32, b: *const f32, c: *mut f32, n: i32) {
let i = thread_idx(); // 获取当前线程索引
if i < n {
unsafe {
// 尽管在核函数内部依然需要 unsafe,
// 但 Rust 的类型系统能更好地管理这些指针的生命周期
*c.add(i as usize) = *a.add(i as usize) + *b.add(i as usize);
}
}
}
虽然上面的代码看起来还是有 unsafe,但对比 C++ 的 __global__ 函数,Rust 的模块化管理和包管理(Cargo)会让整个项目的工程化变得极其舒服。
不过,我依然持保留意见。CUDA C++ 这么多年积累的生态太恐怖了,从 cuDNN 到 cuBLAS,所有的高性能算子都是用 C++ 调优到极致的。Rust 即使在语言特性上胜出,但在“性能榨干”这件事上,能不能在 2027 年之前追上 C++ 的极致优化?这是一个巨大的问号。而且,GPU 编程本身就是极其底层的,很多时候我们需要的就是那种“不安全”的直接内存操作来换取几个周期的性能提升,强行套用 Rust 的安全检查,会不会反而成了性能的枷锁?
另外,对于大多数开发者来说,现在的路径是:Python → PyTorch/Triton → CUDA C++。现在 NVIDIA 强推 CUDA Rust,是想在中间插一脚,还是想取代 C++?我觉得大概率是想在推理引擎(Inference Engines)和驱动层提供一种更现代的替代方案。毕竟现在的 AI 基础设施,从 Serving 到 Runtime,对稳定性和并发要求极高,Rust 的强项正好在这儿。
总的来说,如果你是追求极致性能的 CUDA 架构师,C++ 依然是你的首选;但如果你是构建 AI 系统底层框架的工程师,习惯了 Rust 的工程化能力,那么关注 CUDA Rust 的进展是有意义的。我更期待看到的是它如何处理 GPU 上的异步内存管理,如果能把 async/await 优雅地引入到 GPU 核函数调度中,那才真的有点东西。

终于不用在 C-CUDA 桥接里死磕内存管理了,这波更新救命!
终于不用在内存溢出边缘反复横跳了,这波原生支持简直救命!