ONNX 后端、执行提供者与推理串行化
1. 为何同时依赖 onnxruntime-node 与 onnxruntime-web
backends/onnx.js 顶部注释说明:理想情况是按环境 动态 import,但历史上有 bundler 与 top-level await 限制,因此 静态同时 import,构建时再 tree-shake(Node 包不打进浏览器 bundle)。
运行时选择:
- Node:
onnxruntime-node; - 浏览器:
onnxruntime-web/webgpu路径(与 WebGPU 能力绑定)。
另:若全局存在 Symbol.for('onnxruntime'),则优先使用宿主注入的 ORT(便于嵌入已加载 ORT 的运行时)。
/**
* @file Handler file for choosing the correct version of ONNX Runtime, based on the environment.
* Ideally, we could import the `onnxruntime-web` and `onnxruntime-node` packages only when needed,
* but dynamic imports don't seem to work with the current webpack version and/or configuration.
* This is possibly due to the experimental nature of top-level await statements.
* So, we just import both packages, and use the appropriate one based on the environment:
* - When running in node, we use `onnxruntime-node`.
* - When running in the browser, we use `onnxruntime-web` (`onnxruntime-node` is not bundled).2. 设备 → Execution Provider
deviceToExecutionProviders 将逻辑设备(wasm、webgpu、cuda、webnn-npu 等)映射到 ORT 的 EP 配置;auto / gpu 会展开为 候选列表(按优先级尝试)。
浏览器默认 defaultDevices = ['wasm'];若检测到 WebGPU/WebNN,则把对应设备列入 supportedDevices 供选择。
3. Web 端 Session 创建串行:webInitChain
注释写明:当前不支持在 WASM/WebGPU 下并行加载多个 session,因此用 Promise 链 串行化 InferenceSession.create,否则易出现加载阶段竞态。
/**
* Currently, Transformers.js doesn't support simultaneous loading of sessions in WASM/WebGPU.
* For this reason, we need to chain the loading calls.
* @type {Promise<any>}
*/
let webInitChain = Promise.resolve();
...
export async function createInferenceSession(buffer_or_path, session_options, session_config) {
await ensureWasmLoaded();
const logSeverityLevel = getOnnxLogSeverityLevel(env.logLevel ?? LogLevel.WARNING);
const load = () =>
InferenceSession.create(buffer_or_path, {
logSeverityLevel,
...session_options,
});
const session = await (apis.IS_WEB_ENV ? (webInitChain = webInitChain.then(load)) : load());
session.config = session_config;
return session;
}4. Web 端推理串行:webInferenceChain
更关键的是 run 阶段:runInferenceSession 在 Web 环境把 session.run 也串到同一链上,注释直接点出典型错误:Error: Session already started。
/**
* Currently, Transformers.js doesn't support simultaneous execution of sessions in WASM/WebGPU.
* For this reason, we need to chain the inference calls (otherwise we get "Error: Session already started").
* @type {Promise<any>}
*/
let webInferenceChain = Promise.resolve();
export async function runInferenceSession(session, ortFeed) {
const run = () => session.run(ortFeed);
return apis.IS_WEB_ENV ? (webInferenceChain = webInferenceChain.then(run)) : run();
}设计权衡(可简述为):
- 正确性/兼容性优先:ORT Web 在部分环境下对并发 run 敏感,全局串行是最稳妥策略;
- 代价:多标签页或多模型并行推理时 无法真正并行,吞吐量受限于单链;若需并行,通常要 Worker 隔离 或 多 ORT 实例(需自行验证 EP 支持)。
5. WASM 预加载与 Deno 特例
ensureWasmLoaded 在启用 env.useWasmCache 且配置了 wasm/mjs 路径时,预取二进制与 factory;Deno web 运行时若关闭 cache 会 直接抛错(避免错误走 Node API 分支)。
6. 日志级别映射
getOnnxLogSeverityLevel 将库的 LogLevel 映射到 ORT 的 0–4,并刻意 合并部分级别,因为 ORT 在 session 创建时日志极多,避免淹没用户。
7. 与 session.js 的关系
models/session.js 的 getSession / constructSessions 负责:
- 调用
selectDevice/selectDtype; getCoreModelFile+getModelDataFiles(external data);- WebGPU 下可选
preferredOutputLocation: gpu-buffer以优化 KV cache(见 设备与量化、生成)。
小结:读 Transformers.js 的 ORT 集成,应同时记住 EP 选择 与 Web 双链(init + inference),这是线上「偶现 Session already started」类问题的根源与官方缓解手段。
下一篇:设备、dtype 与量化选择