Rust in Everything
基于 burn 0.22.1

源码地图

主线位置:三条主线经过的代码分布在哪些 crate 里。算子主线从 burn-tensor 出发,经 burn-dispatch 分到 burn-autodiff、burn-fusion,落到 burn-flex 或 burn-cubecl,再下到 cubecl 和 cubek;求导主线几乎全在 burn-autodiff 里;训练主线在 burn-core、burn-nn、burn-optim、burn-train 里,只在算张量时进入算子主线。

flowchart TB
  app["应用 crate"] --> burn["burn 再导出"]
  burn --> core["burn-core 模块 记录 数据"]
  burn --> nn["burn-nn / optim / train / store"]
  nn --> core
  core --> tensor["burn-tensor 张量 API"]
  tensor --> dispatch["burn-dispatch 全局后端"]
  dispatch --> ad["burn-autodiff 装饰器"]
  dispatch --> flex["burn-flex CPU"]
  dispatch --> cubeb["burn-cubecl GPU"]
  cubeb --> fusion["burn-fusion 装饰器"]
  cubeb --> cfus["burn-cubecl-fusion"]
  cubeb --> cubek["cubek 内核库"]
  cubek --> cubecl["cubecl 语言与运行时"]
  ad --> backend["burn-backend 后端接口"]
  flex --> backend
  fusion --> ir["burn-ir"]
  backend --> std["burn-std"]

行数是 git show <tag>:<path> | wc -l 数的 .rs 文件行数(含注释和空行,不含 tests/ 目录,但含 src/ 里的 tests.rs),只用来比较各块的大小。burn 工作区按 Cargo.toml 的 members = ["crates/*", ...] 有 38 个 crate,另有 examples 和 xtask;下面按层分组。

用户 API:burn、burn-core、burn-tensor 和上面几层

crate行数入口做什么讲它的页
burn328src/lib.rs只做再导出:pub use burn_core::*,再按 feature 挂上 nn、optim、train、store、vision 等模块;crate 文档里有完整的 feature 列表怎么读这一卷
burn-tensor21 957src/tensor/api/base.rs(3 927 行)、bridge/、device.rs(1 675 行)Tensor<D, K> 和全部张量方法、Device 门面、把调用转进 Dispatch 的 BridgeTensor02 张量
burn-core9 147src/module/base.rs 的 Module trait、module/param/、config.rs、store/、data/模块、参数、Config、记录、数据加载器;后端 crate 和 Device 构造函数的 feature 也在这里汇总Module 派生、Config
burn-derive3 199src/lib.rs 的 module_derive、config#[derive(Module)]、#[derive(Config)] 等过程宏Module 派生
burn-nn25 840src/modules/、loss/Linear、卷积、归一化、注意力等层,各种损失函数nn 模块
burn-optim9 929src/optim/base.rs 的 Optimizer、module/module_optimizer.rs、grads.rs优化器(Adam、SGD、Muon 等)、GradientsParams、学习率调度优化器
burn-train30 237src/learner/Learner 主循环、指标、事件、checkpoint、TUILearner 与数据
burn-store、burn-pack、pytorch-reader10 001、2 461、6 150burn-store/src/、burn-pack/src/lib.rs、pytorch-reader/src/lib.rs模型存取:burnpack 二进制格式、safetensors、PyTorch .pt 读入(pytorch-reader 不依赖 PyTorch 也不依赖 burn)存储与加载
burn-dataset7 989src/数据集与变换(下载器要联网,本卷不用)Learner 与数据
burn-einsum1 113src/lib.rseinsum! 的方程解析,和后端无关检查与报错

burn-train 无条件依赖 burn-flex(burn-flex = { workspace = true, features = ["default"] }),但在 tag 下 git grep burn_flex crates/burn-train 找不到任何引用:开了 train 就会编译 Flex,不管用不用它。

分派:burn-dispatch 和 burn-backend-extension

crate行数入口做什么讲它的页
burn-dispatch6 528build.rs、src/backend.rs(1 192 行)的 Dispatch、device.rs、tensor.rs、ops/唯一的全局后端:DispatchDevice / DispatchTensor 每个后端一个变体,每个 op 按设备 match;跨后端搬运Dispatch
burn-backend-extension4 246src/catalog.rs 的 BACKENDS、dispatch.rs过程宏 crate:#[backend_dispatch] 为 Dispatch 的每个 op 生成 match;#[backend_extension] 让下游 crate(burn-linalg、burn-vision 等)加自己的 opDispatch

