后端与分派
张量那一部分把调用一路送到 Dispatch 门口。这一部分讲门后:一个后端要实现什么,Dispatch 这个唯一的全局后端怎么把调用分给 Flex、Cube(所有 GPU 运行时)或 Autodiff,以及 CPU 上那一支 Flex 怎么真正把数算出来。
代码在三个 crate:burn-backend(trait 定义,约 1.3 万行,多半是文档注释)、burn-dispatch 加上生成分派代码的过程宏 crate burn-backend-extension(合计约 1.1 万行)、burn-flex(约 3.7 万行,本部分只读存储、逐元素、归约、矩阵乘)。
- Backend trait:
BackendTypes是设备加四种张量原语,Backend是九个 op trait 的合集;197 个方法必须实现,239 个有用别的 op 拼出来的默认实现,各后端按自己的理由挑着覆盖;DeviceOps::defaults决定默认 dtype;读数据返回 future,因为 GPU 和 wasm 不能原地等。 - Dispatch:
Dispatch是空结构体,后端信息在每个DispatchTensor的变体上;变体由build.rs算出的 cfg 裁剪,一个后端都不开时只剩不可构造的NoBackend;#[backend_dispatch]给每个 op 生成一段match,跨后端搬运经主机中转;实测分派层让 Flex 上一个小 op 多 19 到 40 纳秒。 - Flex:CPU 后端:
Arc<Bytes>加带符号步长的Layout,视图类 op 不复制;逐元素运算优先原地写,f32 走 macerator SIMD,矩阵乘交给 gemm,大张量才用 rayon;和弃用的 NdArray 比矩阵乘。
flowchart TB bridge["BridgeTensor"] --> disp["Dispatch"] disp -->|"kind 是 Flex"| flex["Flex"] disp -->|"kind 是 Cube"| cube["Fusion CubeBackend"] disp -->|"带 autodiff"| ad["Autodiff B C"] ad --> flex ad --> cube trait["burn-backend 的 op trait"] -.->|实现| flex trait -.->|实现| cube trait -.->|实现| ad trait -.->|实现| disp
第一页是契约,后两页是契约的两个实现者:一个只转发,一个真算。Cube 那一支先进 burn-fusion 攒操作,见 IR 与融合;再进 cubecl,见 burn-cubecl 后端。Autodiff 那一支在自动微分。
💬 关联讨论
发起讨论加载中...