本文永久链接https://tonybai.com/2026/09/18/nvidia-cuda-native-rust-pure-rust-ai-stack

大家好,我是Tony Bai。

【导读】

2026 年 9 月,英伟达罕见地在 Rust 语言上“亲自下场”——发布 cuda-oxide 和 cutile-rs 两个项目,第一次让开发者能用 Rust 直接写 GPU 内核,编译到 PTX,不再需要绕道 C/C++。消息一出,Rust 最大的技术社区 r/rust 瞬间炸锅:这是不是意味着“纯 Rust 的 AI 技术栈”终于要来了?评论区的争论比公告本身更精彩——从编译期内存安全,聊到 CUDA 的供应商锁定,最后一路挖到了 GPU 编程里最硬核的“地址空间”问题。

【文章要点】

  • 英伟达发布两个开源项目:cuda-oxide(SIMT 路线,自定义 rustc 编译后端)和 cutile-rs(Tile 路线,纯 stable Rust,无需自定义工具链);
  • 两条路线的共同目标:让 GPU 内核在编译期就能被 Rust 的类型系统“管住”,从根上掐掉内存别名和数据竞争;
  • cutile-rs 已经不是玩具——发布在 crates.io 上,被 HuggingFace 的 Grout 推理引擎和 mistral.rs 实际使用;
  • 但 Rust 社区的反应并不是一边倒地欢呼:CubeCL / Burn 这些“纯 Rust AI 栈”的先行者已经在路上,英伟达这次带来的更多是“官方认证”而非从零到一;
  • 讨论最终落到一个硬核问题上——GPU 的“地址空间”(Address Space)在语言层面几乎无解,这可能才是“纯 Rust AI 栈”迟迟未成的真正原因。


发生了什么:CUDA 第一次原生支持 Rust

先说结论:以前用 Rust 也能碰 CUDA,但那是“绑定”(bindings)——host 端用 Rust,kernel 本体还得是 C/C++ 编译出来的 PTX。这次不一样,kernel 本身也能用 Rust 写,直接编译成 PTX。

英伟达在官方技术博客中给出了两条并行的技术路线,对应 CUDA 本身的两种编程模型:

  • SIMT(Single Instruction, Multiple Threads):也就是大家熟悉的“写一个线程干什么,然后启动几千个线程”的老套路,对标 CUDA C++ 和 numba-cuda。对应项目是 cuda-oxide
  • Tile:一种更新的编程模型(CUDA C++ 和 Python 端已经先有了),描述“一个数据块(tile)要做什么”,线程怎么映射、内存怎么排布,全部交给编译器决定。对应项目是 cutile-rs

官方给出的建议很直接:优先考虑 Tile,因为它把架构相关的细节都交给了编译器;只有当你需要精细控制线程和共享内存时,才下沉到 SIMT。

两条路线到底怎么落地到代码里?下面拆开讲。

两条路线的设计差异

SIMT 路线:cuda-oxide,“编译器级”接管

cuda-oxide 是一个自定义的 rustc 编译后端:它拦截编译过程,把标了 #[kernel] 的函数一路从 Rust MIR、经由社区的 Pliron IR 框架、再到 LLVM IR,最终降到 PTX;其余代码则照常交给标准后端处理。也就是说,host 代码和 device 代码可以写在同一个文件里,一条命令编译,不需要单独的 kernel crate。

这条路线的门槛也最实在:需要 Linux、8.0 及以上计算能力的 GPU、CUDA 工具包(12.x 起)、clang 及其 libclang 头文件,以及一个锁定版本的 nightly 工具链。项目自带的 cargo oxide doctor 会帮你把这些依赖挨个体检一遍。

它的内存安全设计核心是一个叫 DisjointSlice 的类型。普通的 &mut [f32] 在 GPU 场景里根本用不了——几千个线程如果都想要同一个可变引用,Rust 会直接拒绝编译。DisjointSlice 做的事情,就是把这“一个大的可变借用”拆成“每个线程独占一小块”的许可证:每个线程通过自己的索引拿到属于自己的那一个元素,其他线程摸不到。

