深色背景中发散亮光的光纤束

从「能调用」到「能写内核」

在 Rust 里调用 GPU 早就不是问题,问题在内核本身还得用别的语言写。9 月 8 日 NVIDIA 发布 CUDA Rust,把这一段补上了:Rust 直接编译到 PTX,不再只是包一层别人的代码。这不是孤例——Nova Linux 驱动用 Rust 写,Dynamo 的核心是 Rust,NVTX 也提供了 Rust 绑定。

两条轨道:SIMT 与 Tile

第一条是 SIMT,对应你熟悉的 CUDA C++ 写法:描述一个线程做什么,然后启动成千上万个线程。对应的项目叫 cuda-oxide,它是一个自定义的 rustc 代码生成后端,把内核函数经 Rust 中间表示、Pliron IR、LLVM 一路降到 PTX。代价是需要 Linux、算力 8.0 以上的显卡、CUDA 12.x 以上、clang,以及一个被钉住版本的 nightly 工具链,目前仍是早期 alpha。

第二条是 Tile,也是 NVIDIA 建议优先选的那条:你描述一块数据做什么,由 Tile IR 编译器决定它怎么映射到具体架构。对应的项目 cutile-rs 跑在稳定版 Rust 1.89+ 与 CUDA 13.3 上,不需要自定义 LLVM,已经发布到 crates.io,并被 HuggingFace 的推理引擎 Grout 和 mistral.rs 用上了。

安全不是口号,是类型

写 GPU 内核最怕内存错误:越界、别名、竞态。cuda-oxide 的做法是用 DisjointSlice 把一次可变借用切成每线程独占的片段,再加启动契约,把启动参数的合法性在编译期和启动前检查掉;cutile-rs 则用张量分区与所有权保证独占访问。换句话说,一类过去靠加班和反复评审才能挡住的错误,现在被类型系统挡住了。

契约式启动,把授权写进类型里

另一个细节是启动参数的检查方式。内核用属性声明自己的契约:按几维索引、每块多少线程。宿主侧构造启动配置时,运行库会拿它和声明、以及当前设备的实际上限做校验,通过之后才返回一个凭据,而安全的启动方法必须拿着这个凭据才能调用。没有声明契约的内核,只暴露原始的 unsafe 启动入口——因为一个裸的启动配置,说明不了它到底在启动什么。

这种设计不新鲜,但它解释了为什么系统层的代码先搬去了 Rust:推理引擎、服务框架、驱动、Agent 运行时这几层变化极快,而它们又最容易出内存这类难以复现的问题。能在编译期拦住的错误,就不必等到线上才发现。

现在该不该跟进

如果手上是成熟稳定的 CUDA C++ 内核,重写没有收益,别动。新项目,尤其是推理与算子层的胶水代码,可以从 Tile 轨道开始试:先让它承担「不用关心架构差异」的部分,遇到必须手动控制的场景再降到 SIMT。NVIDIA 也承诺了与 CUDA C++、CUDA Python 的互操作,这意味着选哪条轨道都不会把你锁死在生态里。

语言迁移的真正价值,从来不是写起来更优雅,而是把错误从运行时的凌晨三点,挪到编译时的下午三点。