diff --git a/8bitbench/benchmark.js b/8bitbench/benchmark.js index 0297c069..5b6551b1 100644 --- a/8bitbench/benchmark.js +++ b/8bitbench/benchmark.js @@ -1,4 +1,5 @@ // Copyright 2025 the V8 project authors. All rights reserved. +// Copyright 2025 Apple Inc. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -34,6 +35,12 @@ function dumpFrame(vec) { class Benchmark { isInstantiated = false; + romBinary; + + async init() { + Module.wasmBinary = await getBinary(wasmBinary); + this.romBinary = await getBinary(romBinary); + } async runIteration() { if (!this.isInstantiated) { @@ -41,7 +48,7 @@ class Benchmark { this.isInstantiated = true; } - wasm_bindgen.loadRom(Module.romBinary); + wasm_bindgen.loadRom(this.romBinary); const frameCount = 2 * 60; for (let i = 0; i < frameCount; ++i) { diff --git a/ARES-6/Babylon/benchmark.js b/ARES-6/Babylon/benchmark.js index 7f11851a..8de7163e 100644 --- a/ARES-6/Babylon/benchmark.js +++ b/ARES-6/Babylon/benchmark.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Apple Inc. All rights reserved. + * Copyright (C) 2017-2025 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,34 +26,18 @@ "use strict"; class Benchmark { - constructor(verbose = 0) - { + async init(verbose = 0) { let sources = []; const files = [ - [isInBrowser ? airBlob : "./ARES-6/Babylon/air-blob.js", {}] - , [isInBrowser ? basicBlob : "./ARES-6/Babylon/basic-blob.js", {}] - , [isInBrowser ? inspectorBlob : "./ARES-6/Babylon/inspector-blob.js", {}] - , [isInBrowser ? babylonBlob : "./ARES-6/Babylon/babylon-blob.js", {sourceType: "module"}] + [airBlob, {}] + , [basicBlob, {}] + , [inspectorBlob, {}] + , [babylonBlob, {sourceType: "module"}] ]; - for (let [file, options] of files) { - function appendSource(s) { - sources.push([file, s, options]); - } - - let s; - if (isInBrowser) { - let request = new XMLHttpRequest(); - request.open('GET', file, false); - request.send(null); - if (!request.responseText.length) - throw new Error("Expect non-empty sources"); - appendSource(request.responseText); - } else { - appendSource(read(file)); - } - } + for (let [file, options] of files) + sources.push([file, await getString(file), options]); this.sources = sources; } diff --git a/Dart/benchmark.js b/Dart/benchmark.js index 3daa61ca..ae7154c0 100644 --- a/Dart/benchmark.js +++ b/Dart/benchmark.js @@ -261,25 +261,9 @@ class Benchmark { async init() { // The generated JavaScript code from dart2wasm is an ES module, which we // can only load with a dynamic import (since this file is not a module.) - // TODO: Support ES6 modules in the driver instead of this one-off solution. - // This probably requires a new `Benchmark` field called `modules` that - // is a map from module variable name (which will hold the resulting module - // namespace object) to relative module URL, which is resolved in the - // `preRunnerCode`, similar to this code here. - if (isInBrowser) { - // In browsers, relative imports don't work since we are not in a module. - // (`import.meta.url` is not defined.) - const pathname = location.pathname.match(/^(.*\/)(?:[^.]+(?:\.(?:[^\/]+))+)?$/)[1]; - this.dart2wasmJsModule = await import(location.origin + pathname + "./Dart/build/flute.dart2wasm.mjs"); - } else { - // In shells, relative imports require different paths, so try with and - // without the "./" prefix (e.g., JSC requires it). - try { - this.dart2wasmJsModule = await import("Dart/build/flute.dart2wasm.mjs"); - } catch { - this.dart2wasmJsModule = await import("./Dart/build/flute.dart2wasm.mjs"); - } - } + + Module.wasmBinary = await getBinary(wasmBinary); + this.dart2wasmJsModule = await dynamicImport(jsModule); } async runIteration() { diff --git a/JetStreamDriver.js b/JetStreamDriver.js index 6c0880d2..581a3335 100644 --- a/JetStreamDriver.js +++ b/JetStreamDriver.js @@ -281,7 +281,6 @@ class Driver { } benchmark.updateUIAfterRun(); - console.log(benchmark.name) if (isInBrowser) { const cache = JetStream.blobDataCache; @@ -776,8 +775,8 @@ class Benchmark { if (this.plan.preload) { let str = ""; - for (let [variableName, blobUrl] of this.preloads) - str += `const ${variableName} = "${blobUrl}";\n`; + for (let [ variableName, blobURLOrPath ] of this.preloads) + str += `const ${variableName} = "${blobURLOrPath}";\n`; addScript(str); } @@ -994,9 +993,15 @@ class Benchmark { if (this._resourcesPromise) return this._resourcesPromise; - const filePromises = !isInBrowser ? this.plan.files.map((file) => fileLoader.load(file)) : []; + this.preloads = []; + + if (isInBrowser) { + this._resourcesPromise = Promise.resolve(); + return this._resourcesPromise; + } - const promise = Promise.all(filePromises).then((texts) => { + const filePromises = this.plan.files.map((file) => fileLoader.load(file)); + this._resourcesPromise = Promise.all(filePromises).then((texts) => { if (isInBrowser) return; this.scripts = []; @@ -1005,10 +1010,11 @@ class Benchmark { this.scripts.push(text); }); - this.preloads = []; - this.blobs = []; + if (this.plan.preload) { + for (const prop of Object.getOwnPropertyNames(this.plan.preload)) + this.preloads.push([ prop, this.plan.preload[prop] ]); + } - this._resourcesPromise = promise; return this._resourcesPromise; } @@ -1130,6 +1136,51 @@ class DefaultBenchmark extends Benchmark { } class AsyncBenchmark extends DefaultBenchmark { + get prerunCode() { + let str = ""; + // FIXME: It would be nice if these were available to any benchmark not just async ones but since these functions + // are async they would only work in a context where the benchmark is async anyway. Long term, we should do away + // with this class and make all benchmarks async. + if (isInBrowser) { + str += ` + async function getBinary(blobURL) { + const response = await fetch(blobURL); + return new Int8Array(await response.arrayBuffer()); + } + + async function getString(blobURL) { + const response = await fetch(blobURL); + return response.text(); + } + + async function dynamicImport(blobURL) { + return await import(blobURL); + } + `; + } else { + str += ` + async function getBinary(path) { + return new Int8Array(read(path, "binary")); + } + + async function getString(path) { + return read(path); + } + + async function dynamicImport(path) { + try { + return await import(path); + } catch (e) { + // In shells, relative imports require different paths, so try with and + // without the "./" prefix (e.g., JSC requires it). + return await import(path.slice("./".length)) + } + } + `; + } + return str; + } + get runnerCode() { return ` async function doRun() { @@ -1197,58 +1248,19 @@ class WasmEMCCBenchmark extends AsyncBenchmark { Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.'); }, }; - globalObject.Module = Module; - `; - return str; - } - // FIXME: Why is this part of the runnerCode and not prerunCode? - get runnerCode() { - let str = `function loadBlob(key, path, andThen) {`; + globalObject.Module = Module; + ${super.prerunCode}; + `; - if (isInBrowser) { - str += ` - var xhr = new XMLHttpRequest(); - xhr.open('GET', path, true); - xhr.responseType = 'arraybuffer'; - xhr.onload = function() { - Module[key] = new Int8Array(xhr.response); - andThen(); - }; - xhr.send(null); - `; - } else { + if (isSpiderMonkey) { str += ` - Module[key] = new Int8Array(read(path, "binary")); - - Module.setStatus = null; - Module.monitorRunDependencies = null; - - Promise.resolve(42).then(() => { - try { - andThen(); - } catch(e) { - console.log("error running wasm:", e); - console.log(e.stack); - throw e; - } - }) + // Needed because SpiderMonkey shell doesn't have a setTimeout. + Module.setStatus = null; + Module.monitorRunDependencies = null; `; } - str += "}"; - - let keys = Object.keys(this.plan.preload); - for (let i = 0; i < keys.length; ++i) { - str += `loadBlob("${keys[i]}", "${this.plan.preload[keys[i]]}", async () => {\n`; - } - - str += super.runnerCode; - for (let i = 0; i < keys.length; ++i) { - str += `})`; - } - str += `;`; - return str; } }; @@ -1585,7 +1597,7 @@ let BENCHMARKS = [ iterations: 60, tags: ["ARES"], }), - new DefaultBenchmark({ + new AsyncBenchmark({ name: "Babylon", files: [ "./ARES-6/Babylon/index.js" @@ -1621,7 +1633,7 @@ let BENCHMARKS = [ tags: ["CDJS"], }), // CodeLoad - new DefaultBenchmark({ + new AsyncBenchmark({ name: "first-inspector-code-load", files: [ "./code-load/code-first-load.js" @@ -1631,7 +1643,7 @@ let BENCHMARKS = [ }, tags: ["CodeLoad"], }), - new DefaultBenchmark({ + new AsyncBenchmark({ name: "multi-inspector-code-load", files: [ "./code-load/code-multi-load.js" @@ -2091,7 +2103,8 @@ let BENCHMARKS = [ "./Dart/benchmark.js", ], preload: { - wasmBinary: "./Dart/build/flute.dart2wasm.wasm" + jsModule: "./Dart/build/flute.dart2wasm.mjs", + wasmBinary: "./Dart/build/flute.dart2wasm.wasm", }, iterations: 15, worstCaseCount: 2, diff --git a/code-load/code-first-load.js b/code-load/code-first-load.js index a72d688e..0a9e3942 100644 --- a/code-load/code-first-load.js +++ b/code-load/code-first-load.js @@ -25,21 +25,8 @@ let indirectEval = eval; class Benchmark { - constructor(iterations) { - - let inspectorText; - if (isInBrowser) { - let request = new XMLHttpRequest(); - request.open('GET', inspectorPayloadBlob, false); - request.send(null); - if (!request.responseText.length) - throw new Error("Expect non-empty sources"); - - inspectorText = request.responseText; - } else - inspectorText = readFile("./code-load/inspector-payload-minified.js"); - - this.inspectorText = `let _____top_level_____ = ${Math.random()}; ${inspectorText}`; + async init() { + this.inspectorText = `let _____top_level_____ = ${Math.random()}; ${await getString(inspectorPayloadBlob)}`; this.index = 0; } diff --git a/code-load/code-multi-load.js b/code-load/code-multi-load.js index 77ce8dc7..1f786b26 100644 --- a/code-load/code-multi-load.js +++ b/code-load/code-multi-load.js @@ -25,20 +25,8 @@ let indirectEval = eval; class Benchmark { - constructor(iterations) { - - let inspectorText; - if (isInBrowser) { - let request = new XMLHttpRequest(); - request.open('GET', inspectorPayloadBlob, false); - request.send(null); - if (!request.responseText.length) - throw new Error("Expect non-empty sources"); - inspectorText = request.responseText; - } else - inspectorText = readFile("./code-load/inspector-payload-minified.js"); - - this.inspectorText = `let _____top_level_____ = ${Math.random()}; ${inspectorText}`; + async init() { + this.inspectorText = `let _____top_level_____ = ${Math.random()}; ${await getString(inspectorPayloadBlob)}`; this.index = 0; } diff --git a/sqlite3/benchmark.js b/sqlite3/benchmark.js index ba2d31ea..416790a5 100644 --- a/sqlite3/benchmark.js +++ b/sqlite3/benchmark.js @@ -1,4 +1,5 @@ // Copyright 2024 the V8 project authors. All rights reserved. +// Copyright 2025 Apple Inc. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -51,6 +52,10 @@ delete globalThis.FileSystemHandle; class Benchmark { sqlite3Module; + async init() { + Module.wasmBinary = await getBinary(wasmBinary); + } + async runIteration() { if (!this.sqlite3Module) { // Defined in the generated SQLite JavaScript code. diff --git a/wasm/HashSet/benchmark.js b/wasm/HashSet/benchmark.js index 0297acf3..c5962770 100644 --- a/wasm/HashSet/benchmark.js +++ b/wasm/HashSet/benchmark.js @@ -1,8 +1,13 @@ // Copyright 2025 the V8 project authors. All rights reserved. +// Copyright 2025 Apple Inc. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. class Benchmark { + async init() { + Module.wasmBinary = await getBinary(wasmBinary); + } + async runIteration() { if (!Module._main) await setupModule(Module); diff --git a/wasm/TSF/benchmark.js b/wasm/TSF/benchmark.js index 5bad2dfa..2d369388 100644 --- a/wasm/TSF/benchmark.js +++ b/wasm/TSF/benchmark.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Apple Inc. All rights reserved. + * Copyright (C) 2024-2025 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,6 +24,10 @@ */ class Benchmark { + async init() { + Module.wasmBinary = await getBinary(wasmBinary); + } + async runIteration() { if (!Module._runIteration) await setupModule(Module); diff --git a/wasm/argon2/benchmark.js b/wasm/argon2/benchmark.js index f7b2e315..13187d3a 100644 --- a/wasm/argon2/benchmark.js +++ b/wasm/argon2/benchmark.js @@ -76,6 +76,10 @@ const version = 0x13; const saltLength = 12; class Benchmark { + async init() { + Module.wasmBinary = await getBinary(wasmBinary); + } + async runIteration() { // Instantiate the Wasm module before the first run. if (!Module._argon2_hash) { diff --git a/wasm/gcc-loops/benchmark.js b/wasm/gcc-loops/benchmark.js index 0297acf3..c5962770 100644 --- a/wasm/gcc-loops/benchmark.js +++ b/wasm/gcc-loops/benchmark.js @@ -1,8 +1,13 @@ // Copyright 2025 the V8 project authors. All rights reserved. +// Copyright 2025 Apple Inc. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. class Benchmark { + async init() { + Module.wasmBinary = await getBinary(wasmBinary); + } + async runIteration() { if (!Module._main) await setupModule(Module); diff --git a/wasm/quicksort/benchmark.js b/wasm/quicksort/benchmark.js index f39f2183..a271ff85 100644 --- a/wasm/quicksort/benchmark.js +++ b/wasm/quicksort/benchmark.js @@ -1,8 +1,13 @@ // Copyright 2025 the V8 project authors. All rights reserved. +// Copyright 2025 Apple Inc. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. class Benchmark { + async init() { + Module.wasmBinary = await getBinary(wasmBinary); + } + async runIteration() { if (!Module._main) await setupModule(Module); diff --git a/wasm/richards/benchmark.js b/wasm/richards/benchmark.js index 6795e77d..53dd1f48 100644 --- a/wasm/richards/benchmark.js +++ b/wasm/richards/benchmark.js @@ -1,8 +1,13 @@ // Copyright 2025 the V8 project authors. All rights reserved. +// Copyright 2025 Apple Inc. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. class Benchmark { + async init() { + Module.wasmBinary = await getBinary(wasmBinary); + } + async runIteration() { // Instantiate the Wasm module before the first run. if (!Module._setup) diff --git a/wasm/zlib/benchmark.js b/wasm/zlib/benchmark.js index 4038c0e6..44a41081 100644 --- a/wasm/zlib/benchmark.js +++ b/wasm/zlib/benchmark.js @@ -1,8 +1,13 @@ // Copyright 2025 the V8 project authors. All rights reserved. +// Copyright 2025 Apple Inc. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. class Benchmark { + async init() { + Module.wasmBinary = await getBinary(wasmBinary); + } + async runIteration() { if (!Module._compressFile) await setupModule(Module);