配合它的还有 #[launch_contract]——在 kernel 上声明“我是几维索引、block 大小是多少”,然后 prepare_vecadd 这类生成的函数会拿实际的启动配置去对这份“契约”做校验,校验通过才发一个“通行证”给真正安全的 launch 方法。没有契约的 kernel,就只能走裸的、unsafe 的启动方式——因为一个裸的启动配置本身,说明不了它到底要启动哪个 kernel。

Tile 路线:cutile-rs,stable Rust 就能跑

cutile-rs 走的是完全不同的哲学:你操作的不是标量,而是一整块 tile。kernel 函数体只会作为“一个逻辑线程”跑一次,背后用多少个真实 GPU 线程去执行,完全由编译器决定。#[cutile::module] 宏会把 kernel 的 AST 嵌入宿主二进制,首次被调用时才通过 CUDA Tile IR 完成 JIT 编译。

门槛比 SIMT 路线轻得多:只需要 8.0 以上算力的 GPU、CUDA 13.3、stable Rust 1.89 及以上,不需要 nightly,也不需要你自己维护一套 LLVM。cutile 已经发布在 crates.io 上,cargo add cutile 就能用。

安全模型这边也不需要专门造一个 DisjointSlice 类型——所有权本身就够用。可变张量要先在 host 端调用 .partition([128]) 这样的方法做“分区”:这一步同时干了三件事——把独占访问权分给每个 tile、把 grid 的启动规模定下来(比如 1024 元素分成 128 一块,正好是 8 个 tile)、并且把 tile 的静态宽度参数一并推导出来。分区之后所有权就转移了,别的地方想再用同一块张量会直接编译不过,这比 cuda-oxide 逐次调用检查更严格——所有权一路跟着张量穿过 launch 边界

整个程序也是惰性的:从造输入张量、调用 kernel,到把结果拷回 host,中间没有一步真正碰了 GPU,直到最后调用 .sync_on(&stream),才是唯一的同步点,之前的一切只是被记录下来的"计划"。

两条路线速览对比

cuda-oxide(SIMT) cutile-rs(Tile)
编程模型 线程级,写一个线程做什么 Tile 级,写一个数据块做什么
工具链要求 锁定版本 nightly Rust + 自带 LLVM stable Rust 1.89+,无需自定义 LLVM
依赖 CUDA 12.x+、clang/libclang CUDA 13.3
安全机制 DisjointSlice + launch_contract,逐次调用校验 所有权 + partition,跨 launch 边界追踪
共享内存 支持,但目前仍需 unsafe 编译器接管,无需你手动管理线程和共享内存
发布状态 早期 alpha 已发布 crates.io,被 Grout、mistral.rs 实际使用
灵活度 更高,可精细控制 更受限,但"安全由构造保证"

一句话总结两者的取舍:Tile 用限制换安全和简单,SIMT 用控制权换灵活性

编译器到底“抓住”了什么

这次发布反复强调的一点是:这两套方案都能在编译期而不是运行期抓到经典的内存别名 bug。GPU 上几千个线程用不保证顺序的方式访问同一块内存,一旦两个线程同时命中同一个地址、其中一个还在写,结果就取决于谁先谁后——这类 bug 出了名地难复现,经常是测试全绿、生产环境炸。

官方给的例子很直白:如果你把 SIMT kernel 的输出缓冲区,同时当作它自己的输入传进去,代码根本编译不过,Rust 借用检查器会直接报“不能既借为可变又借为不可变”。Tile 那边同理,同一个张量既想分区又想原样传入,也会因为所有权已经转移而编译失败。

换句话说,这类 bug 从“跑起来才知道”变成了“写的时候就知道”——这也是这次发布在 Rust 社区能引发热议的根本原因:内存安全叙事第一次真正下沉到了 GPU kernel 这一层

Reddit 的争论:这就是“纯 Rust AI 栈”了吗?

原帖标题就是灵魂拷问:“英伟达都亲自带来原生 CUDA 支持了,现在还有什么能阻止一个纯 Rust 的 AI 技术栈(而不只是绑定)?”

评论区的回应,概括起来是三层递进的泼冷水,一层比一层硬核。

