Rust in Everything
基于 tenferro-runtime 0.8.0

no engine "tenferro-cpu.default.v1" is registered

主线位置:traced 主线的执行一步,Runtime::builder() → register_engine → prepare → run(&mut [&mut engine], ...)。Runtime 里有什么、为什么引擎由调用方持有,见 执行与缓存;CpuEngine 本身见 engine 与 threadteam 池;运算族的执行模块见 扩展算子。

flowchart TB
  prog["CompiledGraph"] --> prep["prepare"]
  prep --> reg{"登记了引擎描述"}
  reg -->|没有| e1["没登记引擎"]
  reg -->|有| fam{"有运算族算子"}
  fam -->|没有| run["run"]
  fam -->|有| mod{"装了执行模块"}
  mod -->|没有| e2["缺执行模块"]
  mod -->|装了| run
  run --> cnt{"engine 个数对"}
  cnt -->|不对| e3["engine 个数不符"]

实验 crate exp-tenferro8/pit2-exp,bin p19_engine,CPU(CpuEngine::serial()),debug,M2 Max、macOS 27、rustc 1.99.0。

现象

两个程序,y = a·a(核心算子)和 z = inv(a)(linalg 的扩展算子),在三种 Runtime 上准备、执行。engine 是调用方自己的 CpuEngine:

let mut engine = CpuEngine::serial();
let (py, pz) = (c.compile(&a.matmul(&a)?)?, c.compile(&a.inv()?)?);

let empty = Runtime::builder().build()?;                       // 1. 什么都没登记
let mut b = Runtime::builder();
b.register_engine(tenferro_cpu::runtime_engine_registration(&engine)?)?;
let cpu_only = b.build()?;                                     // 2. 只登记了 CPU 引擎
// 每个都 rt.prepare(&p, &[], &[engine.target()]) 再 rt.run(&mut [&mut engine], &prepared, &[])
no engine, matmul: prepare Err: Runtime::prepare (Execution): runtime state failure: no engine "tenferro-cpu.default.v1" is registered with this runtime
  Debug: RuntimeState { op: "Runtime::prepare", phase: Execution, message: "no engine \"tenferro-cpu.default.v1\" is registered with this runtime" }
cpu engine, matmul: Ok([17.0, 7.0, 7.0, 10.0])
cpu engine, inv: prepare Err: Runtime::run (Execution): runtime state failure: missing extension family "tenferro-linalg.linalg.v1"
  • Runtime::builder().build() 成功,建图、编译也成功;错误推迟到第一次 prepare。
  • 引擎明明传给了 prepare(engine.target()),run 时也会传;报错说的是另一件事:Runtime 自己没有这个引擎 id 的描述。
  • 只登记了 CPU 引擎时,矩阵乘能跑,inv 报缺 family。报错只给 family id,不说是哪个 crate 的哪个函数能补上。
  • 第二条错是 prepare 返回的,消息却标着 Runtime::run。

准备好之后,run 还会核对传进来的 engine 个数:

run with 0 engines: Err: Runtime::run (Execution): invalid argument engines: the program was prepared for 1 engines, but 0 were passed
run with 2 engines: Err: Runtime::run (Execution): invalid argument engines: the program was prepared for 1 engines, but 2 were passed

出处

0.8.0 的 Runtime 不持有引擎(执行与缓存 的「Runtime:快照加缓存,没有引擎」)。register_engine 只登记一份描述:引擎 id、它能执行什么、上下文类型。prepare 对每个目标先到快照里查这份描述:

        let view = snapshot.engine(target.engine_id()).ok_or_else(|| {
            Error::runtime_state(
                CALLER,
                ErrorPhase::Execution,
                format!(
                    "no engine {:?} is registered with this runtime",
                    target.engine_id().as_str()
                ),
            )
        })?;

查到之后才进入真正的准备:逐个运算找能执行它的提供者,扩展算子要求同一引擎 id 上装了同一 family 的执行模块,找不到就是 PrepareError::MissingExtension。准备阶段的错误统一经过 prepare_error 包装,那里把调用方写死成了 "Runtime::run",所以 prepare 的失败读起来像是 run 的。这是一处措辞上的上游问题,不影响错误类型。

eager 一侧没有这一步:EagerRuntime 第一次见到一种 engine 时自己登记它的描述,linalg、einsum、FFT 的模块也在第一次用到时自动安装(扩展算子 的「接进 traced 和 eager」)。

修法

登记引擎描述,再给每个用到的运算族装执行模块,引擎 id 用 CPU 的:

let mut b = Runtime::builder();
b.register_engine(tenferro_cpu::runtime_engine_registration(&engine)?)?;
b.install_extension_module(tenferro_linalg::extension_module::<CpuEngine>(tenferro_cpu::runtime_engine_id()?)?)?;
let full = b.build()?;
cpu engine + linalg module, inv: Ok([0.2727272727272727, -0.09090909090909091, -0.09090909090909091, 0.36363636363636365])
  • 描述只按类型和 id 登记:同一个 Runtime 可以拿单线程和多线程的 CpuEngine 去跑,两者的 target() 相等(执行与缓存)。run 时传的 engine 个数要和 prepare 时的目标个数一样。
  • 执行模块管「能算」,AdContext 里的规则管「能求导」,两件事分开装;只装了模块、求导时还会碰到 unsupported vjp AD rule。
  • 自定义扩展算子在 eager 下要调 EagerRuntime::install_extension_module(扩展算子)。

怎么确认没中招

  • 建 Runtime 的代码集中到一个函数里,列出所有用到的运算族(grep -E 'TracedTensor(Linalg|Fft)Ext|einsum'),每个都对应一行 install_extension_module。
  • 启动时对一个最小程序(每个运算族一个运算)跑一次 prepare,在真正的计算之前发现缺什么。
  • 看到 Runtime::run 字样的错误先别找 run 的调用:missing extension family 和其他准备阶段的错误也这样标。

小结

  • Runtime::builder().build() 不带任何引擎描述;没有 register_engine 时 prepare 报 no engine "tenferro-cpu.default.v1" is registered with this runtime。
  • 运算族的算子还要装执行模块,否则报 missing extension family "<id>";这条来自 prepare,消息却标着 Runtime::run。
  • run 传的 engine 个数必须等于 prepare 的目标个数。eager 自动登记,不会遇到前两条。

💬 关联讨论

发起讨论
加载中...