先定义:Tensor 与 TensorImpl 各自负责什么?
p10::Tensor 是面向用户和算子 API 的轻量句柄,持有 p10::TensorImpl 智能指针。它负责让上层代码传递张量,而不是把所有运行时细节暴露给每个调用点。
TensorImpl 才是运行时状态的拥有者:形状、步长、数据类型、设备、存储和生命周期都在这里汇合。CPUImpl、CUDAImpl 与 EdgeImpl 可以提供不同的具体实现,而不改变 Tensor 的使用方式。
- Tensor:稳定、轻量的调用入口。
- TensorImpl:描述数据在哪里、如何布局以及由谁管理。
- 后端实现:为具体设备提供内存与 kernel 行为。
Dispatcher:把算子请求交给正确的 kernel
add() 或 matmul() 不需要在调用点硬编码 CPU、CUDA 或边缘设备。Dispatcher 根据输入张量的设备、数据类型和算子签名构造 DispatchKey,再从注册表选择匹配的 kernel。
这条路径把“要做什么”和“在哪个设备上怎样做”分开。新增后端时,维护者可以集中实现存储、注册表和 kernel 适配,而不必复制一套上层张量 API。
p10::Tensor y = p10::matmul(a, b)
-> read a.device(), a.dtype(), b.device(), b.dtype()
-> build DispatchKey
-> lookup the registered kernel
-> execute on the selected backend从接口到硬件:一次调用的可读路径
调度不是一个需要猜测的黑盒。阅读一条算子调用时,可以依次检查输入 Tensor 的 TensorImpl、DispatchKey 的组成、注册表命中的函数以及 kernel 读写的内存布局。
这对调试尤其重要:如果结果错误,可以先判断是形状或步长不对、DispatchKey 选错、后端实现不完整,还是 kernel 本身的数值行为有问题。问题会落在一个可测试的边界内。
后端适配为什么可以独立演进?
P10 把设备抽象、算子注册和具体实现拆开后,新增硬件主要影响后端层。上层模块仍然调用同一组 Tensor 算子,TPX 也可以继续复用自动微分规则。
这种设计的优势不是承诺所有设备都更快,而是让硬件实验更容易做对照:同一输入、同一算子和同一输出契约可以在不同后端上重复运行,再比较 kernel、内存和误差。
- 接口稳定:上层不必知道每个设备的存储细节。
- 实现可替换:后端 kernel 可以独立注册、测试和迭代。
- 结果可验证:设备差异可以被收敛到输出、性能与误差对照。
如何验证 Dispatcher,而不是只看最终结果?
验证调度时,建议同时观察数值结果与路径信息。只比较最终张量,可能无法发现程序错误地落到了默认设备或低效 kernel。
- 固定输入的形状、数据类型与设备,记录 DispatchKey。
- 在 CPU 与 CUDA 上执行相同算子,比较输出误差与边界行为。
- 用非连续张量或不同 stride 覆盖内存布局分支。
- 为新注册的 kernel 增加算子级测试,而不是只依赖网络级回归。