第一层:其实已经有人在做了

高赞回复直接指出,Burn(配合 CubeCL 后端)已经在做这件事,而且做得相当不错,CubeCL 支持多厂商后端;有人补了一句略带调侃的评价——英伟达这次做的事,本质上是把 Rust AST 直接编译到 tile 和 PTX,没有经过中间的 C 代码,但这并不代表它比现有方案更“先进”,只是路线不同。

也有开发者现身说法:用 Burn(CubeCL 后端)跑推理,性能目前还明显落后于 Torch 或 ONNX Runtime,算子覆盖也有限,项目活跃度这一年感觉有点停滞;与此相对,也有人提到 OpenVINO 在 x86-64 CPU 推理上的表现确实能打。

第二层:供应商锁定,还是要不要拥抱

另一条高赞评论态度很鲜明——CUDA 是“终极供应商锁定”,能避开就尽量避开。但马上有人反问:现实中除了 CUDA 和英伟达,还有什么真正能打的替代?

这条线的讨论最后落在了 Vulkan 计算着色器上:理论上它没有供应商锁定,特性也足够全,但在推理这种真正吃 FLOPs 的场景里,Vulkan 目前拿不出接近 cuBLAS/CUTLASS 在 H100 上的性能——Vulkan 更适合消费级硬件,短期内不太可能扛起大规模训练/推理的主力算力。

一位曾在英伟达工作过、长期做可移植深度学习编译的开发者给出了更本质的解释:一个足够好的编译器能补上部分语义差距,但语义本质不同的时候补不了——即便是架构上最接近英伟达的 AMD,写出一个真正 state-of-the-art 的 kernel,中间的性能鸿沟依然很大,而且随着架构多样性增加,这个鸿沟只会越拉越大。OpenCL 在图形领域的没落,就是前车之鉴:客户要的是硬件的极致性能,不是抽象层的整洁。

第三层(最硬核):地址空间才是真正的墙

整个帖子里信息量最大的一条评论,来自一位用户,他直接给出了 TL;DR——问题的核心是地址空间(Address Spaces)

CUDA / 现代 Vulkan 的内存不是连续的。“大道理”是:即便两个指针数值相同,也不代表它们指向同一块内存——常量内存、workgroup 内存、全局内存,彼此是完全不同的地址空间,互不重叠。

Rust 目前没有这个概念,要把它塞进类型系统里,理论上得表达成类似“指针 + 地址空间标签 + 可变性”的组合类型,这在工程上是一个巨大的麻烦。大多数“GPU 相关”的 Rust 项目要么走捷径(一切都当全局内存、所有东西运行时才做多态派发),要么在 rustc 的分支上打一堆补丁,很少有项目真正把这件事做进主线编译器——这也是为什么大部分 GPU 项目最终都长成了“贴在 rustc 上的一堆特殊处理”,而不是原生支持。

这条评论下面的追问和回复,把这个话题继续往下挖:

  • 有人问“为什么不干脆用 64 位地址、拿出高位几个 bit 来标记设备位置”——回复给出的解释是,GPU 上不同地址空间在硬件层面并不是均匀的:global 内存正常寻址,shared 内存只在 SM 内部有效,跨 SM 边界的指针根本不保证有效,kernel 参数还有自己专门的地址空间;GPU 编程语言(OpenGL/CV、CUDA、Vulkan、rust-gpu)几乎都是各自独立的编译器体系,不是常规编译工具链能简单适配的。
  • 另有回复补充:现代编译语言确实允许用“通用”地址空间——本质上是一个 64 位指针,若干 bit 位标记指向哪个内存区域,GPU 也确实有专门的硬件指令支持这种切换,只是比“地址空间特化”的指令慢一点。但要注意,Vulkan 的 SPIR-V 并不提供这种通用地址空间,这也是为什么把通用语言编译到 Vulkan 特别复杂的原因之一(另一个原因是内存不能随意做类型转换,导致联合体和 Rust 的枚举都很难实现);相比之下 OpenCL 对应的 SPIR-V 变体倒是有通用地址空间,反而更好“对付”。

