cuda-oxide:开篇

用 Rust 直接写 CUDA kernel,编译成 PTX 跑在 GPU 上——cuda-oxide 是什么、为什么值得研究、这个系列要讲什么。

📚 系列 cuda-oxide · 第 1 篇

写 GPU kernel 一直是 C++ 的地盘。cuda-oxide 把这件事换成 Rust——给 rustc 装一个特殊的代码生成后端,让 #[kernel] 标记的 Rust 函数直接编译成 PTX,host 部分继续走正常 LLVM 路径。本系列拆它的内部架构。

1. cuda-oxide 是什么

一句话:一个 rustc 编译器后端,把带 #[kernel] 标记的 Rust 函数编译成 NVIDIA PTX

具体一点:

#[kernel]
pub unsafe fn add_one(out: *mut i32) {
    *out = 42;            // ← 这函数最终在 GPU 上跑
}

fn main() {
    // host 代码,在 CPU 上跑
    let ctx = CudaContext::new(0).unwrap();
    let buf = DeviceBuffer::<i32>::zeroed(&stream, 1).unwrap();
    module.add_one(&stream, config, buf.ptr());
    // ...
}

同一个 .rs 文件里,kernel 和 host 代码共存:

  • #[kernel] 函数走 cuda-oxide 路径 → PTX → CUDA driver JIT → 在 GPU 上跑
  • 其它代码走 rustc 自带 LLVM 后端 → x86_64 二进制 → 在 CPU 上跑

NVlabs 开源,定位是研究 / 教学级别的 toolchain,不追求跟 CUDA C++ 完全对等的性能,但够你拿来玩 PTX、写自己的 CUDA kernel、研究 GPU 编译器。

仓库:https://github.com/NVlabs/cuda-oxide

2. 为什么要用 Rust 写 CUDA

C++ 写 GPU kernel 已经存在三十年,Rust 进场的理由不能光靠”我喜欢 Rust”。几个有说服力的角度:

角度C++ kernelRust kernel
类型安全模板地狱 + 隐式转换trait + 显式类型
内存安全手动指针,容易越界借用检查 + unsafe 显式标
工具链nvcc + CMakecargo + rustc
host/device 共享代码头文件 + __host__ __device__crate + cfg
错误处理error code 或 exceptionResult + ?
生态cuBLAS / cuDNN(C++)cublas-rs、cudarc 等(刚起步)

实话:Rust GPU 生态还远远没法跟 CUDA C++ 比。但作为研究方向,它打开了几个新可能:

  • AI 编译器(Triton、IREE)开始用 Rust 写中间层,GPU 后端越来越重要
  • 写 cuBLAS-rs / cuDNN-rs 这种纯 Rust 高性能库,需要直接生成 PTX 的能力
  • rustc 的类型系统 + 借用检查 + zero-cost abstraction 在 GPU kernel 上是有意义的

cuda-oxide 不是终点,但它是Rust 直接编译到 GPU 这件事可行的证据

3. cuda-oxide 的设计哲学

读懂 cuda-oxide 之前先建立三个心智模型——后面所有细节都是这三点的展开。

3.1 不重写 rustc,只插一个后端

rustc 有几十万行代码处理 Rust 语法、类型推断、借用检查、MIR 生成。cuda-oxide 完全不碰这些——它通过 -Z codegen-backend 这个 nightly flag 给 rustc 挂一个动态库后端,只在 codegen 阶段介入。

(-Z codegen-backend 的细节见 compiler 系列第二篇。)

3.2 Composition over Inheritance:host 路径委托给 LLVM 后端

我们的 backend 不替换整个 LLVM 后端——它包装一个 LlvmCodegenBackend 实例,绝大多数 trait 方法直接转发,只在 codegen_crate 这一处分叉:有 #[kernel] 走 cuda-oxide 管线,host 部分继续委托 LLVM 编。

