Change-Id: Ibca337dd7a7f37ee4857c85fccd917b47aa3c9d5 Reviewed-on: https://chromium-review.googlesource.com/c/external/github.com/WebKit/JetStream/+/7232297 Reviewed-by: Marja Hölttä <marja@chromium.org>
119 lines
4 KiB
JavaScript
119 lines
4 KiB
JavaScript
// Copyright 2025 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// Polyfills that Transformers.js / the ONNX runtime needs in JavaScript shells.
|
|
|
|
class URL {
|
|
href;
|
|
constructor(url, base) {
|
|
// DEBUG
|
|
// console.log('URL', url, base);
|
|
this.href = url;
|
|
}
|
|
}
|
|
globalThis.URL = URL;
|
|
|
|
// Polyfill fetch for shell-compatibility and to cache / preload model weights etc.
|
|
let preload = { /* Initialized in init() below due to async. */ };
|
|
|
|
async function redirectingFetch(url) {
|
|
// DEBUG
|
|
// console.log('fetch', url);
|
|
|
|
if (url.startsWith("./"))
|
|
url = JetStream.resources[url];
|
|
|
|
// Redirect some paths to cached/preloaded resources.
|
|
if (preload[url]) {
|
|
return {
|
|
ok: true,
|
|
status: 200,
|
|
arrayBuffer() { return preload[url]; },
|
|
async blob() {
|
|
return {
|
|
size: preload[url].byteLength,
|
|
async arrayBuffer() { return preload[url]; }
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
throw new Error(`Unexpected resource requested in benchmark: ${url}`);
|
|
};
|
|
|
|
// JetStream benchmark harness. Reuse for two different Transformers.js tasks.
|
|
// Assumes `preloadFiles(module)`, `initPipeline(pipelineFromTransformersJs)`,
|
|
// and `doTask(initializedPipeline, inputArrayBuffer)` is in the global scope.
|
|
|
|
class Benchmark {
|
|
transformersJsModule;
|
|
wasmBinary;
|
|
pipeline;
|
|
inputFile;
|
|
output;
|
|
|
|
async init() {
|
|
this.transformersJsModule = await JetStream.dynamicImport(JetStream.preload.transformersJsModule);
|
|
this.wasmBinary = await JetStream.getBinary(JetStream.preload.onnxWasmBinary);
|
|
|
|
for (const url of Object.values(JetStream.preload)) {
|
|
preload[url] = await JetStream.getBinary(url);
|
|
}
|
|
|
|
if ('inputFile' in JetStream.preload) {
|
|
this.inputFile = (await JetStream.getBinary(JetStream.preload.inputFile)).buffer;
|
|
// DEBUG
|
|
// console.log('inputFile', this.inputFile.byteLength, 'bytes');
|
|
}
|
|
|
|
// After we have loaded everything close the door behind us to make sure no other network requests happen.
|
|
globalThis.fetch = redirectingFetch;
|
|
}
|
|
|
|
async runIteration() {
|
|
// Initialize the inference pipeline in the first iteration.
|
|
if (!this.pipeline) {
|
|
// TODO: Profile startup only: What is taking so much time here?
|
|
let { env, pipeline } = this.transformersJsModule;
|
|
|
|
env.allowRemoteModels = false;
|
|
env.allowLocalModels = true;
|
|
env.localModelPath = './transformersjs/build/models/';
|
|
|
|
// Always select the Wasm backend, nothing else.
|
|
delete env.backends.onnx.webgl;
|
|
delete env.backends.onnx.webgpu;
|
|
|
|
// Single-threaded only for now, since we cannot spawn workers in shells.
|
|
// TODO: Implement sufficiently powerful workers in shells (or provide
|
|
// polyfills).
|
|
env.backends.onnx.wasm.numThreads = 1;
|
|
|
|
// Do not specify path prefix, because this loads the JSEP build by default.
|
|
// TODO: Do we want the JSEP build because it's the default online, or the
|
|
// non-asyncified one, since it's the smaller / more performant one?
|
|
// env.backends.onnx.wasm.wasmPaths = 'build/onnxruntime-web/';
|
|
// So instead, give the ONNX runtime files directly:
|
|
env.backends.onnx.wasm.wasmPaths = {
|
|
// The ONNX runtime module is dynamically imported relative to the
|
|
// Transformers.js module above, hence strip the prefix.
|
|
// With preloading, this is an (absolute) blob URL, so the replace is a nop.
|
|
mjs: JetStream.preload.onnxJsModule.replace('./transformersjs/build/', './')
|
|
};
|
|
// Give it the wasmBinary directly instead of a path, such that the
|
|
// ONNX runtime uses asynchronous (not streaming) Wasm instantiation.
|
|
// (To keep the shell and browser results comparable, and streaming
|
|
// instantiation is not available in shells.)
|
|
env.backends.onnx.wasm.wasmBinary = this.wasmBinary;
|
|
|
|
this.pipeline = await initPipeline(pipeline);
|
|
}
|
|
|
|
this.output = await doTask(this.pipeline, this.inputFile);
|
|
}
|
|
|
|
validate() {
|
|
validate(this.output);
|
|
}
|
|
}
|