也有评论从更底层的角度补了一刀:C/C++ 标准里本来就留了大量“实现定义”和“未定义行为”的空间,这恰恰是它们能在不同硬件上“各显神通”的原因;而 GPU 上的别名规则、原子操作的 acquire/release 语义,本质上和 CPU 就不是一回事,语言标准想统一这件事,工程量不小。

换句话说:cuda-oxide 和 cutile-rs 目前解决的,是“内存别名/数据竞争”这一类安全问题;地址空间这道题,它们绕开了,而不是解开了——两个项目都还是紧贴 CUDA/英伟达硬件在做,并没有在 Rust 语言层面引入一个通用的、跨厂商的地址空间抽象。

还有一层现实的声音:别等“纯 Rust”了,能用就行

评论区里也有相当务实的反馈。有人现身说法:自己做无人机的机器人系统,用 Rust 版 CUDA 之后,C++ 部分已经被砍掉了大半,剩下的非 Rust 组件是可视化(打算迁到 Bevy)、物理仿真库,以及算法开发阶段还离不开的 Julia(在数据探索和数学计算上,Julia 目前在他的工作流里基本取代了 Python)。

也有开发者直言:现在整套工具链已经摆在那了,只是希望大家别死等一个“纯 Rust”版本才肯用——llama.cpp、mistral.rs 这类项目已经证明,模型推理这一层继续跑在 C++ 上,其他部分用 Rust 并不冲突。还有评论提到,除去玩具模型或者一些很边缘的实现,主流的训练框架目前没有一个把 OpenCL 当作正式的加速器后端,这也从侧面说明了“完全去 CUDA 化”在训练侧目前还只是理论上的选项。

生态坐标:这两个项目现在站在哪

官方的自我定位也很坦诚:两个项目都是早期阶段,都不是生产就绪的。cuda-oxide 处于早期 alpha;cutile-rs 走得更靠前一些,已经发布到 crates.io,并且已经被 HuggingFace 的 Grout 推理引擎和 mistral.rs 在生产之外的场景里实际用了起来。

英伟达也明确说了,Rust 写 GPU 这件事不是他们发明的——rust-gpu、rust-cuda、cudarc 这些项目早就趟过这条路,英伟达自己也在和 rust-cuda 的维护者保持沟通。这次真正“新”的地方,是英伟达把工程资源正式投了进来,并且给出了一条相对清晰的长期路线图——包括未来打算让 CUDA Rust、CUDA C++、CUDA Python 三种前端之间可以互操作,不至于选了 Rust 就被锁死在 Rust 生态里。

小结

这次发布本身的技术含量没什么好怀疑的:把 GPU 内核的内存安全问题第一次真正做进了编译器和类型系统,DisjointSlicelaunch_contractpartition 这几个设计都不是花架子,是实打实解决“几千个线程同时摸一块内存”这个老大难问题。

但 Reddit 那场讨论提醒我们的是,“CUDA 支持 Rust” 和 “Rust 有了一个能打的、厂商中立的 AI 技术栈”是两件事。前者英伟达这次基本做到了;后者要过的坎更多——性能可移植性、供应商锁定的取舍、以及地址空间这种语言层面的硬骨头,都还没有一个让所有人满意的答案。

对国内做 AI 基础设施和系统编程的团队来说,这至少是一个值得关注的信号:Rust 在 GPU 编程这一层的“官方地位”正在被主动巩固,无论最后是走 cuda-oxide/cutile-rs,还是继续押注 CubeCL/Burn 这类厂商中立方案,“用 Rust 写 AI 系统层代码”这件事,正变得越来越现实。


参考资料:


还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 从0 开始构建 Agent Harness 将带你:

  • 抛弃臃肿框架,回归“驾驭工程 (Harness Engineering)”的第一性原理
  • 用 Go 语言手写 ReAct 循环、并发拦截与上下文压缩引擎等,复刻极简OpenClaw
  • 构建坚不可摧的 Safety Middleware 与飞书人工审批防线
  • 在底层实现 Token 成本审计、链路追踪与自动化跑分评估
  • 从“调包侠”进化为掌控大模型边界的“AI 操作系统架构师”

扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。