这是 host + device “双轨制”的关键。详见 03 用组合代替继承

3.3 Progressive Lowering:7 层 IR 一站一站降

device 路径不是 MIR 直接喷 PTX,而是经过 7 层 IR:

MIR  →  dialect-mir + dialect-nvvm  →  dialect-llvm  →  LLVM IR  →  PTX

每一层比上一层更接近硬件、表达力更弱、约束更明确。这种”渐进式下降”是 MLIR / pliron 设计哲学的核心,也是 AI 编译器普遍采用的架构。

4. 本系列要讲什么

整个系列围绕两件事展开:

4.1 hello-constant 全流程拆解(主线)

从一条命令 cargo oxide run hello_constant 开始,把整条编译 + 执行链路切成 11 步,逐站讲透:

[04] 导读
[05] Step 01:鸟瞰整个管线
[06] Step 02:cargo-oxide driver 进程做了什么
[07] Step 03:rustc 前端把源码变成 MIR
[..] Step 04..11:codegen_crate / collector / mir-importer / mir-lower / llvm-export / llc / 运行时

读完这条主线,你能从源码的 *out = 42 追到 GPU 上真正执行那条 SASS 指令。

4.2 单独主题(穿插在主线之间)

不属于”hello-constant 11 步”的小专题,但能加深对 cuda-oxide 的理解:

  • [02] 确认 codegen backend 符号导出(用 nm 验证 .so)
  • [03] 用组合代替继承——一个真实例子
  • [08] rustc backend dylib 是怎么编出来的
  • (后续会有 WMMA wrapper、PTX 学习、tiled-gemm 拆解等)

编号规则:不管是主线”拆解”还是单独主题,统一按发布顺序往下排。读者从标题就能区分:带”hello-constant 拆解 XX”的是主线,其它的是单独主题。

5. 怎么跟这个系列

5.1 推荐顺序

  1. 先看本篇(开篇),理解 cuda-oxide 的定位
  2. 0203 建立”backend 是动态库 + composition”的基本图景
  3. 04 导读 进入 hello-constant 主线,按 05、06、07 依次走

5.2 配套实战

每篇博客都建议自己跑一遍 cuda-oxide 对照看。一条命令开齐所有 dump:

RUST_LOG=rustc_codegen_cuda=debug,mir_importer=debug,mir_lower=debug,info \
CUDA_OXIDE_VERBOSE=1 \
CUDA_OXIDE_DUMP_MIR=1 \
CUDA_OXIDE_DUMP_LLVM=1 \
CUDA_OXIDE_SHOW_RUSTC_MIR=1 \
cargo oxide run hello_constant 2>&1 | tee /tmp/run.log

输出包括 rustc MIR、dialect-mir(各阶段)、dialect-llvm、.ll.ptx,对照博客一步步看,比单看文章效果好十倍

5.3 预备知识

不必精通,知道大致是什么就够,每篇会按需展开:

  • rustc 基本架构(MIR / codegen 阶段)
  • LLVM IR(基本指令、basic block、SSA)
  • MLIR 概念(dialect、op、interface,pliron 跟它同构)
  • CUDA 基础(thread / warp / block / grid)
  • PTX(NVIDIA 虚拟 ISA,介于 LLVM IR 和 SASS 之间)

6. 一句话总结

cuda-oxide 给 rustc 装了一个动态库后端,让带 #[kernel] 的 Rust 函数直接编译成 PTX 跑在 GPU 上。本系列围绕一个最简单的 hello-constant example,把从源码到 GPU 输出 42 的整条编译 + 执行链路拆成 11 步逐站讲透,中间穿插单独主题——读完你能从 Rust 源码追到任意一条 PTX 指令的来源。

下一篇:确认 codegen backend 符号导出——用 nm 验证 backend 动态库的入口符号是否正确导出,迈出 cuda-oxide 工程入门的第一步。

评论区
评论功能即将上线, 敬请期待。