CubeCL 与 GPU
IR 与融合那一部分讲到融合流 drain、决定执行哪些操作为止。这一部分讲 GPU 上真正执行的那一层:cubecl。它是 tracel 自己写的 GPU 编程框架,单独一个仓库,本卷锁定 burn = "=0.22.1" 解析到的 cubecl 0.11.3;matmul、归约、卷积这些现成内核在另一个仓库 cubek(0.3.1)。burn 这边只有 burn-cubecl(约 2.1 万行)一个 crate 把它接成后端,所有 cubecl 运行时(wgpu、CUDA、ROCm、CPU)共用这一个后端。
cubecl 自己的规模不小:cubecl-core 约 4.0 万行,cubecl-ir 1.8 万,cubecl-runtime 和 cubecl-server 合计 3.2 万,cubecl-macros 1.1 万,cubecl-wgpu 1.1 万。0.11 是一次大改的结果:IR 换成了 pliron,内核参数从 &Array<Line<F>> 改成 &[Vector<F, N>],ComputeClient<R> 擦掉运行时参数、改名 Client。v0.11.0-pre.1(2026-07-28)之前发布的 cubecl 里内核参数还是 &Array<..> 写法,本部分的每个 API 都按 tag 核对。GPU 行为只在 macOS / Metal(M2 Max)上实测,CUDA、ROCm、Vulkan 只读源码。
- #[cube] 内核:
#[cube(launch)]保留原函数做类型检查,另生成expand(构造 pliron IR)、内核描述体和launch;launch在调用线程上只登记参数,IR 在设备线程上按KernelId第一次编译时才构造;用CUBECL_DEBUG_LOG看生成的 MSL;实测 0.11.3 的 MSL 路径上launch和launch_unchecked编出相同的代码。 - 运行时与 Client:
Runtime只剩两个关联类型,Client是到设备线程上dyn Server的通道;create先发句柄后分配,launch不等,读回经暂存缓冲和map_async成为 future;内存池按流分,丢掉张量不还显存,memory_cleanup才还。 - wgpu 与 Metal:burn 的
metalfeature 是 wgpu 运行时加 MSL 编译器(cubecl 的metal = ["wgpu-msl"]),不是原生的cubecl-metal;设备怎么选、内核怎么编译、管线怎么缓存;Vulkan、WebGPU 的差别只读源码。 - autotune:同一个 op 有多个内核变体,第一次遇到某个调优键时实测挑最快的,结果写进当前目录往上最外层
Cargo.toml旁的target/environment/default.db;空缓存的第一次为什么要先跑上万个测速内核。 - burn-cubecl 后端:
CubeBackend和CubeTensor(句柄、形状、strides、dtype、client、设备);一个逐元素 op 怎么实现、matmul 怎么交给 cubek;开fusion时Cube = Fusion<CubeBackend>。
flowchart TB fus["Fusion 流 drain"] --> back["CubeBackend 的 op"] back -->|matmul 等| tune["autotune 选变体"] tune --> cubek["cubek 内核"] back -->|逐元素等| own["burn-cubecl 内核"] cubek --> lau["cube 内核的 launch"] own --> lau lau --> cl["Client"] cl --> srv["设备线程 Server"] srv --> comp["IR 编成 MSL"] srv --> mem["内存池"] srv --> rd["读回 future"]
前两页是 cubecl 本身,不依赖 burn 也成立,实验直接用 burn::cubecl 转出的 cubecl 写内核;第三、四页是这一层里和平台、性能相关的两件事;最后一页回到 burn,看 CubeBackend 怎么把 burn 的 op trait 落到 cubecl 上。在 burn 里写自己的 GPU 内核,也是写一个 #[cube] 函数,再像 burn-cubecl 一样接进后端,见#[cube] 内核和 burn-cubecl 后端。