
近日,VectorWare 团队在 2026 年 2 月发布的技术博文中宣布,他们成功在 GPU 上运行了 Rust 的 async/await 特性。这一突破旨在解决传统 GPU 编程中的并发困境,详情可参考原始博文。
传统 GPU 编程主要依赖于数据并行,即所有线程对不同的数据执行相同的操作。但随着 GPU 程序日益复杂,开发者开始采用 warp specialization(warp 特化),让不同的 warp 执行不同的任务,例如一个 warp 负责加载数据,另一个负责计算。这本质上是从数据并行转向了任务并行。
然而,问题在于:这种并发和同步完全依赖手动管理,缺乏语言或运行时层面的支持,就像在 CPU 上手写线程同步一样,容易出错且难以推理。
VectorWare 的博客文章梳理了三种现有的高层抽象方案:
- JAX 将 GPU 程序建模为计算图,编译器通过分析图中的依赖关系来决定执行顺序和并行策略。
- Triton 使用 "block" 作为独立计算单元,通过 MLIR 多层编译管线来管理并发。
- CUDA Tile 引入了 "tile" 作为一等公民数据单元,使数据依赖变得显式。
但这些方案都有一个共同缺点:它们要求开发者以全新的方式组织代码,需要新的编程范式和生态,对采用构成显著障碍。此外,代码复用困难,现有的 CPU 库和 GPU 库都无法直接与这些框架组合。
文章的核心论点是,Rust 的 Future trait 恰好满足了所需的所有特性:
- Future 是延迟的、可组合的值。类似于 JAX 的计算图,你可以先构建程序的“描述”,再执行,编译器可以在执行前分析依赖关系。
- Future 天然表达独立的并发单元。类似于 Triton 的 block,多个 future 可以串行(通过 .await 链)或并行(使用 join! 等组合子)执行。
- Rust 的所有权系统让数据依赖显式化。类似于 CUDA Tile 的显式 tile,future 通过捕获数据来编码数据流向,而 Send、Sync、Pin 等 trait bound 则约束了数据如何在并发单元间共享和传递。
- 最关键的一点:Warp 特化本质上就是手写的任务状态机,而 Rust 的 future 恰好编译成由编译器自动生成和管理的状态机。既然 future 只是状态机,没有理由不能在 GPU 上运行。
为了实现在 GPU 上运行 async/await,VectorWare 团队移植了 Embassy,这是一个为嵌入式系统设计的 no_std 异步执行器。GPU 没有操作系统,不支持 Rust 标准库,这与嵌入式环境非常相似,因此 Embassy 成为天然的选择。将 Embassy 适配到 GPU 上只需要很少的修改,这种复用现有开源库的能力远优于其他非 Rust 的 GPU 生态。
表面上,这篇文章在讨论 async/await,但它实际上指向一个更大的愿景:利用 Rust 的类型系统和零成本抽象来统一 CPU 和 GPU 的编程模型。Future 不关心自己运行在哪里——线程、核心、block 或 warp 都可以。同一段 async 代码可以不加修改地在 CPU 和 GPU 上运行。
这与 JAX/Triton 那种“为 GPU 写一套全新的东西”的思路根本不同,是一种从语言层面自底向上的统一。VectorWare 此前还发布过关于在 GPU 上启用 Rust std 的文章,结合这次的 async/await 突破,他们的目标很明确:让 GPU 编程变成“普通的 Rust 编程”,而不是一个需要全新心智模型的特殊领域。
更多关于 Rust 和 GPU 编程的深度讨论,欢迎访问云栈社区,这里汇聚了众多开发者的实践经验和技术分享。
|