Skip to content

Commit df99e62

Browse files
authored
Merge pull request #74 from kmiller68/preload-blobs-wasm
Pass the blob URL for preloads in WasmEMCCBenchmark.
2 parents 9163dcb + 510e017 commit df99e62

File tree

14 files changed

+133
-132
lines changed

14 files changed

+133
-132
lines changed

8bitbench/benchmark.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright 2025 the V8 project authors. All rights reserved.
2+
// Copyright 2025 Apple Inc. All rights reserved.
23
// Use of this source code is governed by a BSD-style license that can be
34
// found in the LICENSE file.
45

@@ -34,14 +35,20 @@ function dumpFrame(vec) {
3435

3536
class Benchmark {
3637
isInstantiated = false;
38+
romBinary;
39+
40+
async init() {
41+
Module.wasmBinary = await getBinary(wasmBinary);
42+
this.romBinary = await getBinary(romBinary);
43+
}
3744

3845
async runIteration() {
3946
if (!this.isInstantiated) {
4047
await wasm_bindgen(Module.wasmBinary);
4148
this.isInstantiated = true;
4249
}
4350

44-
wasm_bindgen.loadRom(Module.romBinary);
51+
wasm_bindgen.loadRom(this.romBinary);
4552

4653
const frameCount = 2 * 60;
4754
for (let i = 0; i < frameCount; ++i) {

ARES-6/Babylon/benchmark.js

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2017 Apple Inc. All rights reserved.
2+
* Copyright (C) 2017-2025 Apple Inc. All rights reserved.
33
*
44
* Redistribution and use in source and binary forms, with or without
55
* modification, are permitted provided that the following conditions
@@ -26,34 +26,18 @@
2626
"use strict";
2727

2828
class Benchmark {
29-
constructor(verbose = 0)
30-
{
29+
async init(verbose = 0) {
3130
let sources = [];
3231

3332
const files = [
34-
[isInBrowser ? airBlob : "./ARES-6/Babylon/air-blob.js", {}]
35-
, [isInBrowser ? basicBlob : "./ARES-6/Babylon/basic-blob.js", {}]
36-
, [isInBrowser ? inspectorBlob : "./ARES-6/Babylon/inspector-blob.js", {}]
37-
, [isInBrowser ? babylonBlob : "./ARES-6/Babylon/babylon-blob.js", {sourceType: "module"}]
33+
[airBlob, {}]
34+
, [basicBlob, {}]
35+
, [inspectorBlob, {}]
36+
, [babylonBlob, {sourceType: "module"}]
3837
];
3938

40-
for (let [file, options] of files) {
41-
function appendSource(s) {
42-
sources.push([file, s, options]);
43-
}
44-
45-
let s;
46-
if (isInBrowser) {
47-
let request = new XMLHttpRequest();
48-
request.open('GET', file, false);
49-
request.send(null);
50-
if (!request.responseText.length)
51-
throw new Error("Expect non-empty sources");
52-
appendSource(request.responseText);
53-
} else {
54-
appendSource(read(file));
55-
}
56-
}
39+
for (let [file, options] of files)
40+
sources.push([file, await getString(file), options]);
5741

5842
this.sources = sources;
5943
}

Dart/benchmark.js

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -261,25 +261,9 @@ class Benchmark {
261261
async init() {
262262
// The generated JavaScript code from dart2wasm is an ES module, which we
263263
// can only load with a dynamic import (since this file is not a module.)
264-
// TODO: Support ES6 modules in the driver instead of this one-off solution.
265-
// This probably requires a new `Benchmark` field called `modules` that
266-
// is a map from module variable name (which will hold the resulting module
267-
// namespace object) to relative module URL, which is resolved in the
268-
// `preRunnerCode`, similar to this code here.
269-
if (isInBrowser) {
270-
// In browsers, relative imports don't work since we are not in a module.
271-
// (`import.meta.url` is not defined.)
272-
const pathname = location.pathname.match(/^(.*\/)(?:[^.]+(?:\.(?:[^\/]+))+)?$/)[1];
273-
this.dart2wasmJsModule = await import(location.origin + pathname + "./Dart/build/flute.dart2wasm.mjs");
274-
} else {
275-
// In shells, relative imports require different paths, so try with and
276-
// without the "./" prefix (e.g., JSC requires it).
277-
try {
278-
this.dart2wasmJsModule = await import("Dart/build/flute.dart2wasm.mjs");
279-
} catch {
280-
this.dart2wasmJsModule = await import("./Dart/build/flute.dart2wasm.mjs");
281-
}
282-
}
264+
265+
Module.wasmBinary = await getBinary(wasmBinary);
266+
this.dart2wasmJsModule = await dynamicImport(jsModule);
283267
}
284268

285269
async runIteration() {

JetStreamDriver.js

Lines changed: 71 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,6 @@ class Driver {
281281
}
282282

283283
benchmark.updateUIAfterRun();
284-
console.log(benchmark.name)
285284

286285
if (isInBrowser) {
287286
const cache = JetStream.blobDataCache;
@@ -776,8 +775,8 @@ class Benchmark {
776775

777776
if (this.plan.preload) {
778777
let str = "";
779-
for (let [variableName, blobUrl] of this.preloads)
780-
str += `const ${variableName} = "${blobUrl}";\n`;
778+
for (let [ variableName, blobURLOrPath ] of this.preloads)
779+
str += `const ${variableName} = "${blobURLOrPath}";\n`;
781780
addScript(str);
782781
}
783782

@@ -994,9 +993,15 @@ class Benchmark {
994993
if (this._resourcesPromise)
995994
return this._resourcesPromise;
996995

997-
const filePromises = !isInBrowser ? this.plan.files.map((file) => fileLoader.load(file)) : [];
996+
this.preloads = [];
997+
998+
if (isInBrowser) {
999+
this._resourcesPromise = Promise.resolve();
1000+
return this._resourcesPromise;
1001+
}
9981002

999-
const promise = Promise.all(filePromises).then((texts) => {
1003+
const filePromises = this.plan.files.map((file) => fileLoader.load(file));
1004+
this._resourcesPromise = Promise.all(filePromises).then((texts) => {
10001005
if (isInBrowser)
10011006
return;
10021007
this.scripts = [];
@@ -1005,10 +1010,11 @@ class Benchmark {
10051010
this.scripts.push(text);
10061011
});
10071012

1008-
this.preloads = [];
1009-
this.blobs = [];
1013+
if (this.plan.preload) {
1014+
for (const prop of Object.getOwnPropertyNames(this.plan.preload))
1015+
this.preloads.push([ prop, this.plan.preload[prop] ]);
1016+
}
10101017

1011-
this._resourcesPromise = promise;
10121018
return this._resourcesPromise;
10131019
}
10141020

@@ -1130,6 +1136,51 @@ class DefaultBenchmark extends Benchmark {
11301136
}
11311137

11321138
class AsyncBenchmark extends DefaultBenchmark {
1139+
get prerunCode() {
1140+
let str = "";
1141+
// FIXME: It would be nice if these were available to any benchmark not just async ones but since these functions
1142+
// are async they would only work in a context where the benchmark is async anyway. Long term, we should do away
1143+
// with this class and make all benchmarks async.
1144+
if (isInBrowser) {
1145+
str += `
1146+
async function getBinary(blobURL) {
1147+
const response = await fetch(blobURL);
1148+
return new Int8Array(await response.arrayBuffer());
1149+
}
1150+
1151+
async function getString(blobURL) {
1152+
const response = await fetch(blobURL);
1153+
return response.text();
1154+
}
1155+
1156+
async function dynamicImport(blobURL) {
1157+
return await import(blobURL);
1158+
}
1159+
`;
1160+
} else {
1161+
str += `
1162+
async function getBinary(path) {
1163+
return new Int8Array(read(path, "binary"));
1164+
}
1165+
1166+
async function getString(path) {
1167+
return read(path);
1168+
}
1169+
1170+
async function dynamicImport(path) {
1171+
try {
1172+
return await import(path);
1173+
} catch (e) {
1174+
// In shells, relative imports require different paths, so try with and
1175+
// without the "./" prefix (e.g., JSC requires it).
1176+
return await import(path.slice("./".length))
1177+
}
1178+
}
1179+
`;
1180+
}
1181+
return str;
1182+
}
1183+
11331184
get runnerCode() {
11341185
return `
11351186
async function doRun() {
@@ -1197,58 +1248,19 @@ class WasmEMCCBenchmark extends AsyncBenchmark {
11971248
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
11981249
},
11991250
};
1200-
globalObject.Module = Module;
1201-
`;
1202-
return str;
1203-
}
12041251
1205-
// FIXME: Why is this part of the runnerCode and not prerunCode?
1206-
get runnerCode() {
1207-
let str = `function loadBlob(key, path, andThen) {`;
1252+
globalObject.Module = Module;
1253+
${super.prerunCode};
1254+
`;
12081255

1209-
if (isInBrowser) {
1210-
str += `
1211-
var xhr = new XMLHttpRequest();
1212-
xhr.open('GET', path, true);
1213-
xhr.responseType = 'arraybuffer';
1214-
xhr.onload = function() {
1215-
Module[key] = new Int8Array(xhr.response);
1216-
andThen();
1217-
};
1218-
xhr.send(null);
1219-
`;
1220-
} else {
1256+
if (isSpiderMonkey) {
12211257
str += `
1222-
Module[key] = new Int8Array(read(path, "binary"));
1223-
1224-
Module.setStatus = null;
1225-
Module.monitorRunDependencies = null;
1226-
1227-
Promise.resolve(42).then(() => {
1228-
try {
1229-
andThen();
1230-
} catch(e) {
1231-
console.log("error running wasm:", e);
1232-
console.log(e.stack);
1233-
throw e;
1234-
}
1235-
})
1258+
// Needed because SpiderMonkey shell doesn't have a setTimeout.
1259+
Module.setStatus = null;
1260+
Module.monitorRunDependencies = null;
12361261
`;
12371262
}
12381263

1239-
str += "}";
1240-
1241-
let keys = Object.keys(this.plan.preload);
1242-
for (let i = 0; i < keys.length; ++i) {
1243-
str += `loadBlob("${keys[i]}", "${this.plan.preload[keys[i]]}", async () => {\n`;
1244-
}
1245-
1246-
str += super.runnerCode;
1247-
for (let i = 0; i < keys.length; ++i) {
1248-
str += `})`;
1249-
}
1250-
str += `;`;
1251-
12521264
return str;
12531265
}
12541266
};
@@ -1585,7 +1597,7 @@ let BENCHMARKS = [
15851597
iterations: 60,
15861598
tags: ["ARES"],
15871599
}),
1588-
new DefaultBenchmark({
1600+
new AsyncBenchmark({
15891601
name: "Babylon",
15901602
files: [
15911603
"./ARES-6/Babylon/index.js"
@@ -1621,7 +1633,7 @@ let BENCHMARKS = [
16211633
tags: ["CDJS"],
16221634
}),
16231635
// CodeLoad
1624-
new DefaultBenchmark({
1636+
new AsyncBenchmark({
16251637
name: "first-inspector-code-load",
16261638
files: [
16271639
"./code-load/code-first-load.js"
@@ -1631,7 +1643,7 @@ let BENCHMARKS = [
16311643
},
16321644
tags: ["CodeLoad"],
16331645
}),
1634-
new DefaultBenchmark({
1646+
new AsyncBenchmark({
16351647
name: "multi-inspector-code-load",
16361648
files: [
16371649
"./code-load/code-multi-load.js"
@@ -2091,7 +2103,8 @@ let BENCHMARKS = [
20912103
"./Dart/benchmark.js",
20922104
],
20932105
preload: {
2094-
wasmBinary: "./Dart/build/flute.dart2wasm.wasm"
2106+
jsModule: "./Dart/build/flute.dart2wasm.mjs",
2107+
wasmBinary: "./Dart/build/flute.dart2wasm.wasm",
20952108
},
20962109
iterations: 15,
20972110
worstCaseCount: 2,

code-load/code-first-load.js

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,8 @@
2525

2626
let indirectEval = eval;
2727
class Benchmark {
28-
constructor(iterations) {
29-
30-
let inspectorText;
31-
if (isInBrowser) {
32-
let request = new XMLHttpRequest();
33-
request.open('GET', inspectorPayloadBlob, false);
34-
request.send(null);
35-
if (!request.responseText.length)
36-
throw new Error("Expect non-empty sources");
37-
38-
inspectorText = request.responseText;
39-
} else
40-
inspectorText = readFile("./code-load/inspector-payload-minified.js");
41-
42-
this.inspectorText = `let _____top_level_____ = ${Math.random()}; ${inspectorText}`;
28+
async init() {
29+
this.inspectorText = `let _____top_level_____ = ${Math.random()}; ${await getString(inspectorPayloadBlob)}`;
4330

4431
this.index = 0;
4532
}

code-load/code-multi-load.js

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,8 @@
2525

2626
let indirectEval = eval;
2727
class Benchmark {
28-
constructor(iterations) {
29-
30-
let inspectorText;
31-
if (isInBrowser) {
32-
let request = new XMLHttpRequest();
33-
request.open('GET', inspectorPayloadBlob, false);
34-
request.send(null);
35-
if (!request.responseText.length)
36-
throw new Error("Expect non-empty sources");
37-
inspectorText = request.responseText;
38-
} else
39-
inspectorText = readFile("./code-load/inspector-payload-minified.js");
40-
41-
this.inspectorText = `let _____top_level_____ = ${Math.random()}; ${inspectorText}`;
28+
async init() {
29+
this.inspectorText = `let _____top_level_____ = ${Math.random()}; ${await getString(inspectorPayloadBlob)}`;
4230
this.index = 0;
4331
}
4432

sqlite3/benchmark.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright 2024 the V8 project authors. All rights reserved.
2+
// Copyright 2025 Apple Inc. All rights reserved.
23
// Use of this source code is governed by a BSD-style license that can be
34
// found in the LICENSE file.
45

@@ -51,6 +52,10 @@ delete globalThis.FileSystemHandle;
5152
class Benchmark {
5253
sqlite3Module;
5354

55+
async init() {
56+
Module.wasmBinary = await getBinary(wasmBinary);
57+
}
58+
5459
async runIteration() {
5560
if (!this.sqlite3Module) {
5661
// Defined in the generated SQLite JavaScript code.

wasm/HashSet/benchmark.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
// Copyright 2025 the V8 project authors. All rights reserved.
2+
// Copyright 2025 Apple Inc. All rights reserved.
23
// Use of this source code is governed by a BSD-style license that can be
34
// found in the LICENSE file.
45

56
class Benchmark {
7+
async init() {
8+
Module.wasmBinary = await getBinary(wasmBinary);
9+
}
10+
611
async runIteration() {
712
if (!Module._main)
813
await setupModule(Module);

0 commit comments

Comments
 (0)