burn-dispatch 是整个依赖图的腰:上面的 crate 只认识 Dispatch 这一个后端,下面每个后端 crate 都是它的可选依赖。0.21 里它已经存在(#4508,首次发布于 v0.21.0-pre.2),那时 Tensor 还带后端类型参数,Dispatch 只是可选的一种后端;metal、vulkan、webgpu 三个 wgpu 后端在它里面互斥,同时打开时构建脚本打印「Only one WGPU backend can be enabled at once」并把三个都关掉。0.22 里七种 cubecl 运行时合成一个 Cube 变体,见怎么读这一卷的「burn-dispatch 的三个 cfg」。

后端接口:burn-backend、burn-std、burn-ir

crate行数入口做什么讲它的页
burn-backend13 429src/backend/base.rs 的 BackendTypes、Backend;backend/ops/tensor.rs(1 964 行)等后端要实现的全部 trait:FloatTensorOps、IntTensorOps、ModuleOps……;很多 op 有用别的 op 拼出来的默认实现Backend trait
burn-std9 442src/和后端无关的公共类型:TensorData、DType、Shape、Bytes、运行期配置Tensor 与 Kind
burn-ir8 311src/operation.rs 的 OperationIr、tensor.rs 的 TensorIr每个 op 的可序列化描述;Fusion、Router、Remote、Capture 都靠它burn-ir

装饰器:包在别的后端外面

crate行数入口做什么讲它的页
burn-autodiff11 378src/backend.rs 的 Autodiff<B, C>、ops/tensor.rs(4 830 行)、runtime/、grads.rs套在任何后端外面记录计算图、做反向传播04 自动微分
burn-fusion25 173src/backend.rs 的 Fusion<B>、client.rs、server.rs、stream/、search/不立刻执行,把 op 记成 burn-ir 送进流,攒够了再决定怎么融合Fusion 装饰器、融合搜索
burn-router9 152src/backend.rs 的 BackendRouter<R>把 op 记成 IR,经一个 RouterChannel 送到别处执行;burn-remote 和 burn-capture 的底座一句带过
burn-remote、burn-communication9 132、1 136burn-remote/src/lib.rs远程设备:客户端发 op、服务端在自己的设备上跑(默认 Iroh 传输,另有 WebSocket,服务端用 axum)一句带过
burn-capture1 191src/lib.rs只记录不执行的「后端」,用于导出计算图一句带过

Autodiff<B, C> 和 Fusion<B> 是真正的类型层装饰器:类型参数就是被包的后端,Backend::name 也一层套一层(autodiff<fusion<cubecl<...>>>)。BackendRouter<R> 的参数是一个通道而不是后端,它不包某个后端,而是把 op 送给通道另一头的解释器。

实现:burn-flex、burn-cubecl 和下面的 cubecl、cubek

crate行数入口做什么讲它的页
burn-flex37 033src/backend.rs 的 Flex、ops/(conv.rs 3 056 行、reduce.rs 2 860 行)、ops/matmul.rs纯 Rust 的 eager CPU 后端,matmul 交给 gemm 0.19,SIMD 用 macerator,并行用 rayon;支持 no_std 和 wasmFlex:CPU 后端
burn-cubecl20 684src/backend.rs 的 CubeBackend、lib.rs 的 Cube、ops/、kernel/所有 cubecl 运行时共用的一个后端;开 fusion 时 Cube = Fusion<CubeBackend>;matmul、reduce、卷积交给 cubekburn-cubecl 后端
burn-cubecl-fusion16 660src/engine/、optim/burn-fusion 决定哪些 op 一组,它为这一组生成并启动融合内核生成融合内核
burn-wgpu、burn-cuda、burn-rocm、burn-cpu253、58、26、61src/lib.rs只是门面:给 burn-cubecl 打开某个 cubecl 运行时的 featurewgpu 与 Metal
burn-ndarray、burn-tch13 015、4 434src/lib.rs 顶部的 #![deprecated]弃用:ndarray 0.17 的 CPU 后端、LibTorch(tch 0.22)绑定Flex:CPU 后端

cubecl 和 cubek 是两个单独的仓库。在 Metal 这条路上用到的部分(cubecl 0.11.3、cubek 0.3.1):

crate行数做什么讲它的页
cubecl-macros、cubecl-core11 170、40 475#[cube] 宏把 Rust 函数体展开成「构造 IR 的代码」;前端类型(缓冲区参数 &[T] / &Tensor<T>、向量化的 Vector<F, N>、comptime)#[cube] 内核
cubecl-ir、cubecl-opt18 351、8 800内核 IR 和优化 pass#[cube] 内核
cubecl-runtime、cubecl-server16 845、15 506Client、内核提交、autotune(tune/);内存池、流、编译管线运行时与 Client、autotune
cubecl-wgpu、cubecl-cpp11 252、10 804wgpu 运行时;cubecl-cpp 的 metal 方言把 IR 编成 MSL(cubecl 的 metal = ["wgpu-msl"])wgpu 与 Metal
cubecl-environment、cubecl-common11 150、9 194跨 std / no_std / wasm 的同步原语、block_on、配置、持久缓存(turso 数据库);设备服务线程运行时与 Client
cubecl-std11 480cube 标准库,含测设备吞吐的内核(autotune 的上界从这里来)autotune
cubek-matmul、cubek-tile37 106、32 252matmul 内核族和它们共用的 tile 抽象burn-cubecl 后端
cubek-reduce、cubek-attention、cubek-convolution 等9 924、8 876、8 208 …归约、注意力、卷积、池化、随机数、量化一句带过

cubecl 还有 cubecl-cuda(4 274)、cubecl-hip(3 020)、cubecl-cpu(3 335)+ cubecl-llvm(13 449)、cubecl-spirv(4 890)、原生的 cubecl-metal(2 545)这些运行时和编译器,本卷只读源码;burn 的 metal feature 用不到 cubecl-metal。

领域扩展

burn-vision(9 350)、burn-linalg(2 991)、burn-signal(1 741)、burn-rl(1 235)是同名 feature 打开的领域算子和强化学习工具,本卷一句带过。burn-nn 依赖 burn-linalg,所以后者总会被编译。测试用的 burn-backend-tests(6 242)、burn-no-std-tests(460)不讲。

依赖树有多大

同一个空 main、不同 feature,用 cargo tree -e normal 数去重后的包(锁文件相同):

features = ["flex"]                                   157 个包,其中 cubecl/cubek 3 个
features = ["metal"]                                  368 个包,其中 cubecl/cubek 26 个
features = ["flex", "metal"]                          396 个包,其中 cubecl/cubek 26 个
default-features = false, features = ["flex"]         112 个包
📝NOTE
没有蠢问题

问: 只开 flex,为什么依赖树里还有 cubecl?

答: 那 3 个是 cubecl-common、cubecl-environment、cubecl-zspace,来自 burn-std 的直接依赖,不是 GPU 运行时:burn-std 用它们提供跨 std / no_std / wasm 的同步原语、block_on、流 id 和配置。cubecl-core、cubecl-runtime、cubek 只在打开某个 cubecl 运行时 feature 时才进来。代价是即便只用 CPU,TensorData、Shape 这些基础类型也和 cubecl 的版本绑在一起(burn 0.22.1 的工作区写的是 cubecl-common = "0.11.1")。

小结

  • 五层:用户 API(burn 只再导出,burn-tensor 是张量 API,burn-core 是模块和记录)→ 分派(burn-dispatch,整个图的腰)→ 后端接口(burn-backend、burn-std、burn-ir)→ 装饰器(burn-autodiff、burn-fusion,以及基于 IR 转发的 burn-router / burn-remote / burn-capture)→ 实现(burn-flex、burn-cubecl 加 cubecl、cubek)。
  • 最大的几个 crate:cubecl-core 4.0 万行、burn-flex 3.7 万、cubek-matmul 3.7 万、burn-train 3.0 万、burn-nn 2.6 万、burn-fusion 2.5 万。
  • burn-wgpu 这些「后端 crate」在 0.22 只剩几十到两百多行,真正的后端是 burn-cubecl 一个,运行时由设备值决定。
  • 只开 flex 拉进 157 个包,开 metal 是 368 个;burn-train 不管用不用都会编译 burn-flex。

💬 关联讨论

发起讨论
加载中...