Reset main-custom to latest main version

Following a different approach to better keep main-custom up to date
with main by resetting and merging pending upstream PRs on top.

Change-Id: I26c7a2c63b166715d01b50da0bd47aaf99aa3c1a
Reviewed-on: https://chromium-review.googlesource.com/c/external/github.com/WebKit/JetStream/+/6976606
Reviewed-by: Victor Gomes <victorgomes@chromium.org>
This commit is contained in:
Camillo Bruni 2025-09-23 16:47:00 +02:00
parent 97f348ec9d
commit 8270d839bf
141 changed files with 210056 additions and 21677 deletions

View file

@ -1,5 +1,5 @@
Built on Thu Aug 14 04:47:37 PM CEST 2025 Built on Thu Aug 14 04:47:37 PM CEST 2025
Cloning into 'wasm_gc_benchmarks'... Cloning into 'wasm_gc_benchmarks'...
13951e1 Roll new version of flute and regenerate flute-based benchmarks 13951e1 Roll new version of flute and regenerate flute-based benchmarks
Copying files from wasm_gc_benchmarks/ into build/ Copying files from wasm_gc_benchmarks/ into build/
Build success Build success

View file

@ -348,13 +348,6 @@ a.button {
scroll-margin-bottom: 20vh; scroll-margin-bottom: 20vh;
} }
.benchmark .result.detail {
display: none;
}
.details .benchmark .result.detail {
display: inline-block;
}
.benchmark h4, .benchmark h4,
.benchmark .result, .benchmark .result,
.benchmark label, .benchmark label,

View file

@ -30,84 +30,50 @@ const measureTotalTimeAsSubtest = false; // Once we move to preloading all resou
const defaultIterationCount = 120; const defaultIterationCount = 120;
const defaultWorstCaseCount = 4; const defaultWorstCaseCount = 4;
globalThis.performance ??= Date; if (!JetStreamParams.prefetchResources)
globalThis.RAMification ??= false;
globalThis.testIterationCount ??= undefined;
globalThis.testIterationCountMap ??= new Map();
globalThis.testWorstCaseCount ??= undefined;
globalThis.testWorstCaseCountMap ??= new Map();
globalThis.dumpJSONResults ??= false;
globalThis.testList ??= undefined;
globalThis.startDelay ??= undefined;
globalThis.shouldReport ??= false;
globalThis.prefetchResources ??= true;
function getIntParam(urlParams, key) {
const rawValue = urlParams.get(key);
const value = parseInt(rawValue);
if (value <= 0)
throw new Error(`Expected positive value for ${key}, but got ${rawValue}`);
return value;
}
function getBoolParam(urlParams, key) {
const rawValue = urlParams.get(key).toLowerCase()
return !(rawValue === "false" || rawValue === "0")
}
function getTestListParam(urlParams, key) {
if (globalThis.testList?.length)
throw new Error(`Overriding previous testList=${globalThis.testList.join()} with ${key} url-parameter.`);
return urlParams.getAll(key);
}
if (typeof(URLSearchParams) !== "undefined") {
const urlParameters = new URLSearchParams(window.location.search);
if (urlParameters.has("report"))
globalThis.shouldReport = urlParameters.get("report").toLowerCase() == "true";
if (urlParameters.has("startDelay"))
globalThis.startDelay = getIntParam(urlParameters, "startDelay");
if (globalThis.shouldReport && !globalThis.startDelay)
globalThis.startDelay = 4000;
if (urlParameters.has("tag"))
globalThis.testList = getTestListParam(urlParameters, "tag");
if (urlParameters.has("test"))
globalThis.testList = getTestListParam(urlParameters, "test");
if (urlParameters.has("iterationCount"))
globalThis.testIterationCount = getIntParam(urlParameters, "iterationCount");
if (urlParameters.has("worstCaseCount"))
globalThis.testWorstCaseCount = getIntParam(urlParameters, "worstCaseCount");
if (urlParameters.has("prefetchResources"))
globalThis.prefetchResources = getBoolParam(urlParameters, "prefetchResources");
}
if (!globalThis.prefetchResources)
console.warn("Disabling resource prefetching!"); console.warn("Disabling resource prefetching!");
// Used for the promise representing the current benchmark run. // Used for the promise representing the current benchmark run.
this.currentResolve = null; this.currentResolve = null;
this.currentReject = null; this.currentReject = null;
let showScoreDetails = false;
let categoryScores = null;
function displayCategoryScores() { function displayCategoryScores() {
document.body.classList.add("details"); if (!categoryScores)
return;
let scoreDetails = `<div class="benchmark benchmark-done">`;
for (let [category, scores] of categoryScores) {
scoreDetails += `<span class="result">
<span>${uiFriendlyScore(geomeanScore(scores))}</span>
<label>${category}</label>
</span>`;
}
scoreDetails += "</div>";
let summaryElement = document.getElementById("result-summary");
summaryElement.innerHTML += scoreDetails;
categoryScores = null;
} }
function getIterationCount(plan) { function getIterationCount(plan) {
if (testIterationCountMap.has(plan.name)) if (JetStreamParams.testIterationCountMap.has(plan.name))
return testIterationCountMap.get(plan.name); return JetStreamParams.testIterationCountMap.get(plan.name);
if (globalThis.testIterationCount) if (JetStreamParams.testIterationCount)
return globalThis.testIterationCount; return JetStreamParams.testIterationCount;
if (plan.iterations) if (plan.iterations)
return plan.iterations; return plan.iterations;
return defaultIterationCount; return defaultIterationCount;
} }
function getWorstCaseCount(plan) { function getWorstCaseCount(plan) {
if (testWorstCaseCountMap.has(plan.name)) if (JetStreamParams.testWorstCaseCountMap.has(plan.name))
return testWorstCaseCountMap.get(plan.name); return JetStreamParams.testWorstCaseCountMap.get(plan.name);
if (globalThis.testWorstCaseCount) if (JetStreamParams.testWorstCaseCount)
return globalThis.testWorstCaseCount; return JetStreamParams.testWorstCaseCount;
if (plan.worstCaseCount) if (plan.worstCaseCount !== undefined)
return plan.worstCaseCount; return plan.worstCaseCount;
return defaultWorstCaseCount; return defaultWorstCaseCount;
} }
@ -115,23 +81,19 @@ function getWorstCaseCount(plan) {
if (isInBrowser) { if (isInBrowser) {
document.onkeydown = (keyboardEvent) => { document.onkeydown = (keyboardEvent) => {
const key = keyboardEvent.key; const key = keyboardEvent.key;
if (key === "d" || key === "D") if (key === "d" || key === "D") {
showScoreDetails = true;
displayCategoryScores(); displayCategoryScores();
}
}; };
} }
function sum(values) { function mean(values) {
console.assert(values instanceof Array); console.assert(values instanceof Array);
let sum = 0; let sum = 0;
for (let x of values) for (let x of values)
sum += x; sum += x;
return sum; return sum / values.length;
}
function mean(values) {
const totalSum = sum(values)
return totalSum / values.length;
} }
function geomeanScore(values) { function geomeanScore(values) {
@ -173,7 +135,7 @@ function uiFriendlyScore(num) {
} }
function uiFriendlyDuration(time) { function uiFriendlyDuration(time) {
return `${time.toFixed(2)} ms`; return `${time.toFixed(3)} ms`;
} }
// TODO: Cleanup / remove / merge. This is only used for caching loads in the // TODO: Cleanup / remove / merge. This is only used for caching loads in the
@ -188,7 +150,7 @@ class ShellFileLoader {
// share common code. // share common code.
load(url) { load(url) {
console.assert(!isInBrowser); console.assert(!isInBrowser);
if (!globalThis.prefetchResources) if (!JetStreamParams.prefetchResources)
return `load("${url}");` return `load("${url}");`
if (this.requests.has(url)) { if (this.requests.has(url)) {
@ -227,7 +189,7 @@ class Driver {
if (isInBrowser) { if (isInBrowser) {
statusElement = document.getElementById("status"); statusElement = document.getElementById("status");
statusElement.innerHTML = `<label>Running...</label>`; statusElement.innerHTML = `<label>Running...</label>`;
} else if (!dumpJSONResults) } else if (!JetStreamParams.dumpJSONResults)
console.log("Starting JetStream3"); console.log("Starting JetStream3");
performance.mark("update-ui-start"); performance.mark("update-ui-start");
@ -246,9 +208,8 @@ class Driver {
performance.mark("update-ui"); performance.mark("update-ui");
benchmark.updateUIAfterRun(); benchmark.updateUIAfterRun();
console.log("")
if (isInBrowser && globalThis.prefetchResources) { if (isInBrowser && JetStreamParams.prefetchResources) {
const cache = JetStream.blobDataCache; const cache = JetStream.blobDataCache;
for (const file of benchmark.files) { for (const file of benchmark.files) {
const blobData = cache[file]; const blobData = cache[file];
@ -264,7 +225,7 @@ class Driver {
if (measureTotalTimeAsSubtest) { if (measureTotalTimeAsSubtest) {
if (isInBrowser) if (isInBrowser)
document.getElementById("benchmark-total-time-score").innerHTML = uiFriendlyNumber(totalTime); document.getElementById("benchmark-total-time-score").innerHTML = uiFriendlyNumber(totalTime);
else if (!dumpJSONResults) else if (!JetStreamParams.dumpJSONResults)
console.log("Total time:", uiFriendlyNumber(totalTime)); console.log("Total time:", uiFriendlyNumber(totalTime));
allScores.push(totalTime); allScores.push(totalTime);
} }
@ -276,13 +237,10 @@ class Driver {
allScores.push(score); allScores.push(score);
} }
const categoryScores = new Map(); categoryScores = new Map;
const categoryTimes = new Map();
for (const benchmark of this.benchmarks) { for (const benchmark of this.benchmarks) {
for (let category of Object.keys(benchmark.subScores())) for (let category of Object.keys(benchmark.subScores()))
categoryScores.set(category, []); categoryScores.set(category, []);
for (let category of Object.keys(benchmark.subTimes()))
categoryTimes.set(category, []);
} }
for (const benchmark of this.benchmarks) { for (const benchmark of this.benchmarks) {
@ -291,49 +249,25 @@ class Driver {
console.assert(value > 0, `Invalid ${benchmark.name} ${category} score: ${value}`); console.assert(value > 0, `Invalid ${benchmark.name} ${category} score: ${value}`);
arr.push(value); arr.push(value);
} }
for (let [category, value] of Object.entries(benchmark.subTimes())) {
const arr = categoryTimes.get(category);
console.assert(value > 0, `Invalid ${benchmark.name} ${category} time: ${value}`);
arr.push(value);
}
} }
const totalScore = geomeanScore(allScores); const totalScore = geomeanScore(allScores);
console.assert(totalScore > 0, `Invalid total score: ${totalScore}`); console.assert(totalScore > 0, `Invalid total score: ${totalScore}`);
if (isInBrowser) { if (isInBrowser) {
let summaryHtml = `<div class="score">${uiFriendlyScore(totalScore)}</div>
<label>Score</label>`;
summaryHtml += `<div class="benchmark benchmark-done">`;
for (let [category, scores] of categoryScores) {
summaryHtml += `<span class="result detail">
<span>${uiFriendlyScore(geomeanScore(scores))}</span>
<label>${category}</label>
</span>`;
}
for (let [category, times] of categoryTimes) {
summaryHtml += `<span class="result detail">
<span>${uiFriendlyDuration(geomeanScore(times))}</span>
<label>${category}</label>
</span>`;
}
summaryHtml += "</div>";
const summaryElement = document.getElementById("result-summary"); const summaryElement = document.getElementById("result-summary");
summaryElement.classList.add("done"); summaryElement.classList.add("done");
summaryElement.innerHTML = summaryHtml; summaryElement.innerHTML = `<div class="score">${uiFriendlyScore(totalScore)}</div>
<label>Score</label>`;
summaryElement.onclick = displayCategoryScores; summaryElement.onclick = displayCategoryScores;
if (showScoreDetails)
displayCategoryScores();
statusElement.innerHTML = ""; statusElement.innerHTML = "";
} else if (!dumpJSONResults) { } else if (!JetStreamParams.dumpJSONResults) {
console.log("Mean-Scores:"); console.log("\n");
for (let [category, scores] of categoryScores) for (let [category, scores] of categoryScores)
console.log(` ${category}-Score: ${uiFriendlyScore(geomeanScore(scores))}`); console.log(`${category}: ${uiFriendlyScore(geomeanScore(scores))}`);
console.log("Mean-Times:"); console.log("\nTotal Score: ", uiFriendlyScore(totalScore), "\n");
for (let [category, times] of categoryTimes)
console.log(` ${category}-Time: ${uiFriendlyDuration(geomeanScore(times))}`);
console.log("");
console.log("Totals:");
console.log(" Total-Score: ", uiFriendlyScore(totalScore));
console.log(" Total-Time: ", uiFriendlyDuration(totalTime));
} }
this.reportScoreToRunBenchmarkRunner(); this.reportScoreToRunBenchmarkRunner();
@ -350,39 +284,32 @@ class Driver {
prepareToRun() { prepareToRun() {
this.benchmarks.sort((a, b) => a.plan.name.toLowerCase() < b.plan.name.toLowerCase() ? 1 : -1); this.benchmarks.sort((a, b) => a.plan.name.toLowerCase() < b.plan.name.toLowerCase() ? 1 : -1);
if (!isInBrowser)
return;
let text = ""; let text = "";
for (const benchmark of this.benchmarks) { for (const benchmark of this.benchmarks) {
const scoreDescription = Object.keys(benchmark.allScores()); const description = Object.keys(benchmark.subScores());
const timeDescription = Object.keys(benchmark.allTimes()); description.push("Score");
const scoreIds = benchmark.scoreIdentifiers(); const scoreIds = benchmark.scoreIdentifiers();
const overallScoreId = scoreIds.pop(); const overallScoreId = scoreIds.pop();
const timeIds = benchmark.timeIdentifiers();
text += if (isInBrowser) {
`<div class="benchmark" id="benchmark-${benchmark.name}"> text +=
<h3 class="benchmark-name">${benchmark.name} <a class="info" href="in-depth.html#${benchmark.name}">i</a></h3> `<div class="benchmark" id="benchmark-${benchmark.name}">
<h4 class="score" id="${overallScoreId}">&nbsp;</h4> <h3 class="benchmark-name">${benchmark.name} <a class="info" href="in-depth.html#${benchmark.name}">i</a></h3>
<h4 class="plot" id="plot-${benchmark.name}">&nbsp;</h4> <h4 class="score" id="${overallScoreId}">&nbsp;</h4>
<p>`; <h4 class="plot" id="plot-${benchmark.name}">&nbsp;</h4>
for (let i = 0; i < scoreIds.length; i++) { <p>`;
const scoreId = scoreIds[i]; for (let i = 0; i < scoreIds.length; i++) {
const label = scoreDescription[i]; const scoreId = scoreIds[i];
text += `<span class="result score"><span id="${scoreId}">&nbsp;</span><label>${label}</label></span>` const label = description[i];
text += `<span class="result"><span id="${scoreId}">&nbsp;</span><label>${label}</label></span>`
}
text += `</p></div>`;
} }
for (let i = 0; i < timeIds.length; i++) {
const timeId = timeIds[i];
const label = timeDescription[i];
text += `<span class="result detail"><span id="${timeId}">&nbsp;</span><label>${label}</label></span>`
}
text += `</p></div>`;
} }
for (let f = 0; f < 5; f++) if (!isInBrowser)
text += `<div class="benchmark fill"></div>`; return;
const timestamp = performance.now(); const timestamp = performance.now();
document.getElementById('jetstreams').style.backgroundImage = `url('jetstreams.svg?${timestamp}')`; document.getElementById('jetstreams').style.backgroundImage = `url('jetstreams.svg?${timestamp}')`;
@ -402,13 +329,12 @@ class Driver {
if (!isInBrowser) if (!isInBrowser)
return; return;
for (const id of benchmark.scoreIdentifiers()) for (const id of benchmark.scoreIdentifiers()) {
document.getElementById(id).innerHTML = "error"; document.getElementById(id).innerHTML = "error";
for (const id of benchmark.timeIdentifiers()) const benchmarkResultsUI = document.getElementById(`benchmark-${benchmark.name}`);
document.getElementById(id).innerHTML = "error"; benchmarkResultsUI.classList.remove("benchmark-running");
const benchmarkResultsUI = document.getElementById(`benchmark-${benchmark.name}`); benchmarkResultsUI.classList.add("benchmark-error");
benchmarkResultsUI.classList.remove("benchmark-running"); }
benchmarkResultsUI.classList.add("benchmark-error");
} }
pushError(name, error) { pushError(name, error) {
@ -427,8 +353,8 @@ class Driver {
this.isReady = true; this.isReady = true;
if (isInBrowser) { if (isInBrowser) {
globalThis.dispatchEvent(new Event("JetStreamReady")); globalThis.dispatchEvent(new Event("JetStreamReady"));
if (typeof(globalThis.startDelay) !== "undefined") { if (typeof(JetStreamParams.startDelay) !== "undefined") {
setTimeout(() => this.start(), globalThis.startDelay); setTimeout(() => this.start(), JetStreamParams.startDelay);
} }
} }
} }
@ -533,7 +459,7 @@ class Driver {
dumpJSONResultsIfNeeded() dumpJSONResultsIfNeeded()
{ {
if (dumpJSONResults) { if (JetStreamParams.dumpJSONResults) {
console.log("\n"); console.log("\n");
console.log(this.resultsJSON()); console.log(this.resultsJSON());
console.log("\n"); console.log("\n");
@ -552,7 +478,7 @@ class Driver {
if (!isInBrowser) if (!isInBrowser)
return; return;
if (!globalThis.shouldReport) if (!JetStreamParams.shouldReport)
return; return;
const content = this.resultsJSON(); const content = this.resultsJSON();
@ -599,6 +525,7 @@ class Scripts {
this.add(` this.add(`
performance.mark ??= function(name) { return { name }}; performance.mark ??= function(name) { return { name }};
performance.measure ??= function() {}; performance.measure ??= function() {};
performance.timeOrigin ??= performance.now();
`); `);
} }
@ -767,11 +694,6 @@ class Benchmark {
}; };
} }
get isDone() {
return this._state == BenchmarkState.DONE || this._state == BenchmarkState.ERROR;
}
get isSuccess() { return this._state = BenchmarkState.DONE; }
get runnerCode() { get runnerCode() {
return `{ return `{
const benchmark = new Benchmark(${JSON.stringify(this.benchmarkArguments)}); const benchmark = new Benchmark(${JSON.stringify(this.benchmarkArguments)});
@ -809,36 +731,16 @@ class Benchmark {
return geomeanScore(subScores); return geomeanScore(subScores);
} }
get totalTime() {
const subTimes = Object.values(this.subTimes());
return sum(subTimes);
}
get wallTime() {
return this.endTime - this.startTime;
}
subScores() { subScores() {
throw new Error("Subclasses need to implement this"); throw new Error("Subclasses need to implement this");
} }
subTimes() {
throw new Error("Subclasses need to implement this");
}
allScores() { allScores() {
const allScores = this.subScores(); const allScores = this.subScores();
allScores["Score"] = this.score; allScores["Score"] = this.score;
return allScores; return allScores;
} }
allTimes() {
const allTimes = this.subTimes();
allTimes["Total"] = this.totalTime;
allTimes["Wall-Time"] = this.wallTime;
return allTimes;
}
get prerunCode() { return null; } get prerunCode() { return null; }
get preIterationCode() { get preIterationCode() {
@ -846,8 +748,8 @@ class Benchmark {
if (this.plan.deterministicRandom) if (this.plan.deterministicRandom)
code += `Math.random.__resetSeed();`; code += `Math.random.__resetSeed();`;
if (globalThis.customPreIterationCode) if (JetStreamParams.customPreIterationCode)
code += customPreIterationCode; code += JetStreamParams.customPreIterationCode;
return code; return code;
} }
@ -855,8 +757,8 @@ class Benchmark {
get postIterationCode() { get postIterationCode() {
let code = ""; let code = "";
if (globalThis.customPostIterationCode) if (JetStreamParams.customPostIterationCode)
code += customPostIterationCode; code += JetStreamParams.customPostIterationCode;
return code; return code;
} }
@ -890,7 +792,7 @@ class Benchmark {
} else { } else {
const cache = JetStream.blobDataCache; const cache = JetStream.blobDataCache;
for (const file of this.plan.files) { for (const file of this.plan.files) {
scripts.addWithURL(globalThis.prefetchResources ? cache[file].blobURL : file); scripts.addWithURL(JetStreamParams.prefetchResources ? cache[file].blobURL : file);
} }
} }
@ -904,7 +806,7 @@ class Benchmark {
performance.mark(this.name); performance.mark(this.name);
this.startTime = performance.now(); this.startTime = performance.now();
if (RAMification) if (JetStreamParams.RAMification)
resetMemoryPeak(); resetMemoryPeak();
let magicFrame; let magicFrame;
@ -924,7 +826,7 @@ class Benchmark {
this.endTime = performance.now(); this.endTime = performance.now();
performance.measure(this.name, this.name); performance.measure(this.name, this.name);
if (RAMification) { if (JetStreamParams.RAMification) {
const memoryFootprint = MemoryFootprint(); const memoryFootprint = MemoryFootprint();
this.currentFootprint = memoryFootprint.current; this.currentFootprint = memoryFootprint.current;
this.peakFootprint = memoryFootprint.peak; this.peakFootprint = memoryFootprint.peak;
@ -941,7 +843,7 @@ class Benchmark {
async doLoadBlob(resource) { async doLoadBlob(resource) {
const blobData = JetStream.blobDataCache[resource]; const blobData = JetStream.blobDataCache[resource];
if (!globalThis.prefetchResources) { if (!JetStreamParams.prefetchResources) {
blobData.blobURL = resource; blobData.blobURL = resource;
return blobData; return blobData;
} }
@ -1068,10 +970,6 @@ class Benchmark {
return !counter.failedPreloadResources && counter.loadedResources == counter.totalResources; return !counter.failedPreloadResources && counter.loadedResources == counter.totalResources;
} }
prefetchResourcesForD8() {
// For d8 we just keep the file name
}
async retryPrefetchResourcesForBrowser() { async retryPrefetchResourcesForBrowser() {
console.assert(isInBrowser); console.assert(isInBrowser);
@ -1112,17 +1010,8 @@ class Benchmark {
return `results-cell-${this.name}-${scoreName}`; return `results-cell-${this.name}-${scoreName}`;
} }
timeIdentifiers() {
const ids = Object.keys(this.allTimes()).map(name => this.timeIdentifier(name));
return ids;
}
timeIdentifier(scoreName) {
return `results-cell-${this.name}-${scoreName}-time`;
}
updateUIBeforeRun() { updateUIBeforeRun() {
if (!dumpJSONResults) if (!JetStreamParams.dumpJSONResults)
console.log(`Running ${this.name}:`); console.log(`Running ${this.name}:`);
if (isInBrowser) if (isInBrowser)
this.updateUIBeforeRunInBrowser(); this.updateUIBeforeRunInBrowser();
@ -1135,29 +1024,24 @@ class Benchmark {
for (const id of this.scoreIdentifiers()) for (const id of this.scoreIdentifiers())
document.getElementById(id).innerHTML = "..."; document.getElementById(id).innerHTML = "...";
for (const id of this.timeIdentifiers())
document.getElementById(id).innerHTML = "...";
} }
updateUIAfterRun() { updateUIAfterRun() {
const scoreEntries = Object.entries(this.allScores()); const scoreEntries = Object.entries(this.allScores());
const timeEntries = Object.entries(this.allTimes());
if (isInBrowser) if (isInBrowser)
this.updateUIAfterRunInBrowser(scoreEntries, timeEntries); this.updateUIAfterRunInBrowser(scoreEntries);
if (dumpJSONResults) if (JetStreamParams.dumpJSONResults)
return; return;
this.updateConsoleAfterRun(scoreEntries, timeEntries); this.updateConsoleAfterRun(scoreEntries);
} }
updateUIAfterRunInBrowser(scoreEntries, timeEntries) { updateUIAfterRunInBrowser(scoreEntries) {
const benchmarkResultsUI = document.getElementById(`benchmark-${this.name}`); const benchmarkResultsUI = document.getElementById(`benchmark-${this.name}`);
benchmarkResultsUI.classList.remove("benchmark-running"); benchmarkResultsUI.classList.remove("benchmark-running");
benchmarkResultsUI.classList.add("benchmark-done"); benchmarkResultsUI.classList.add("benchmark-done");
for (const [name, value] of scoreEntries) for (const [name, value] of scoreEntries)
document.getElementById(this.scoreIdentifier(name)).innerHTML = uiFriendlyScore(value); document.getElementById(this.scoreIdentifier(name)).innerHTML = uiFriendlyScore(value);
for (const [name, value] of timeEntries)
document.getElementById(this.timeIdentifier(name)).innerHTML = uiFriendlyDuration(value);
this.renderScatterPlot(); this.renderScatterPlot();
} }
@ -1190,21 +1074,15 @@ class Benchmark {
plotContainer.innerHTML = `<svg width="${width}px" height="${height}px">${circlesSVG}</svg>`; plotContainer.innerHTML = `<svg width="${width}px" height="${height}px">${circlesSVG}</svg>`;
} }
updateConsoleAfterRun(scoreEntries, timeEntries) { updateConsoleAfterRun(scoreEntries) {
for (let [name, value] of scoreEntries) { for (let [name, value] of scoreEntries) {
if (!name.endsWith("Score")) console.log(` ${name}:`, uiFriendlyScore(value));
name += "-Score";
console.log(this.name, `${name}:`, uiFriendlyScore(value));
} }
for (let [name, value] of timeEntries) { if (JetStreamParams.RAMification) {
if (!name.endsWith("Time")) console.log(" Current Footprint:", uiFriendlyNumber(this.currentFootprint));
name += "-Time"; console.log(" Peak Footprint:", uiFriendlyNumber(this.peakFootprint));
console.log(this.name, `${name}:`, uiFriendlyDuration(value));
}
if (RAMification) {
console.log(this.name, "Current Footprint:", uiFriendlyNumber(this.currentFootprint));
console.log(this.name, "Peak Footprint:", uiFriendlyNumber(this.peakFootprint));
} }
console.log(" Wall-Time:", uiFriendlyDuration(this.endTime - this.startTime));
} }
}; };
@ -1289,22 +1167,6 @@ class GroupedBenchmark extends Benchmark {
results[subScore] = geomeanScore(results[subScore]); results[subScore] = geomeanScore(results[subScore]);
return results; return results;
} }
subTimes() {
const results = {};
for (const benchmark of this.benchmarks) {
let times = benchmark.subTimes();
for (let subTime in times) {
results[subTime] ??= [];
results[subTime].push(times[subTime]);
}
}
for (let subTimes in results)
results[subTimes] = sum(results[subTimes]);
return results;
}
}; };
class DefaultBenchmark extends Benchmark { class DefaultBenchmark extends Benchmark {
@ -1318,8 +1180,9 @@ class DefaultBenchmark extends Benchmark {
this.worstScore = null; this.worstScore = null;
this.averageTime = null; this.averageTime = null;
this.averageScore = null; this.averageScore = null;
if (this.worstCaseCount)
console.assert(this.iterations > this.worstCaseCount); console.assert(this.iterations > this.worstCaseCount);
console.assert(this.worstCaseCount >= 0);
} }
processResults(results) { processResults(results) {
@ -1333,29 +1196,24 @@ class DefaultBenchmark extends Benchmark {
for (let i = 0; i + 1 < results.length; ++i) for (let i = 0; i + 1 < results.length; ++i)
console.assert(results[i] >= results[i + 1]); console.assert(results[i] >= results[i + 1]);
const worstCase = []; if (this.worstCaseCount) {
for (let i = 0; i < this.worstCaseCount; ++i) const worstCase = [];
worstCase.push(results[i]); for (let i = 0; i < this.worstCaseCount; ++i)
this.worstTime = mean(worstCase); worstCase.push(results[i]);
this.worstScore = toScore(this.worstTime); this.worstTime = mean(worstCase);
this.worstScore = toScore(this.worstTime);
}
this.averageTime = mean(results); this.averageTime = mean(results);
this.averageScore = toScore(this.averageTime); this.averageScore = toScore(this.averageTime);
} }
subScores() { subScores() {
return { const scores = { "First": this.firstIterationScore }
"First": this.firstIterationScore, if (this.worstCaseCount)
"Worst": this.worstScore, scores["Worst"] = this.worstScore;
"Average": this.averageScore, if (this.iterations > 1)
}; scores["Average"] = this.averageScore;
} return scores;
subTimes() {
return {
"First": this.firstIterationTime,
"Worst": this.worstTime,
"Average": this.averageTime,
};
} }
} }
@ -1408,7 +1266,7 @@ class AsyncBenchmark extends DefaultBenchmark {
get runnerCode() { get runnerCode() {
return ` return `
async function doRun() { async function doRun() {
const benchmark = new Benchmark(${this.iterations}); const benchmark = new Benchmark(${JSON.stringify(this.benchmarkArguments)});
await benchmark.init?.(); await benchmark.init?.();
const results = []; const results = [];
const benchmarkName = "${this.name}"; const benchmarkName = "${this.name}";
@ -1521,13 +1379,6 @@ class WSLBenchmark extends Benchmark {
}`; }`;
} }
subTimes() {
return {
"Stdlib": this.stdlibTime,
"MainRun": this.mainRunTime,
};
}
subScores() { subScores() {
return { return {
"Stdlib": this.stdlibScore, "Stdlib": this.stdlibScore,
@ -1659,26 +1510,12 @@ class WasmLegacyBenchmark extends Benchmark {
return str; return str;
} }
subTimes() {
return {
"Startup": this.startupTime,
"Runtime": this.runTime,
};
}
subScores() { subScores() {
return { return {
"Startup": this.startupScore, "Startup": this.startupScore,
"Runtime": this.runScore, "Runtime": this.runScore,
}; };
} }
subTimes() {
return {
"Startup": this.startupTime,
"Runtime": this.runTime,
};
}
}; };
function dotnetPreloads(type) function dotnetPreloads(type)
@ -1924,7 +1761,7 @@ let BENCHMARKS = [
tags: ["Default", "Octane"], tags: ["Default", "Octane"],
}), }),
new DefaultBenchmark({ new DefaultBenchmark({
name: "typescript", name: "typescript-octane",
files: [ files: [
"./Octane/typescript-compiler.js", "./Octane/typescript-compiler.js",
"./Octane/typescript-input.js", "./Octane/typescript-input.js",
@ -1933,7 +1770,7 @@ let BENCHMARKS = [
iterations: 15, iterations: 15,
worstCaseCount: 2, worstCaseCount: 2,
deterministicRandom: true, deterministicRandom: true,
tags: ["Default", "Octane"], tags: ["Octane", "typescript"],
}), }),
// RexBench // RexBench
new DefaultBenchmark({ new DefaultBenchmark({
@ -2135,6 +1972,19 @@ let BENCHMARKS = [
], ],
tags: ["Default", "Proxy"], tags: ["Default", "Proxy"],
}), }),
new AsyncBenchmark({
name: "web-ssr",
files: [
"./web-ssr/benchmark.js",
],
preload: {
// Debug Sources for nicer profiling.
// BUNDLE_BLOB: "./web-ssr/dist/bundle.js",
BUNDLE_BLOB: "./web-ssr/dist/bundle.min.js",
},
tags: ["Default", "web", "ssr"],
iterations: 30,
}),
// Class fields // Class fields
new DefaultBenchmark({ new DefaultBenchmark({
name: "raytrace-public-class-fields", name: "raytrace-public-class-fields",
@ -2150,6 +2000,24 @@ let BENCHMARKS = [
], ],
tags: ["Default", "ClassFields"], tags: ["Default", "ClassFields"],
}), }),
new AsyncBenchmark({
name: "typescript-lib",
files: [
"./TypeScript/src/mock/sys.js",
"./TypeScript/dist/bundle.js",
"./TypeScript/benchmark.js",
],
preload: {
// Large test project:
// "tsconfig": "./TypeScript/src/gen/zod-medium/tsconfig.json",
// "files": "./TypeScript/src/gen/zod-medium/files.json",
"tsconfig": "./TypeScript/src/gen/immer-tiny/tsconfig.json",
"files": "./TypeScript/src/gen/immer-tiny/files.json",
},
iterations: 1,
worstCaseCount: 0,
tags: ["Default", "typescript"],
}),
// Generators // Generators
new AsyncBenchmark({ new AsyncBenchmark({
name: "async-fs", name: "async-fs",
@ -2209,18 +2077,6 @@ let BENCHMARKS = [
// now, and it was over-incentivizing inlining. // now, and it was over-incentivizing inlining.
tags: ["Wasm"], tags: ["Wasm"],
}), }),
new WasmEMCCBenchmark({
name: "tsf-wasm",
files: [
"./wasm/TSF/build/tsf.js",
"./wasm/TSF/benchmark.js",
],
preload: {
wasmBinary: "./wasm/TSF/build/tsf.wasm",
},
iterations: 50,
tags: ["Default", "Wasm"],
}),
new WasmEMCCBenchmark({ new WasmEMCCBenchmark({
name: "quicksort-wasm", name: "quicksort-wasm",
files: [ files: [
@ -2231,7 +2087,9 @@ let BENCHMARKS = [
wasmBinary: "./wasm/quicksort/build/quicksort.wasm", wasmBinary: "./wasm/quicksort/build/quicksort.wasm",
}, },
iterations: 50, iterations: 50,
tags: ["Default", "Wasm"], // No longer run by-default: We have more realistic Wasm workloads by
// now, and it was a small microbenchmark.
tags: ["Wasm"],
}), }),
new WasmEMCCBenchmark({ new WasmEMCCBenchmark({
name: "gcc-loops-wasm", name: "gcc-loops-wasm",
@ -2243,6 +2101,20 @@ let BENCHMARKS = [
wasmBinary: "./wasm/gcc-loops/build/gcc-loops.wasm", wasmBinary: "./wasm/gcc-loops/build/gcc-loops.wasm",
}, },
iterations: 50, iterations: 50,
// No longer run by-default: We have more realistic Wasm workloads by
// now, and it was a small microbenchmark.
tags: ["Wasm"],
}),
new WasmEMCCBenchmark({
name: "tsf-wasm",
files: [
"./wasm/TSF/build/tsf.js",
"./wasm/TSF/benchmark.js",
],
preload: {
wasmBinary: "./wasm/TSF/build/tsf.wasm",
},
iterations: 50,
tags: ["Default", "Wasm"], tags: ["Default", "Wasm"],
}), }),
new WasmEMCCBenchmark({ new WasmEMCCBenchmark({
@ -2280,19 +2152,6 @@ let BENCHMARKS = [
jsModule: "./Dart/build/flute.complex.dart2wasm.mjs", jsModule: "./Dart/build/flute.complex.dart2wasm.mjs",
wasmBinary: "./Dart/build/flute.complex.dart2wasm.wasm", wasmBinary: "./Dart/build/flute.complex.dart2wasm.wasm",
}, },
iterations: 30,
worstCaseCount: 2,
tags: ["Wasm"],
}),
new WasmEMCCBenchmark({
name: "Dart-flute-wasm",
files: [
"./Dart/benchmark.js",
],
preload: {
jsModule: "./Dart/build/flute.dart2wasm.mjs",
wasmBinary: "./Dart/build/flute.dart2wasm.wasm",
},
iterations: 15, iterations: 15,
worstCaseCount: 2, worstCaseCount: 2,
// Not run by default because the `CupertinoTimePicker` widget is very allocation-heavy, // Not run by default because the `CupertinoTimePicker` widget is very allocation-heavy,
@ -2335,6 +2194,56 @@ let BENCHMARKS = [
worstCaseCount: 2, worstCaseCount: 2,
tags: ["Default", "Wasm"], tags: ["Default", "Wasm"],
}), }),
new AsyncBenchmark({
name: "transformersjs-bert-wasm",
files: [
"./polyfills/fast-text-encoding/1.0.3/text.js",
"./transformersjs/benchmark.js",
"./transformersjs/task-bert.js",
],
preload: {
transformersJsModule: "./transformersjs/build/transformers.js",
onnxJsModule: "./transformersjs/build/onnxruntime-web/ort-wasm-simd-threaded.mjs",
onnxWasmBinary: "./transformersjs/build/onnxruntime-web/ort-wasm-simd-threaded.wasm",
modelWeights: "./transformersjs/build/models/Xenova/distilbert-base-uncased-finetuned-sst-2-english/onnx/model_uint8.onnx",
modelConfig: "./transformersjs/build/models/Xenova/distilbert-base-uncased-finetuned-sst-2-english/config.json",
modelTokenizer: "./transformersjs/build/models/Xenova/distilbert-base-uncased-finetuned-sst-2-english/tokenizer.json",
modelTokenizerConfig: "./transformersjs/build/models/Xenova/distilbert-base-uncased-finetuned-sst-2-english/tokenizer_config.json",
},
iterations: 30,
allowUtf16: true,
tags: ["Default", "Wasm", "transformersjs"],
}),
new AsyncBenchmark({
name: "transformersjs-whisper-wasm",
files: [
"./polyfills/fast-text-encoding/1.0.3/text.js",
"./transformersjs/benchmark.js",
"./transformersjs/task-whisper.js",
],
preload: {
transformersJsModule: "./transformersjs/build/transformers.js",
onnxJsModule: "./transformersjs/build/onnxruntime-web/ort-wasm-simd-threaded.mjs",
onnxWasmBinary: "./transformersjs/build/onnxruntime-web/ort-wasm-simd-threaded.wasm",
modelEncoderWeights: "./transformersjs/build/models/Xenova/whisper-tiny.en/onnx/encoder_model_quantized.onnx",
modelDecoderWeights: "./transformersjs/build/models/Xenova/whisper-tiny.en/onnx/decoder_model_merged_quantized.onnx",
modelConfig: "./transformersjs/build/models/Xenova/whisper-tiny.en/config.json",
modelTokenizer: "./transformersjs/build/models/Xenova/whisper-tiny.en/tokenizer.json",
modelTokenizerConfig: "./transformersjs/build/models/Xenova/whisper-tiny.en/tokenizer_config.json",
modelPreprocessorConfig: "./transformersjs/build/models/Xenova/whisper-tiny.en/preprocessor_config.json",
modelGenerationConfig: "./transformersjs/build/models/Xenova/whisper-tiny.en/generation_config.json",
inputFile: "./transformersjs/build/inputs/jfk.raw",
},
iterations: 5,
worstCaseCount: 1,
allowUtf16: true,
tags: ["Default", "Wasm", "transformersjs"],
}),
new WasmLegacyBenchmark({ new WasmLegacyBenchmark({
name: "tfjs-wasm", name: "tfjs-wasm",
files: [ files: [
@ -2651,9 +2560,53 @@ let BENCHMARKS = [
worstCaseCount: 2, worstCaseCount: 2,
tags: ["Default", "Wasm", "dotnet"], tags: ["Default", "Wasm", "dotnet"],
}), }),
// J2CL
new AsyncBenchmark({
name: "j2cl-box2d-wasm",
files: [
"./wasm/j2cl-box2d/benchmark.js",
"./wasm/j2cl-box2d/build/Box2dBenchmark_j2wasm_entry.js",
],
preload: {
wasmBinary: "./wasm/j2cl-box2d/build/Box2dBenchmark_j2wasm_binary.wasm",
},
iterations: 40,
tags: ["Default", "Wasm"],
}),
]; ];
const INTL_TESTS = [
"DateTimeFormat",
"ListFormat",
"RelativeTimeFormat",
"NumberFormat",
"PluralRules",
];
const INTL_BENCHMARKS = [];
for (const test of INTL_TESTS) {
const benchmark = new AsyncBenchmark({
name: `${test}-intl`,
files: [
"./intl/src/helper.js",
`./intl/src/${test}.js`,
"./intl/benchmark.js",
],
iterations: 2,
worstCaseCount: 1,
deterministicRandom: true,
tags: ["Javascript", "intl"],
});
INTL_BENCHMARKS.push(benchmark);
}
BENCHMARKS.push(
new GroupedBenchmark({
name: "intl",
tags: ["Javascript", "intl"],
}, INTL_BENCHMARKS));
// SunSpider tests // SunSpider tests
const SUNSPIDER_TESTS = [ const SUNSPIDER_TESTS = [
"3d-cube", "3d-cube",
@ -2821,8 +2774,8 @@ const defaultDisabledTags = [];
if (!isInBrowser) if (!isInBrowser)
defaultDisabledTags.push("WorkerTests"); defaultDisabledTags.push("WorkerTests");
if (globalThis.testList?.length) { if (JetStreamParams.testList.length) {
benchmarks = processTestList(globalThis.testList); benchmarks = processTestList(JetStreamParams.testList);
} else { } else {
benchmarks = findBenchmarksByTag("Default", defaultDisabledTags) benchmarks = findBenchmarksByTag("Default", defaultDisabledTags)
} }

2
TypeScript/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
node_modules
build/*.git

17
TypeScript/README.md Normal file
View file

@ -0,0 +1,17 @@
# TypeSCript test for JetStream
Measures the performance of running typescript on several framwork sources.
The build steps bundles sources from different frameworks:
- [jestjs](https://github.com/jestjs/jest.git)
- [zod](https://github.com/colinhacks/zod.git)
- [immer](https://github.com/immerjs/immer.git)
## Build Instructions
```bash
# install required node packages.
npm ci
# build the workload, output is ./dist
npm run build
```

41
TypeScript/benchmark.js Normal file
View file

@ -0,0 +1,41 @@
/*
* Copyright (C) 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
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Prevent typescript logging output.
console.log = () => {};
class Benchmark {
tsConfig;
fileData;
async init() {
this.tsConfig = JSON.parse(await JetStream.getString(JetStream.preload.tsconfig));
this.fileData = JSON.parse(await JetStream.getString(JetStream.preload.files));
}
runIteration() {
TypeScriptCompileTest.compileTest(this.tsConfig, this.fileData);
}
}

View file

@ -0,0 +1,198 @@
import fs from "fs";
import path from "path";
import { spawnSync } from "child_process";
import { globSync } from "glob";
import assert from 'assert/strict';
import { fileURLToPath } from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
class Importer {
constructor({ projectName, size, repoUrl, srcFolder, extraFiles, extraDirs }) {
this.projectName = projectName;
assert(projectName.endsWith(`-${size}`), "missing size annotation in projectName");
this.repoUrl = repoUrl;
this.baseDir = path.resolve(__dirname);
let repoName = path.basename(this.repoUrl);
if (!repoName.endsWith(".git")) {
repoName = `${repoName}.git`;
}
this.repoDir = path.resolve(__dirname, repoName);
this.srcFolder = srcFolder;
this.outputDir = path.resolve(__dirname, `../src/gen/${this.projectName}`);
fs.mkdirSync(this.outputDir, { recursive: true });
this.srcFileData = Object.create(null);
this.extraFiles = extraFiles;
this.extraDirs = extraDirs;
}
run() {
this.cloneRepo();
this.readSrcFileData();
this.addExtraFilesFromDirs();
this.addSpecificFiles();
this.writeTsConfig();
this.writeSrcFileData();
}
cloneRepo() {
if (fs.existsSync(this.repoDir)) return;
console.info(`Cloning src data repository to ${this.repoDir}`);
spawnSync("git", ["clone", this.repoUrl, this.repoDir]);
}
readSrcFileData() {
const patterns = [`${this.srcFolder}/**/*.ts`, `${this.srcFolder}/**/*.d.ts`, `${this.srcFolder}/*.d.ts`];
patterns.forEach(pattern => {
const files = globSync(pattern, { cwd: this.repoDir, nodir: true });
files.forEach(file => {
const filePath = path.join(this.repoDir, file);
const relativePath = path.relative(this.repoDir, filePath).toLowerCase();
const fileContents = fs.readFileSync(filePath, "utf8");
this.addFileContents(relativePath, fileContents);
});
});
}
addExtraFilesFromDirs() {
this.extraDirs.forEach(({ dir, nameOnly = false }) => {
const absoluteSourceDir = path.resolve(__dirname, dir);
let allFiles = globSync("**/*.d.ts", { cwd: absoluteSourceDir, nodir: true });
allFiles = allFiles.concat(globSync("**/*.d.mts", { cwd: absoluteSourceDir, nodir: true }));
allFiles.forEach(file => {
const filePath = path.join(absoluteSourceDir, file);
let relativePath = path.join(dir, path.relative(absoluteSourceDir, filePath));
if (nameOnly) {
relativePath = path.basename(relativePath);
}
this.addFileContents(relativePath, fs.readFileSync(filePath, "utf8"))
});
});
}
addFileContents(relativePath, fileContents) {
if (relativePath in this.srcFileData) {
if (this.srcFileData[relativePath] !== fileContents) {
throw new Error(`${relativePath} was previously added with different contents.`);
}
} else {
this.srcFileData[relativePath] = fileContents;
}
}
addSpecificFiles() {
this.extraFiles.forEach(file => {
const filePath = path.join(this.baseDir, file);
this.srcFileData[file] = fs.readFileSync(filePath, "utf8");
});
}
writeSrcFileData() {
const filesDataPath = path.join(this.outputDir, "files.json");
fs.writeFileSync(
filesDataPath,
JSON.stringify(this.srcFileData, null, 2)
);
const stats = fs.statSync(filesDataPath);
const fileSizeInKiB = (stats.size / 1024) | 0;
console.info(`Exported ${this.projectName}`);
console.info(` File Contents: ${path.relative(process.cwd(), filesDataPath)}`);
console.info(` Total Size: ${fileSizeInKiB} KiB`);
}
writeTsConfig() {
const tsconfigInputPath = path.join(this.repoDir, "tsconfig.json");
const mergedTsconfig = this.loadAndMergeTsconfig(tsconfigInputPath);
const tsconfigOutputPath = path.join(this.outputDir, "tsconfig.json");
fs.writeFileSync(
tsconfigOutputPath,
JSON.stringify(mergedTsconfig, null, 2)
);
}
loadAndMergeTsconfig(configPath) {
const tsconfigContent = fs.readFileSync(configPath, "utf8");
const tsconfigContentWithoutComments = tsconfigContent.replace(/(?:^|\s)\/\/.*$|\/\*[\s\S]*?\*\//gm, "");
const tsconfig = JSON.parse(tsconfigContentWithoutComments);
let baseConfigPath = tsconfig.extends;
if (!baseConfigPath) return tsconfig;
if (!baseConfigPath.startsWith('./') && !baseConfigPath.startsWith('../')) return tsconfig;
baseConfigPath = path.resolve(path.dirname(configPath), baseConfigPath);
const baseConfig = this.loadAndMergeTsconfig(baseConfigPath);
const mergedConfig = { ...baseConfig, ...tsconfig };
if (baseConfig.compilerOptions && tsconfig.compilerOptions) {
mergedConfig.compilerOptions = { ...baseConfig.compilerOptions, ...tsconfig.compilerOptions };
}
delete mergedConfig.extends;
return mergedConfig;
}
}
const jest = new Importer({
projectName: "jestjs-large",
size: "large",
repoUrl: "https://github.com/jestjs/jest.git",
srcFolder: "packages",
extraFiles: [
"../../node_modules/@babel/types/lib/index.d.ts",
"../../node_modules/callsites/index.d.ts",
"../../node_modules/camelcase/index.d.ts",
"../../node_modules/chalk/types/index.d.ts",
"../../node_modules/execa/index.d.ts",
"../../node_modules/fast-json-stable-stringify/index.d.ts",
"../../node_modules/get-stream/index.d.ts",
"../../node_modules/strip-json-comments/index.d.ts",
"../../node_modules/tempy/index.d.ts",
"../../node_modules/tempy/node_modules/type-fest/index.d.ts",
"../node_modules/@jridgewell/trace-mapping/types/trace-mapping.d.mts",
"../node_modules/@types/eslint/index.d.ts",
"../node_modules/ansi-regex/index.d.ts",
"../node_modules/ansi-styles/index.d.ts",
"../node_modules/glob/dist/esm/index.d.ts",
"../node_modules/jest-worker/build/index.d.ts",
"../node_modules/lru-cache/dist/esm/index.d.ts",
"../node_modules/minipass/dist/esm/index.d.ts",
"../node_modules/p-limit/index.d.ts",
"../node_modules/path-scurry/dist/esm/index.d.ts",
"../node_modules/typescript/lib/lib.dom.d.ts",
],
extraDirs: [
{ dir: "../node_modules/@types/" },
{ dir: "../node_modules/typescript/lib/", nameOnly: true },
{ dir: "../node_modules/jest-worker/build/" },
{ dir: "../node_modules/@jridgewell/trace-mapping/types/" },
{ dir: "../node_modules/minimatch/dist/esm/" },
{ dir: "../node_modules/glob/dist/esm/" },
{ dir: "../../node_modules/tempy/node_modules/type-fest/source/" }
],
});
const zod = new Importer({
projectName: "zod-medium",
size: "medium",
repoUrl: "https://github.com/colinhacks/zod.git",
srcFolder: "packages",
extraFiles: [],
extraDirs: [
{ dir: "../node_modules/typescript/lib/", nameOnly: true },
],
});
const immer =new Importer({
projectName: "immer-tiny",
size: "tiny",
repoUrl: "https://github.com/immerjs/immer.git",
srcFolder: "src",
extraFiles: [],
extraDirs: [
{ dir: "../node_modules/typescript/lib/", nameOnly: true },
],
});
// Skip jest since it produces a hugh in-memory FS.
// jest.run();
zod.run();
immer.run();

7
TypeScript/dist/bundle.js vendored Normal file

File diff suppressed because one or more lines are too long

1
TypeScript/dist/bundle.js.map vendored Normal file

File diff suppressed because one or more lines are too long

2194
TypeScript/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

29
TypeScript/package.json Normal file
View file

@ -0,0 +1,29 @@
{
"name": "typescript-compile",
"version": "1.0.0",
"description": "A demo project to evaluate TypeScript performance.",
"main": "index.js",
"scripts": {
"test": "node ./src/benchmark-node.mjs",
"clean": "rm -rf dist/* && rm -rf src/gen/*.json",
"import": "node ./build/import-src-project.mjs",
"prebuild": "npm run clean && npm run import",
"build": "webpack",
"build:dev": "npm run prebuild && webpack --mode development"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@dapplets/unicode-escape-webpack-plugin": "^0.1.1",
"@types/node": "^24.2.0",
"ansi-styles": "^6.2.1",
"glob": "^11.0.3",
"path-browserify": "^1.0.1",
"typescript": "^5.9.2",
"undici-types": "^7.13.0",
"util": "^0.12.5",
"webpack": "^5.101.0",
"webpack-cli": "^6.0.1"
}
}

View file

@ -0,0 +1,14 @@
import { compileTest } from "./test.mjs";
import tsConfig from "./gen/immer-tiny/tsconfig.json" with { type: "json" };
import srcFileData from "./gen/immer-tiny/files.json" with { type: "json" };
console.log("Starting TypeScript in-memory compilation benchmark...");
const startTime = performance.now();
compileTest(tsConfig, srcFileData);
const endTime = performance.now();
const duration = (endTime - startTime) / 1000;
console.log(`TypeScript compilation finished.`);
console.log(`Compilation took ${duration.toFixed(2)} seconds.`);

View file

@ -0,0 +1,4 @@
Auto-generated path-to-file-content mapping for an in-memory filesystem
consumed by TypeScript.
See ../../build/import-src-project.mjs for more details.

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "ES2020",
"strict": true,
"declaration": true,
"importHelpers": false,
"noImplicitAny": true,
"esModuleInterop": true,
"noUnusedLocals": false,
"sourceMap": true,
"declarationMap": true,
"noEmit": true,
"module": "ES6",
"allowJs": true
},
"files": [
"./src/immer.ts",
"./src/plugins/mapset.ts",
"./src/plugins/patches.ts",
"src/types/globals.d.ts"
]
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,49 @@
{
"compilerOptions": {
"target": "es2020",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"module": "nodenext",
"moduleResolution": "nodenext",
"jsx": "react",
"strict": true,
"alwaysStrict": true,
"noUncheckedIndexedAccess": false,
"exactOptionalPropertyTypes": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitOverride": true,
"noFallthroughCasesInSwitch": true,
"resolveJsonModule": true,
"removeComments": false,
"esModuleInterop": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"downlevelIteration": true,
"isolatedModules": true,
"pretty": true,
"customConditions": [
"@zod/source"
],
"types": [
"node"
]
},
"exclude": [
"node_modules"
],
"include": [
"***.mts",
"***.d.ts",
"***.d.mts",
"packages/docs"
]
}

View file

@ -0,0 +1,33 @@
function fileExists(path) {
return false;
}
function readFile(path, encoding) {
return undefined;
}
var sys = {
args: [],
newLine: "\n",
useCaseSensitiveFileNames: true,
write: function (s) {
},
writeOutputIsTTY: function () {
return false;
},
readFile: readFile,
fileExists: fileExists,
exit: function (exitCode) {
},
getExecutingFilePath: function () {
return "./";
},
getCurrentDirectory: function () {
return "./";
},
getModifiedTime: function (path) {
return undefined;
},
createDirectory: function (path) {
},
deleteFile: function (path) {
},
};

104
TypeScript/src/test.mjs Normal file
View file

@ -0,0 +1,104 @@
import ts from "typescript";
const REPO_ROOT = "/User/test/JetStream/";
class CompilerHost {
constructor(options, srcFileData) {
this.options = options;
this.srcFileData = srcFileData;
this.outFileData = Object.create(null);
}
getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile) {
const fileContent = this.readFile(fileName);
return ts.createSourceFile(fileName, fileContent, languageVersion);
}
resolveModuleNames(moduleNames, containingFile) {
const resolvedModules = [];
for (const moduleName of moduleNames) {
const result = ts.resolveModuleName(
moduleName,
containingFile,
this.options,
this
);
if (result.resolvedModule) {
resolvedModules.push(result.resolvedModule);
} else {
resolvedModules.push(undefined);
}
}
return resolvedModules;
}
getDefaultLibFileName() {
return "lib.d.ts";
}
getCurrentDirectory() {
return "";
}
getCanonicalFileName(fileName) {
return fileName.toLowerCase();
}
useCaseSensitiveFileNames() {
return true;
}
getNewLine() {
return "\n";
}
fileExists(filePath) {
return filePath in this.srcFileData;
}
readFile(filePath) {
const fileContent = this.srcFileData[filePath.toLowerCase()];
if (fileContent === undefined) {
throw new Error(`"${filePath}" does not exist.`);
}
return fileContent;
}
writeFile(fileName, data, writeByteOrderMark) {
this.outFileData[fileName] = data;
}
}
export function compileTest(tsConfig, srcFileData) {
const options = ts.convertCompilerOptionsFromJson(
tsConfig.compilerOptions,
REPO_ROOT
).options;
options.lib = [...(options.lib || []), "dom"];
const host = new CompilerHost(options, srcFileData);
const program = ts.createProgram(Object.keys(srcFileData), options, host);
const emitResult = program.emit();
const diagnostics = formatDiagnostics(program, emitResult);
return {
diagnostics,
resultFilesCount: Object.keys(host.outFileData).length,
};
}
function formatDiagnostics(program, emitResult) {
const allDiagnostics = ts
.getPreEmitDiagnostics(program)
.concat(emitResult.diagnostics);
if (allDiagnostics.length > 0) {
console.log(`Found ${allDiagnostics.length} errors:`);
}
const formattedDiagnostics = allDiagnostics.map((diagnostic) => {
if (diagnostic.file) {
const { line, character } = ts.getLineAndCharacterOfPosition(
diagnostic.file,
diagnostic.start
);
const message = ts.flattenDiagnosticMessageText(
diagnostic.messageText,
"\n"
);
return `${diagnostic.file.fileName} (${line + 1},${
character + 1
}): ${message}`;
} else {
return ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
}
});
formattedDiagnostics.slice(0, 20).map((each) => console.log(each));
return formattedDiagnostics;
}

View file

@ -0,0 +1,45 @@
import path from "path";
import { fileURLToPath } from "url";
import TerserPlugin from "terser-webpack-plugin";
import UnicodeEscapePlugin from "@dapplets/unicode-escape-webpack-plugin";
import * as PathBrowserify from "path-browserify";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const commonConfig = {
mode: "production",
devtool: "source-map",
target: "web",
entry: path.resolve(__dirname, "src/test.mjs"),
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
library: {
name: "TypeScriptCompileTest",
type: "globalThis",
},
libraryTarget: "assign",
},
plugins: [
new UnicodeEscapePlugin({
test: /\.(js|jsx|ts|tsx)$/, // Escape Unicode in JavaScript and TypeScript files
}),
],
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
keep_fnames: true,
},
}),
],
},
resolve: {
fallback: {
"path": "path-browserify",
"fs": false,
},
},
};
export default [commonConfig];

172
cli.js
View file

@ -21,48 +21,108 @@
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. * THE POSSIBILITY OF SUCH DAMAGE.
*/ */
load("./shell-config.js") load("./shell-config.js");
const cliFlags = { __proto__: null }; const CLI_PARAMS = {
__proto__: null,
help: { help: "Print this help message.", param: "help" },
"iteration-count": {
help: "Set the default iteration count.",
param: "testIterationCount",
},
"worst-case-count": {
help: "Set the default worst-case count.",
param: "testWorstCaseCount",
},
"dump-json-results": {
help: "Print summary json to the console.",
param: "dumpJSONResults",
},
"dump-test-list": {
help: "Print test list instead of running.",
param: "dumpTestList",
},
ramification: {
help: "Enable ramification support. See RAMification.py for more details.",
param: "RAMification",
},
"no-prefetch": {
help: "Do not prefetch resources. Will add network overhead to measurements!",
param: "prefetchResources",
},
test: {
help: "Run a specific test or comma-separated list of tests.",
param: "test",
},
tag: {
help: "Run tests with a specific tag or comma-separated list of tags.",
param: "tag",
},
"start-automatically": {
help: "Start the benchmark automatically.",
param: "startAutomatically",
},
report: { help: "Report results to a server.", param: "shouldReport" },
"start-delay": {
help: "Delay before starting the benchmark.",
param: "startDelay",
},
"custom-pre-iteration-code": {
help: "Custom code to run before each iteration.",
param: "customPreIterationCode",
},
"custom-post-iteration-code": {
help: "Custom code to run after each iteration.",
param: "customPostIterationCode",
},
};
const cliParams = new Map();
const cliArgs = []; const cliArgs = [];
if (globalThis.arguments?.length) { if (globalThis.arguments?.length) {
for (const argument of globalThis.arguments) for (const argument of globalThis.arguments) {
if (argument.startsWith("--")) { if (argument.startsWith("--")) {
const parts = argument.split("="); parseCliFlag(argument);
cliFlags[parts[0].toLowerCase()] = parts.slice(1).join("="); } else {
} else cliArgs.push(argument);
cliArgs.push(argument); }
}
} }
function getIntFlag(flags, flag) { function parseCliFlag(argument) {
if (!(flag in flags)) const parts = argument.slice(2).split("=");
return undefined; const flagName = parts[0];
const rawValue = flags[flag]; if (!(flagName in CLI_PARAMS)) {
const value = parseInt(rawValue); const message =`Unknown flag: '--${flagName}'`;
if (value <= 0) help(message);
throw new Error(`Expected positive value for ${flag}, but got ${rawValue}`); throw Error(message);
return value; }
let value = parts.slice(1).join("=");
if (flagName === "no-prefetch") value = "false";
else if (value === "") value = "true";
cliParams.set(CLI_PARAMS[flagName].param, value);
} }
if ("--iteration-count" in cliFlags)
globalThis.testIterationCount = getIntFlag(cliFlags, "--iteration-count"); if (cliArgs.length) {
if ("--worst-case-count" in cliFlags) let tests = cliParams.has("test") ? cliParams.get("tests").split(",") : []
globalThis.testWorstCaseCount = getIntFlag(cliFlags, "--worst-case-count"); tests = tests.concat(cliArgs);
if ("--dump-json-results" in cliFlags) cliParams.set("test", tests.join(","));
globalThis.dumpJSONResults = true; }
if (typeof runMode !== "undefined" && runMode == "RAMification")
globalThis.RAMification = true; if (cliParams.size)
if ("--ramification" in cliFlags) globalThis.JetStreamParamsSource = cliParams;
globalThis.RAMification = true;
if ("--no-prefetch" in cliFlags) load("./params.js");
globalThis.prefetchResources = false;
if (cliArgs.length)
globalThis.testList = cliArgs;
async function runJetStream() { async function runJetStream() {
if (!JetStreamParams.isDefault) {
console.warn(`Using non standard params: ${JSON.stringify(JetStreamParams, null, 2)}`)
}
try { try {
await JetStream.initialize(); await JetStream.initialize();
await JetStream.start(); await JetStream.start();
@ -75,31 +135,39 @@ async function runJetStream() {
load("./JetStreamDriver.js"); load("./JetStreamDriver.js");
if ("--help" in cliFlags) { function help(message=undefined) {
console.log("JetStream Driver Help"); if (message)
console.log(message);
else
console.log("JetStream Driver Help");
console.log(""); console.log("");
console.log("Options:"); console.log("Options:");
console.log(" --iteration-count: Set the default iteration count."); for (const [flag, { help }] of Object.entries(CLI_PARAMS))
console.log(" --worst-case-count: Set the default worst-case count"); console.log(` --${flag.padEnd(20)} ${help}`);
console.log(" --dump-json-results: Print summary json to the console.");
console.log(" --dump-test-list: Print test list instead of running.");
console.log(" --ramification: Enable ramification support. See RAMification.py for more details.");
console.log(" --no-prefetch: Do not prefetch resources. Will add network overhead to measurements!");
console.log(""); console.log("");
console.log("Available tags:"); // If we had an early bailout during flag parsing we won't have
const tagNames = Array.from(benchmarksByTag.keys()).sort(); // JetStreamDriver.js loaded yet and thus none of the helper globals here
for (const tagName of tagNames) // have been defined yet.
console.log(" ", tagName); if (typeof benchmarksByTag !== "undefined") {
console.log(""); console.log("Available tags:");
const tagNames = Array.from(benchmarksByTag.keys()).sort();
for (const tagName of tagNames) console.log(" ", tagName);
console.log("");
}
console.log("Available tests:"); if (typeof BENCHMARKS !== "undefined") {
const benchmarkNames = BENCHMARKS.map(b => b.name).sort(); console.log("Available tests:");
for (const benchmark of benchmarkNames) const benchmarkNames = BENCHMARKS.map((b) => b.name).sort();
console.log(" ", benchmark); for (const benchmark of benchmarkNames) console.log(" ", benchmark);
} else if ("--dump-test-list" in cliFlags) { }
JetStream.dumpTestList(); }
} else {
runJetStream(); if (cliParams.has("help")) {
help();
} else if (cliParams.has("dumpTestList")) {
JetStream.dumpTestList();
} else {
runJetStream();
} }

View file

@ -36,6 +36,7 @@
const isInBrowser = true; const isInBrowser = true;
const isD8 = false; const isD8 = false;
const isSpiderMonkey = false; const isSpiderMonkey = false;
globalThis.JetStreamParamsSource = new URLSearchParams(globalThis?.location?.search);
globalThis.allIsGood = true; globalThis.allIsGood = true;
window.onerror = function(e) { window.onerror = function(e) {
if (e == "Script error.") { if (e == "Script error.") {
@ -65,6 +66,7 @@
} }
</script> </script>
<script src="params.js"></script>
<script src="JetStreamDriver.js"></script> <script src="JetStreamDriver.js"></script>
</head> </head>

55
intl/benchmark.js Normal file
View file

@ -0,0 +1,55 @@
/*
* Copyright (C) 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
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
class Benchmark {
iterationCount;
verbose;
lastResult;
totalLength = 0;
expectedMinLength = 0;
constructor({ iterationCount, verbose = false } = {}) {
this.iterationCount = iterationCount;
this.verbose = verbose;
}
runIteration() {
// See implementations in src/.
const { lastResult, totalLength, expectedMinLength } = runTest(
this.verbose
);
this.lastResult = lastResult;
this.totalLength += totalLength;
this.expectedMinLength = expectedMinLength;
}
validate() {
const expectedMinTotalLength = this.expectedMinLength * this.iterationCount;
console.assert(
this.totalLength >= expectedMinTotalLength,
`Invalid totalLength = ${this.totalLength}, expected >= ${expectedMinTotalLength}`
);
}
}

View file

@ -0,0 +1,77 @@
function generateRandomDates(count) {
const firstDate = new Date(1800, 11, 5, 13, 6);
let currentTimeStamp = firstDate.getTime();
const dates = [];
for (let i = 0; i < count; i++) {
dates.push(new Date(currentTimeStamp));
currentTimeStamp += 1234569;
}
return dates;
}
const DATE_STYLE_OPTIONS = ["full", "long", "medium", "short"];
const TIME_STYLE_OPTIONS = ["full", "long", "medium", "short"];
function* dateTimeFormatOptions() {
for (const locale of LOCALES) {
for (const dateStyle of DATE_STYLE_OPTIONS) {
for (const timeStyle of TIME_STYLE_OPTIONS) {
yield { locale, dateStyle, timeStyle };
}
}
}
}
function runTest(verbose = false) {
let totalLength = 0;
let lastFormatResult;
let lastFormatPartResult;
let lastFormatRangeResult;
const dates = generateRandomDates(100);
let dateIndex = 0;
const FORMAT_COUNT = 17;
const FORMAT_RANGE_COUNT = 7;
for (const { locale, dateStyle, timeStyle } of shuffleOptions(
dateTimeFormatOptions
)) {
const options = { dateStyle, timeStyle };
if (verbose) {
console.log(locale, JSON.stringify(options));
}
const formatter = new Intl.DateTimeFormat(locale, options);
for (let i = 0; i < FORMAT_COUNT; i++) {
let date = dates[dateIndex % dates.length];
lastFormatResult = formatter.format(date);
totalLength += lastFormatResult.length;
if (verbose) {
console.log(date, lastFormatResult);
}
dateIndex++;
date = dates[dateIndex % dates.length];
lastFormatPartResult = formatter.formatToParts(date);
for (const part of lastFormatPartResult) {
totalLength += part.value.length;
}
dateIndex++;
}
let dateRangeStart = dates[0];
for (let i = 0; i < FORMAT_RANGE_COUNT; i++) {
const date = dates[dateIndex % dates.length];
if (dateRangeStart < date) {
lastFormatRangeResult = formatter.formatRange(dateRangeStart, date);
if (verbose) {
console.log(dateRangeStart, date, lastFormatRangeResult);
}
}
dateRangeStart = date;
}
}
return {
lastResult: lastFormatResult + lastFormatRangeResult,
totalLength,
expectedMinLength: 438_000,
};
}

47
intl/src/ListFormat.js Normal file
View file

@ -0,0 +1,47 @@
const LISTS = [
["One"],
["1", "2"],
["Motorcycle", "Bus", "Car"],
LOCALES,
new Array(100).fill(9).map((_, index) => index.toString()),
];
function* listOptions() {
const styleOptions = ["long", "short", "narrow"];
const typeOptions = ["conjunction", "disjunction", "unit"];
for (const locale of LOCALES) {
for (const style of styleOptions) {
for (const type of typeOptions) {
yield { locale, style, type };
}
}
}
}
function runTest(verbose = false) {
let lastResult;
let totalLength = 0;
const LIST_FORMAT_COUNT = 10;
let listIndex = 0;
for (const { locale, style, type } of shuffleOptions(listOptions)) {
const options = { style, type };
if (verbose) {
console.log(locale, JSON.stringify(options));
}
const formatter = new Intl.ListFormat(locale, options);
for (let i = 0; i < LIST_FORMAT_COUNT; i++) {
const list = LISTS[listIndex % LISTS.length];
listIndex++;
lastResult = formatter.format(list);
totalLength += lastResult.length;
const formatPartsResult = formatter.formatToParts(list);
if (verbose) {
console.log(value, lastResult);
}
for (const part of formatPartsResult) {
totalLength += part.value.length;
}
}
}
return { lastResult, totalLength, expectedMinLength: 506_000 };
}

96
intl/src/NumberFormat.js Normal file
View file

@ -0,0 +1,96 @@
const CURRENCIES = ["USD", "EUR", "JPY", "INR", "NGN"];
const NUMBER_UNITS = [
"acre",
"bit",
"byte",
"celsius",
"centimeter",
"day",
"degree",
"fahrenheit",
"fluid-ounce",
"foot",
"gallon",
"gigabit",
"gigabyte",
"gram",
"hectare",
"hour",
"inch",
"kilobit",
"kilobyte",
"kilogram",
"kilometer",
"liter",
"megabit",
"megabyte",
"meter",
"microsecond",
"mile",
"mile-scandinavian",
"milliliter",
"millimeter",
"millisecond",
"minute",
"month",
"nanosecond",
"ounce",
"percent",
"petabyte",
"pound",
"second",
"stone",
"terabit",
"terabyte",
"week",
"yard",
"year",
];
function* numberFormatOptions() {
const currencyDisplayOptions = ["symbol", "narrowSymbol", "code", "name"];
const unitDisplayOptions = ["short", "long", "narrow"];
for (const locale of LOCALES) {
for (const currency of CURRENCIES) {
for (const currencyDisplay of currencyDisplayOptions) {
yield { locale, style: "currency", currency, currencyDisplay };
}
}
for (const unit of NUMBER_UNITS.slice(0, 20)) {
for (const unitDisplay of unitDisplayOptions) {
yield { locale, style: "unit", unit, unitDisplay };
}
}
yield { locale, style: "decimal" };
yield { locale, style: "percent" };
}
}
function runTest(verbose = false) {
let lastResult;
let totalLength = 0;
const NUMBER_FORMAT_COUNT = 10;
let counter = 1;
for (const options of shuffleOptions(numberFormatOptions).slice(0, 200)) {
const formatter = new Intl.NumberFormat(options.locale, options);
if (verbose) {
console.log(options.locale, JSON.stringify(options));
}
for (let i = 0; i < NUMBER_FORMAT_COUNT; i++) {
counter += 599;
const value = counter % 10_000;
lastResult = formatter.format(value);
if (verbose) {
console.log(value, lastResult);
}
totalLength += lastResult.length;
const formatPartsResult = formatter.formatToParts(value);
for (const part of formatPartsResult) {
totalLength += part.value.length;
}
}
}
return { lastResult, totalLength, expectedMinLength: 40_000 };
}

38
intl/src/PluralRules.js Normal file
View file

@ -0,0 +1,38 @@
function* pluralRulesOptions() {
const typeOptions = ["cardinal", "ordinal"];
for (const locale of LOCALES) {
for (const type of typeOptions) {
yield { locale, type };
}
}
}
function runTest(verbose = false) {
let lastResult;
let totalLength = 0;
const PLURAL_RULES_COUNT = 1000;
for (const { locale, type } of shuffleOptions(pluralRulesOptions)) {
if (verbose) {
console.log(locale, type);
}
const formatter = new Intl.PluralRules(locale, { type });
let i = 0;
for (let value = 0; value < 4; value++) {
lastResult = formatter.select(value);
totalLength += lastResult.length;
if (verbose) {
console.log(value, lastResult);
}
i++;
}
for (; i < PLURAL_RULES_COUNT; i++) {
const value = Math.floor(Math.random() * 1000);
lastResult = formatter.select(value);
totalLength += lastResult.length;
if (verbose) {
console.log(value, lastResult);
}
}
}
return { lastResult, totalLength, expectedMinLength: 244_000 };
}

View file

@ -0,0 +1,53 @@
const UNITS = [
"year",
"quarter",
"month",
"week",
"day",
"hour",
"minute",
"second",
];
function* relativeTimeFormatOptions() {
const styleOptions = ["long", "short", "narrow"];
const numericOptions = ["always", "auto"];
for (const locale of LOCALES) {
for (const style of styleOptions) {
for (const numeric of numericOptions) {
yield { locale, style, numeric };
}
}
}
}
function runTest(verbose = false) {
let lastResult;
let totalLength = 0;
const RELATIVE_TIME_FORMAT_COUNT = 100;
let unitIndex = 0;
for (const { locale, style, numeric } of shuffleOptions(
relativeTimeFormatOptions
)) {
const options = { style, numeric };
if (verbose) {
console.log(locale, JSON.stringify(options));
}
const formatter = new Intl.RelativeTimeFormat(locale, options);
for (let i = 0; i < RELATIVE_TIME_FORMAT_COUNT; i++) {
const unit = UNITS[unitIndex % UNITS.length];
unitIndex++;
const value = Math.random() * 100 - 50;
lastResult = formatter.format(value, unit);
if (verbose) {
console.log(value, unit, lastResult);
}
totalLength += lastResult.length;
const formatPartsResult = formatter.formatToParts(value, unit);
for (const part of formatPartsResult) {
totalLength += part.value.length;
}
}
}
return { lastResult, totalLength, expectedMinLength: 432_000 };
}

39
intl/src/helper.js Normal file
View file

@ -0,0 +1,39 @@
function shuffleOptions(optionsGenerator) {
const options = Array.from(optionsGenerator());
for (let i = options.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[options[i], options[j]] = [options[j], options[i]];
}
return options;
}
const LOCALES = [
"ar-SA",
"zh-CN",
"zh-TW",
"da-DK",
"en-US",
"en-GB",
"en-CA",
"en-AU",
"fr-FR",
"fr-CA",
"de-DE",
"hi-IN",
"it-IT",
"ja-JP",
"ko-KR",
"pt-BR",
"pt-PT",
"ru-RU",
"es-ES",
"es-MX",
"sw-KE",
"sv-SE",
"th-TH",
"tr-TR",
"vi-VN",
];
globalThis.console ??= {};
console.log ??= (...args) => print(...args);

9
package-lock.json generated
View file

@ -8,6 +8,9 @@
"name": "jetstream", "name": "jetstream",
"version": "3.0.0-alpha", "version": "3.0.0-alpha",
"license": "SEE LICENSE IN LICENSE", "license": "SEE LICENSE IN LICENSE",
"dependencies": {
"@dapplets/unicode-escape-webpack-plugin": "^0.1.1"
},
"devDependencies": { "devDependencies": {
"@actions/core": "^1.11.1", "@actions/core": "^1.11.1",
"@babel/core": "^7.21.3", "@babel/core": "^7.21.3",
@ -471,6 +474,12 @@
"integrity": "sha512-1uLNT5NZsUVIGS4syuHwTzZ8HycMPyr6POA3FCE4GbMtc4rhoJk8aZKtNIRthJYfL+iioppi+rTfH3olMPr9nA==", "integrity": "sha512-1uLNT5NZsUVIGS4syuHwTzZ8HycMPyr6POA3FCE4GbMtc4rhoJk8aZKtNIRthJYfL+iioppi+rTfH3olMPr9nA==",
"dev": true "dev": true
}, },
"node_modules/@dapplets/unicode-escape-webpack-plugin": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@dapplets/unicode-escape-webpack-plugin/-/unicode-escape-webpack-plugin-0.1.1.tgz",
"integrity": "sha512-dBcrCWE6ZOOHD1XLe3raXk29CwQSJQCXYx8QNcCMvfG+cZ9tZh1h2OVV7D936pmues8OTm1KhjWeiZXbH30SAg==",
"license": "MIT"
},
"node_modules/@eslint-community/eslint-utils": { "node_modules/@eslint-community/eslint-utils": {
"version": "4.4.1", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",

View file

@ -44,5 +44,8 @@
"local-web-server": "^5.4.0", "local-web-server": "^5.4.0",
"prettier": "^2.8.3", "prettier": "^2.8.3",
"selenium-webdriver": "^4.35.0" "selenium-webdriver": "^4.35.0"
},
"dependencies": {
"@dapplets/unicode-escape-webpack-plugin": "^0.1.1"
} }
} }

146
params.js Normal file
View file

@ -0,0 +1,146 @@
"use strict";
/*
* Copyright (C) 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
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
class Params {
// Enable a detailed developer menu to change the current Params.
developerMode = false;
startAutomatically = false;
shouldReport = false;
startDelay = undefined;
testList = [];
testIterationCount = undefined;
testWorstCaseCount = undefined;
prefetchResources = true;
RAMification = false;
dumpJSONResults = false;
testIterationCountMap = new Map();
testWorstCaseCountMap = new Map();
customPreIterationCode = undefined;
customPostIterationCode = undefined;
constructor(sourceParams = undefined) {
if (sourceParams)
this._copyFromSearchParams(sourceParams);
if (!this.developerMode)
Object.freeze(this);
}
_copyFromSearchParams(sourceParams) {
this.startAutomatically = this._parseBooleanParam(sourceParams, "startAutomatically");
this.developerMode = this._parseBooleanParam(sourceParams, "developerMode");
this.shouldReport = this._parseBooleanParam(sourceParams, "report");
this.prefetchResources = this._parseBooleanParam(sourceParams, "prefetchResources");
this.RAMification = this._parseBooleanParam(sourceParams, "RAMification");
this.dumpJSONResults = this._parseBooleanParam(sourceParams, "dumpJSONResults");
this.customPreIterationCode = this._parseStringParam(sourceParams, "customPreIterationCode");
this.customPostIterationCode = this._parseStringParam(sourceParams, "customPostIterationCode");
this.startDelay = this._parseIntParam(sourceParams, "startDelay", 0);
if (this.shouldReport && !this.startDelay)
this.startDelay = 4000;
for (const paramKey of ["tag", "tags", "test", "tests"])
this.testList = this._parseTestListParam(sourceParams, paramKey);
this.testIterationCount = this._parseIntParam(sourceParams, "iterationCount", 1);
this.testWorstCaseCount = this._parseIntParam(sourceParams, "worstCaseCount", 1);
const unused = Array.from(sourceParams.keys());
if (unused.length > 0)
console.error("Got unused source params", unused);
}
_parseTestListParam(sourceParams, key) {
if (!sourceParams.has(key))
return this.testList;
let testList = [];
if (sourceParams?.getAll) {
testList = sourceParams?.getAll(key);
} else {
// fallback for cli sourceParams which is just a Map;
testList = sourceParams.get(key).split(",");
}
sourceParams.delete(key);
if (this.testList.length > 0 && testList.length > 0)
throw new Error(`Overriding previous testList='${this.testList.join()}' with ${key} url-parameter.`);
return testList;
}
_parseStringParam(sourceParams, paramKey) {
if (!sourceParams.has(paramKey))
return DefaultJetStreamParams[paramKey];
const value = sourceParams.get(paramKey);
sourceParams.delete(paramKey);
return value;
}
_parseBooleanParam(sourceParams, paramKey) {
if (!sourceParams.has(paramKey))
return DefaultJetStreamParams[paramKey];
const value = sourceParams.get(paramKey).toLowerCase();
sourceParams.delete(paramKey);
return !(value === "false" || value === "0");
}
_parseIntParam(sourceParams, paramKey, minValue) {
if (!sourceParams.has(paramKey))
return DefaultJetStreamParams[paramKey];
const parsedValue = this._parseInt(sourceParams.get(paramKey), paramKey);
if (parsedValue < minValue)
throw new Error(`Invalid ${paramKey} param: '${parsedValue}', value must be >= ${minValue}.`);
sourceParams.delete(paramKey);
return parsedValue;
}
_parseInt(value, errorMessage) {
const number = Number(value);
if (!Number.isInteger(number) && errorMessage)
throw new Error(`Invalid ${errorMessage} param: '${value}', expected int.`);
return parseInt(number);
}
get isDefault() {
return this === DefaultJetStreamParams;
}
}
const DefaultJetStreamParams = new Params();
let maybeCustomParams = DefaultJetStreamParams;
if (globalThis?.JetStreamParamsSource) {
try {
maybeCustomParams = new Params(globalThis?.JetStreamParamsSource);
} catch (e) {
console.error("Invalid Params", e, "\nUsing defaults as fallback:", maybeCustomParams);
}
}
const JetStreamParams = maybeCustomParams;

View file

@ -1,163 +0,0 @@
#! /usr/bin/env node
/* eslint-disable-next-line no-unused-vars */
import serve from "./server.mjs";
import { Builder, Capabilities } from "selenium-webdriver";
import commandLineArgs from "command-line-args";
import commandLineUsage from "command-line-usage";
const optionDefinitions = [
{ name: "browser", type: String, description: "Set the browser to test, choices are [safari, firefox, chrome, edge]. By default the $BROWSER env variable is used." },
{ name: "port", type: Number, defaultValue: 8010, description: "Set the test-server port, The default value is 8010." },
{ name: "help", alias: "h", description: "Print this help text." },
];
function printHelp(message = "") {
const usage = commandLineUsage([
{
header: "Run all tests",
},
{
header: "Options",
optionList: optionDefinitions,
},
]);
if (!message) {
console.log(usage);
process.exit(0);
} else {
console.error(message);
console.error();
console.error(usage);
process.exit(1);
}
}
const options = commandLineArgs(optionDefinitions);
if ("help" in options)
printHelp();
const BROWSER = options?.browser;
if (!BROWSER)
printHelp("No browser specified, use $BROWSER or --browser");
let capabilities;
switch (BROWSER) {
case "safari":
capabilities = Capabilities.safari();
break;
case "firefox": {
capabilities = Capabilities.firefox();
break;
}
case "chrome": {
capabilities = Capabilities.chrome();
break;
}
case "edge": {
capabilities = Capabilities.edge();
break;
}
default: {
printHelp(`Invalid browser "${BROWSER}", choices are: "safari", "firefox", "chrome", "edge"`);
}
}
process.on("unhandledRejection", (err) => {
console.error(err);
process.exit(1);
});
process.once("uncaughtException", (err) => {
console.error(err);
process.exit(1);
});
const PORT = options.port;
const server = await serve(PORT);
async function testEnd2End() {
const driver = await new Builder().withCapabilities(capabilities).build();
let results;
try {
const url = `http://localhost:${PORT}/index.html?worstCaseCount=2&iterationCount=3`;
console.log(`JetStream PREPARE ${url}`);
await driver.get(url);
await driver.executeAsyncScript((callback) => {
// callback() is explicitly called without the default event
// as argument to avoid serialization issues with chromedriver.
globalThis.addEventListener("JetStreamReady", () => callback());
// We might not get a chance to install the on-ready listener, thus
// we also check if the runner is ready synchronously.
if (globalThis?.JetStream?.isReady)
callback()
});
results = await benchmarkResults(driver);
// FIXME: validate results;
console.log("\n✅ Tests completed!");
} catch(e) {
console.error("\n❌ Tests failed!");
console.error(e);
throw e;
} finally {
driver.quit();
server.close();
}
}
async function benchmarkResults(driver) {
console.log("JetStream START");
await driver.manage().setTimeouts({ script: 60_000 });
await driver.executeAsyncScript((callback) => {
globalThis.JetStream.start();
callback();
});
await new Promise((resolve, reject) => pollResultsUntilDone(driver, resolve, reject));
const resultsJSON = await driver.executeScript(() => {
return globalThis.JetStream.resultsJSON();
});
return JSON.parse(resultsJSON);
}
class JetStreamTestError extends Error {
constructor(errors) {
super(`Tests failed: ${errors.map(e => e.name).join(", ")}`);
this.errors = errors;
}
}
const UPDATE_INTERVAL = 250;
async function pollResultsUntilDone(driver, resolve, reject) {
const previousResults = new Set();
const intervalId = setInterval(async function logResult() {
const {done, errors, resultsJSON} = await driver.executeScript(() => {
return {
done: globalThis.JetStream.isDone,
errors: globalThis.JetStream.errors,
resultsJSON: JSON.stringify(globalThis.JetStream.resultsObject("simple")),
};
});
if (errors.length) {
clearInterval(intervalId);
reject(new JetStreamTestError(errors));
}
logIncrementalResult(previousResults, JSON.parse(resultsJSON));
if (done) {
clearInterval(intervalId);
resolve();
}
}, UPDATE_INTERVAL)
}
function logIncrementalResult(previousResults, benchmarkResults) {
for (const [testName, testResults] of Object.entries(benchmarkResults)) {
if (previousResults.has(testName))
continue;
console.log(testName, testResults);
previousResults.add(testName);
}
}
setImmediate(testEnd2End);

View file

@ -1,4 +1,5 @@
load("shell-config.js"); load("shell-config.js");
load("params.js");
load("startup-helper/StartupBenchmark.js"); load("startup-helper/StartupBenchmark.js");
load("JetStreamDriver.js"); load("JetStreamDriver.js");

2
transformersjs/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/util/node_modules/
/util/package-lock.json

12
transformersjs/README.md Normal file
View file

@ -0,0 +1,12 @@
- Two tasks: one text/NLP, one audio processing/speech-to-text.
- Everything in `build/` is generated or an upstream library.
- Everything in `util/` is tooling for building and preparing the benchmark.
# Licenses
- Transformers.js: Apache 2.0, https://github.com/huggingface/transformers.js/blob/main/LICENSE
- ONNX runtime: MIT, https://github.com/microsoft/onnxruntime/blob/main/LICENSE
- `text-encoding` Polyfill: Unlicense OR Apache 2.0, https://github.com/inexorabletash/text-encoding/blob/master/LICENSE.md
- Model `DistilBERT base uncased finetuned SST-2`: Apache 2.0, https://huggingface.co/distilbert/distilbert-base-uncased-finetuned-sst-2-english
- Model `openai/whisper-tiny.en`: Apache 2.0, https://huggingface.co/openai/whisper-tiny.en
- Audio file for speech-to-text task: Public domain, https://www.jfklibrary.org/learn/about-jfk/historic-speeches/inaugural-address

116
transformersjs/benchmark.js Normal file
View file

@ -0,0 +1,116 @@
// 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. */ };
const originalFetch = globalThis.fetch ?? function(url) {
throw new Error("no fetch available");
}
globalThis.fetch = async function(url) {
// DEBUG
// console.log('fetch', 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]; }
}
},
};
}
// This should only be called in the browser, where fetch() is available.
return originalFetch(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');
}
}
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);
}
}

9
transformersjs/build.log Normal file
View file

@ -0,0 +1,9 @@
Built on 2025-08-20T13:30:51Z
Installing Node dependencies...
Download and convert audio input(s)...
Converted 4.25s of audio
from 'jfk.wav', 2 channel(s), 44100 Hz, 16 bit, 176000 samples
to 'build/inputs/jfk.raw', 1 channel(s), 16000 Hz, 32 bit float, 68000 samples, 272000 bytes
Download and run model(s)...
Copy library files into build/...
Building done

50
transformersjs/build.sh Executable file
View file

@ -0,0 +1,50 @@
#!/bin/bash
set -euo pipefail
rm -rf build/
mkdir -p build/{models,inputs,onnxruntime-web}/
# Optional: clean all node packages as well.
rm -rf util/node_modules/
touch build.log
BUILD_LOG="$(realpath build.log)"
echo "Built on $(date -u '+%Y-%m-%dT%H:%M:%SZ')" | tee "$BUILD_LOG"
echo "Installing Node dependencies..." | tee -a "$BUILD_LOG"
pushd util/
npm install
popd
echo "Download and convert audio input(s)..." | tee -a "$BUILD_LOG"
wget https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/jfk.wav | tee -a "$BUILD_LOG"
# Shorten the audio file to one sentence in the middle, to speed up a single iteration.
node util/convert-audio.mjs jfk.wav build/inputs/jfk.raw 52000 120000 | tee -a "$BUILD_LOG"
rm jfk.wav
echo "Download and run model(s)..." | tee -a "$BUILD_LOG"
# This automatically places the model files in `build/models/`.
node util/test-models.mjs
echo "Copy library files into build/..." | tee -a "$BUILD_LOG"
cp util/node_modules/@huggingface/transformers/dist/transformers.js build/
git apply transformers.js.patch
# Transformers.js packages the ONNX runtime JSEP build by default, even when
# only using the Wasm backend, which would be fine with the non-JSEP build.
# JSEP uses ASYNCIFY, which isn't optimal. And it's a much larger Wasm binary.
# cp util/node_modules/@huggingface/transformers/dist/ort-wasm-simd-threaded.jsep.{mjs,wasm} build/
# There is also an ONNX runtime build in the onnxruntime-web package.
# TODO(dlehmann): Discuss with upstream Transformers.js folks, whether they can
# use the non-JSEP build if one requests the Wasm backend.
# TODO(dlehmann): Measure performance difference between the two.
cp util/node_modules/onnxruntime-web/dist/ort-wasm-simd-threaded.{mjs,wasm} build/onnxruntime-web/
# TODO: Compress model data (and maybe Wasm modules) with zstd.
# Either decompress with native APIs available in browsers or JS/Wasm polyfill?
# E.g., https://github.com/101arrowz/fzstd or https://github.com/fabiospampinato/zstandard-wasm or https://github.com/donmccurdy/zstddec-wasm
echo "Building done" | tee -a "$BUILD_LOG"

Binary file not shown.

View file

@ -0,0 +1,33 @@
{
"_name_or_path": "distilbert-base-uncased-finetuned-sst-2-english",
"activation": "gelu",
"architectures": [
"DistilBertForSequenceClassification"
],
"attention_dropout": 0.1,
"dim": 768,
"dropout": 0.1,
"finetuning_task": "sst-2",
"hidden_dim": 3072,
"id2label": {
"0": "NEGATIVE",
"1": "POSITIVE"
},
"initializer_range": 0.02,
"label2id": {
"NEGATIVE": 0,
"POSITIVE": 1
},
"max_position_embeddings": 512,
"model_type": "distilbert",
"n_heads": 12,
"n_layers": 6,
"output_past": true,
"pad_token_id": 0,
"qa_dropout": 0.1,
"seq_classif_dropout": 0.2,
"sinusoidal_pos_embds": false,
"tie_weights_": true,
"transformers_version": "4.29.2",
"vocab_size": 30522
}

View file

@ -0,0 +1,15 @@
{
"clean_up_tokenization_spaces": true,
"cls_token": "[CLS]",
"do_basic_tokenize": true,
"do_lower_case": true,
"mask_token": "[MASK]",
"model_max_length": 512,
"never_split": null,
"pad_token": "[PAD]",
"sep_token": "[SEP]",
"strip_accents": null,
"tokenize_chinese_chars": true,
"tokenizer_class": "DistilBertTokenizer",
"unk_token": "[UNK]"
}

View file

@ -0,0 +1,147 @@
{
"_name_or_path": "openai/whisper-tiny.en",
"activation_dropout": 0.0,
"activation_function": "gelu",
"apply_spec_augment": false,
"architectures": [
"WhisperForConditionalGeneration"
],
"attention_dropout": 0.0,
"begin_suppress_tokens": [
220,
50256
],
"bos_token_id": 50257,
"classifier_proj_size": 256,
"d_model": 384,
"decoder_attention_heads": 6,
"decoder_ffn_dim": 1536,
"decoder_layerdrop": 0.0,
"decoder_layers": 4,
"decoder_start_token_id": 50257,
"dropout": 0.0,
"encoder_attention_heads": 6,
"encoder_ffn_dim": 1536,
"encoder_layerdrop": 0.0,
"encoder_layers": 4,
"eos_token_id": 50256,
"forced_decoder_ids": [
[
1,
50362
]
],
"init_std": 0.02,
"is_encoder_decoder": true,
"mask_feature_length": 10,
"mask_feature_min_masks": 0,
"mask_feature_prob": 0.0,
"mask_time_length": 10,
"mask_time_min_masks": 2,
"mask_time_prob": 0.05,
"max_length": 448,
"max_source_positions": 1500,
"max_target_positions": 448,
"median_filter_width": 7,
"model_type": "whisper",
"num_hidden_layers": 4,
"num_mel_bins": 80,
"pad_token_id": 50256,
"scale_embedding": false,
"suppress_tokens": [
1,
2,
7,
8,
9,
10,
14,
25,
26,
27,
28,
29,
31,
58,
59,
60,
61,
62,
63,
90,
91,
92,
93,
357,
366,
438,
532,
685,
705,
796,
930,
1058,
1220,
1267,
1279,
1303,
1343,
1377,
1391,
1635,
1782,
1875,
2162,
2361,
2488,
3467,
4008,
4211,
4600,
4808,
5299,
5855,
6329,
7203,
9609,
9959,
10563,
10786,
11420,
11709,
11907,
13163,
13697,
13700,
14808,
15306,
16410,
16791,
17992,
19203,
19510,
20724,
22305,
22935,
27007,
30109,
30420,
33409,
34949,
40283,
40493,
40549,
47282,
49146,
50257,
50357,
50358,
50359,
50360,
50361
],
"transformers_version": "4.33.0.dev0",
"use_cache": true,
"use_weighted_layer_sum": false,
"vocab_size": 51864
}

View file

@ -0,0 +1,148 @@
{
"alignment_heads": [
[
1,
0
],
[
2,
0
],
[
2,
5
],
[
3,
0
],
[
3,
1
],
[
3,
2
],
[
3,
3
],
[
3,
4
]
],
"begin_suppress_tokens": [
220,
50256
],
"bos_token_id": 50257,
"decoder_start_token_id": 50257,
"eos_token_id": 50256,
"forced_decoder_ids": [
[
1,
50362
]
],
"is_multilingual": false,
"max_initial_timestamp_index": 1,
"max_length": 448,
"no_timestamps_token_id": 50362,
"pad_token_id": 50256,
"return_timestamps": false,
"suppress_tokens": [
1,
2,
7,
8,
9,
10,
14,
25,
26,
27,
28,
29,
31,
58,
59,
60,
61,
62,
63,
90,
91,
92,
93,
357,
366,
438,
532,
685,
705,
796,
930,
1058,
1220,
1267,
1279,
1303,
1343,
1377,
1391,
1635,
1782,
1875,
2162,
2361,
2488,
3467,
4008,
4211,
4600,
4808,
5299,
5855,
6329,
7203,
9609,
9959,
10563,
10786,
11420,
11709,
11907,
13163,
13697,
13700,
14808,
15306,
16410,
16791,
17992,
19203,
19510,
20724,
22305,
22935,
27007,
30109,
30420,
33409,
34949,
40283,
40493,
40549,
47282,
49146,
50257,
50357,
50358,
50359,
50360,
50361
],
"transformers_version": "4.33.0.dev0"
}

View file

@ -0,0 +1,14 @@
{
"chunk_length": 30,
"feature_extractor_type": "WhisperFeatureExtractor",
"feature_size": 80,
"hop_length": 160,
"n_fft": 400,
"n_samples": 480000,
"nb_max_frames": 3000,
"padding_side": "right",
"padding_value": 0.0,
"processor_class": "WhisperProcessor",
"return_attention_mask": false,
"sampling_rate": 16000
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,36 @@
{
"add_bos_token": false,
"add_prefix_space": false,
"bos_token": {
"__type": "AddedToken",
"content": "<|endoftext|>",
"lstrip": false,
"normalized": true,
"rstrip": false,
"single_word": false
},
"clean_up_tokenization_spaces": true,
"eos_token": {
"__type": "AddedToken",
"content": "<|endoftext|>",
"lstrip": false,
"normalized": true,
"rstrip": false,
"single_word": false
},
"errors": "replace",
"model_max_length": 1024,
"pad_token": null,
"processor_class": "WhisperProcessor",
"return_attention_mask": false,
"tokenizer_class": "WhisperTokenizer",
"trust_remote_code": false,
"unk_token": {
"__type": "AddedToken",
"content": "<|endoftext|>",
"lstrip": false,
"normalized": true,
"rstrip": false,
"single_word": false
}
}

View file

@ -0,0 +1,70 @@
var ortWasmThreaded = (() => {
var _scriptName = import.meta.url;
return (
async function(moduleArg = {}) {
var moduleRtn;
var f=moduleArg,aa,ba,ca=new Promise((a,b)=>{aa=a;ba=b}),da="object"==typeof window,k="undefined"!=typeof WorkerGlobalScope,l="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node&&"renderer"!=process.type,m=k&&self.name?.startsWith("em-pthread");if(l){const {createRequire:a}=await import("module");var require=a(import.meta.url),n=require("worker_threads");global.Worker=n.Worker;m=(k=!n.jb)&&"em-pthread"==n.workerData}
f.mountExternalData=(a,b)=>{a.startsWith("./")&&(a=a.substring(2));(f.Sa||(f.Sa=new Map)).set(a,b)};f.unmountExternalData=()=>{delete f.Sa};var SharedArrayBuffer=globalThis.SharedArrayBuffer??(new WebAssembly.Memory({initial:0,maximum:0,lb:!0})).buffer.constructor,ea=Object.assign({},f),fa="./this.program",q=(a,b)=>{throw b;},r="",ha,t;
if(l){var fs=require("fs"),ia=require("path");import.meta.url.startsWith("data:")||(r=ia.dirname(require("url").fileURLToPath(import.meta.url))+"/");t=a=>{a=u(a)?new URL(a):a;return fs.readFileSync(a)};ha=async a=>{a=u(a)?new URL(a):a;return fs.readFileSync(a,void 0)};!f.thisProgram&&1<process.argv.length&&(fa=process.argv[1].replace(/\\/g,"/"));process.argv.slice(2);q=(a,b)=>{process.exitCode=a;throw b;}}else if(da||k)k?r=self.location.href:"undefined"!=typeof document&&document.currentScript&&
(r=document.currentScript.src),_scriptName&&(r=_scriptName),r.startsWith("blob:")?r="":r=r.slice(0,r.replace(/[?#].*/,"").lastIndexOf("/")+1),l||(k&&(t=a=>{var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),ha=async a=>{if(u(a))return new Promise((c,d)=>{var e=new XMLHttpRequest;e.open("GET",a,!0);e.responseType="arraybuffer";e.onload=()=>{200==e.status||0==e.status&&e.response?c(e.response):d(e.status)};e.onerror=d;e.send(null)});
var b=await fetch(a,{credentials:"same-origin"});if(b.ok)return b.arrayBuffer();throw Error(b.status+" : "+b.url);});var ja=console.log.bind(console),ka=console.error.bind(console);l&&(ja=(...a)=>fs.writeSync(1,a.join(" ")+"\n"),ka=(...a)=>fs.writeSync(2,a.join(" ")+"\n"));var la=ja,w=ka;Object.assign(f,ea);ea=null;var x=f.wasmBinary,y,ma,z=!1,A,B,na,oa,pa,qa,ra,C,sa,u=a=>a.startsWith("file://");function D(){y.buffer!=B.buffer&&E();return B}function F(){y.buffer!=B.buffer&&E();return na}
function ta(){y.buffer!=B.buffer&&E();return oa}function G(){y.buffer!=B.buffer&&E();return pa}function H(){y.buffer!=B.buffer&&E();return qa}function va(){y.buffer!=B.buffer&&E();return ra}function I(){y.buffer!=B.buffer&&E();return sa}
if(m){var wa;if(l){var xa=n.parentPort;xa.on("message",b=>onmessage({data:b}));Object.assign(globalThis,{self:global,postMessage:b=>xa.postMessage(b)})}var ya=!1;w=function(...b){b=b.join(" ");l?fs.writeSync(2,b+"\n"):console.error(b)};self.alert=function(...b){postMessage({Ra:"alert",text:b.join(" "),eb:J()})};self.onunhandledrejection=b=>{throw b.reason||b;};function a(b){try{var c=b.data,d=c.Ra;if("load"===d){let e=[];self.onmessage=g=>e.push(g);self.startWorker=()=>{postMessage({Ra:"loaded"});
for(let g of e)a(g);self.onmessage=a};for(const g of c.Za)if(!f[g]||f[g].proxy)f[g]=(...h)=>{postMessage({Ra:"callHandler",Ya:g,args:h})},"print"==g&&(la=f[g]),"printErr"==g&&(w=f[g]);y=c.gb;E();wa(c.hb)}else if("run"===d){za(c.Qa);Aa(c.Qa,0,0,1,0,0);Ba();Ca(c.Qa);ya||=!0;try{Da(c.bb,c.Va)}catch(e){if("unwind"!=e)throw e;}}else"setimmediate"!==c.target&&("checkMailbox"===d?ya&&K():d&&(w(`worker: received unknown command ${d}`),w(c)))}catch(e){throw Ea(),e;}}self.onmessage=a}
function E(){var a=y.buffer;f.HEAP8=B=new Int8Array(a);f.HEAP16=oa=new Int16Array(a);f.HEAPU8=na=new Uint8Array(a);f.HEAPU16=new Uint16Array(a);f.HEAP32=pa=new Int32Array(a);f.HEAPU32=qa=new Uint32Array(a);f.HEAPF32=ra=new Float32Array(a);f.HEAPF64=sa=new Float64Array(a);f.HEAP64=C=new BigInt64Array(a);f.HEAPU64=new BigUint64Array(a)}m||(y=new WebAssembly.Memory({initial:256,maximum:65536,shared:!0}),E());function Fa(){m?startWorker(f):L.$()}var M=0,N=null;
function Ga(){M--;if(0==M&&N){var a=N;N=null;a()}}function O(a){a="Aborted("+a+")";w(a);z=!0;a=new WebAssembly.RuntimeError(a+". Build with -sASSERTIONS for more info.");ba(a);throw a;}var Ha;async function Ia(a){if(!x)try{var b=await ha(a);return new Uint8Array(b)}catch{}if(a==Ha&&x)a=new Uint8Array(x);else if(t)a=t(a);else throw"both async and sync fetching of the wasm failed";return a}
async function Ja(a,b){try{var c=await Ia(a);return await WebAssembly.instantiate(c,b)}catch(d){w(`failed to asynchronously prepare wasm: ${d}`),O(d)}}async function Ka(a){var b=Ha;if(!x&&"function"==typeof WebAssembly.instantiateStreaming&&!u(b)&&!l)try{var c=fetch(b,{credentials:"same-origin"});return await WebAssembly.instantiateStreaming(c,a)}catch(d){w(`wasm streaming compile failed: ${d}`),w("falling back to ArrayBuffer instantiation")}return Ja(b,a)}
function La(){Ma={j:Na,b:Oa,E:Pa,f:Qa,U:Ra,A:Sa,C:Ta,V:Ua,S:Va,L:Wa,R:Xa,n:Ya,B:Za,y:$a,T:ab,z:bb,_:cb,O:db,w:eb,F:fb,t:gb,i:hb,N:Ca,X:ib,I:jb,J:kb,K:lb,G:mb,H:nb,u:ob,q:pb,Z:qb,o:rb,k:sb,Y:tb,d:ub,W:vb,x:wb,c:xb,e:yb,h:zb,v:Ab,s:Bb,r:Cb,P:Db,Q:Eb,D:Fb,g:Gb,m:Hb,M:Ib,l:Jb,a:y,p:Kb};return{a:Ma}}
var Mb={794988:(a,b,c,d,e)=>{if("undefined"==typeof f||!f.Sa)return 1;a=Lb(Number(a>>>0));a.startsWith("./")&&(a=a.substring(2));a=f.Sa.get(a);if(!a)return 2;b=Number(b>>>0);c=Number(c>>>0);d=Number(d>>>0);if(b+c>a.byteLength)return 3;try{const g=a.subarray(b,b+c);switch(e){case 0:F().set(g,d>>>0);break;case 1:f.ib?f.ib(d,g):f.kb(d,g);break;default:return 4}return 0}catch{return 4}},795812:()=>"undefined"!==typeof wasmOffsetConverter};function Na(){return"undefined"!==typeof wasmOffsetConverter}
class Nb{name="ExitStatus";constructor(a){this.message=`Program terminated with exit(${a})`;this.status=a}}
var Ob=a=>{a.terminate();a.onmessage=()=>{}},Pb=[],Sb=a=>{0==Q.length&&(Qb(),Rb(Q[0]));var b=Q.pop();if(!b)return 6;R.push(b);S[a.Qa]=b;b.Qa=a.Qa;var c={Ra:"run",bb:a.ab,Va:a.Va,Qa:a.Qa};l&&b.unref();b.postMessage(c,a.Xa);return 0},T=0,V=(a,b,...c)=>{for(var d=2*c.length,e=Tb(),g=Ub(8*d),h=g>>>3,p=0;p<c.length;p++){var v=c[p];"bigint"==typeof v?(C[h+2*p]=1n,C[h+2*p+1]=v):(C[h+2*p]=0n,I()[h+2*p+1>>>0]=v)}a=Vb(a,0,d,g,b);U(e);return a};
function Kb(a){if(m)return V(0,1,a);A=a;if(!(0<T)){for(var b of R)Ob(b);for(b of Q)Ob(b);Q=[];R=[];S={};z=!0}q(a,new Nb(a))}function Wb(a){if(m)return V(1,0,a);Fb(a)}var Fb=a=>{A=a;if(m)throw Wb(a),"unwind";Kb(a)},Q=[],R=[],Xb=[],S={};function Yb(){for(var a=f.numThreads-1;a--;)Qb();Pb.unshift(()=>{M++;Zb(()=>Ga())})}var ac=a=>{var b=a.Qa;delete S[b];Q.push(a);R.splice(R.indexOf(a),1);a.Qa=0;$b(b)};function Ba(){Xb.forEach(a=>a())}
var Rb=a=>new Promise(b=>{a.onmessage=g=>{g=g.data;var h=g.Ra;if(g.Ta&&g.Ta!=J()){var p=S[g.Ta];p?p.postMessage(g,g.Xa):w(`Internal error! Worker sent a message "${h}" to target pthread ${g.Ta}, but that thread no longer exists!`)}else if("checkMailbox"===h)K();else if("spawnThread"===h)Sb(g);else if("cleanupThread"===h)ac(S[g.cb]);else if("loaded"===h)a.loaded=!0,l&&!a.Qa&&a.unref(),b(a);else if("alert"===h)alert(`Thread ${g.eb}: ${g.text}`);else if("setimmediate"===g.target)a.postMessage(g);else if("callHandler"===
h)f[g.Ya](...g.args);else h&&w(`worker sent an unknown command ${h}`)};a.onerror=g=>{w(`${"worker sent an error!"} ${g.filename}:${g.lineno}: ${g.message}`);throw g;};l&&(a.on("message",g=>a.onmessage({data:g})),a.on("error",g=>a.onerror(g)));var c=[],d=[],e;for(e of d)f.propertyIsEnumerable(e)&&c.push(e);a.postMessage({Ra:"load",Za:c,gb:y,hb:ma})});function Zb(a){m?a():Promise.all(Q.map(Rb)).then(a)}
function Qb(){var a=new Worker(new URL(import.meta.url),{type:"module",workerData:"em-pthread",name:"em-pthread"});Q.push(a)}var za=a=>{E();var b=H()[a+52>>>2>>>0];a=H()[a+56>>>2>>>0];bc(b,b-a);U(b)},W=[],cc,Da=(a,b)=>{T=0;var c=W[a];c||(a>=W.length&&(W.length=a+1),W[a]=c=cc.get(a));a=c(b);0<T?A=a:dc(a)};class ec{constructor(a){this.Ua=a-24}}var fc=0,gc=0;
function Oa(a,b,c){a>>>=0;var d=new ec(a);b>>>=0;c>>>=0;H()[d.Ua+16>>>2>>>0]=0;H()[d.Ua+4>>>2>>>0]=b;H()[d.Ua+8>>>2>>>0]=c;fc=a;gc++;throw fc;}function hc(a,b,c,d){return m?V(2,1,a,b,c,d):Pa(a,b,c,d)}function Pa(a,b,c,d){a>>>=0;b>>>=0;c>>>=0;d>>>=0;if("undefined"==typeof SharedArrayBuffer)return 6;var e=[];if(m&&0===e.length)return hc(a,b,c,d);a={ab:c,Qa:a,Va:d,Xa:e};return m?(a.Ra="spawnThread",postMessage(a,e),0):Sb(a)}
var ic="undefined"!=typeof TextDecoder?new TextDecoder:void 0,jc=(a,b=0,c=NaN)=>{b>>>=0;var d=b+c;for(c=b;a[c]&&!(c>=d);)++c;if(16<c-b&&a.buffer&&ic)return ic.decode(a.buffer instanceof ArrayBuffer?a.subarray(b,c):a.slice(b,c));for(d="";b<c;){var e=a[b++];if(e&128){var g=a[b++]&63;if(192==(e&224))d+=String.fromCharCode((e&31)<<6|g);else{var h=a[b++]&63;e=224==(e&240)?(e&15)<<12|g<<6|h:(e&7)<<18|g<<12|h<<6|a[b++]&63;65536>e?d+=String.fromCharCode(e):(e-=65536,d+=String.fromCharCode(55296|e>>10,56320|
e&1023))}}else d+=String.fromCharCode(e)}return d},Lb=(a,b)=>(a>>>=0)?jc(F(),a,b):"";function Qa(a,b,c){return m?V(3,1,a,b,c):0}function Ra(a,b){if(m)return V(4,1,a,b)}
var X=(a,b,c)=>{var d=F();b>>>=0;if(0<c){var e=b;c=b+c-1;for(var g=0;g<a.length;++g){var h=a.charCodeAt(g);if(55296<=h&&57343>=h){var p=a.charCodeAt(++g);h=65536+((h&1023)<<10)|p&1023}if(127>=h){if(b>=c)break;d[b++>>>0]=h}else{if(2047>=h){if(b+1>=c)break;d[b++>>>0]=192|h>>6}else{if(65535>=h){if(b+2>=c)break;d[b++>>>0]=224|h>>12}else{if(b+3>=c)break;d[b++>>>0]=240|h>>18;d[b++>>>0]=128|h>>12&63}d[b++>>>0]=128|h>>6&63}d[b++>>>0]=128|h&63}}d[b>>>0]=0;a=b-e}else a=0;return a};
function Sa(a,b){if(m)return V(5,1,a,b)}function Ta(a,b,c){if(m)return V(6,1,a,b,c)}function Ua(a,b,c){return m?V(7,1,a,b,c):0}function Va(a,b){if(m)return V(8,1,a,b)}function Wa(a,b,c){if(m)return V(9,1,a,b,c)}function Xa(a,b,c,d){if(m)return V(10,1,a,b,c,d)}function Ya(a,b,c,d){if(m)return V(11,1,a,b,c,d)}function Za(a,b,c,d){if(m)return V(12,1,a,b,c,d)}function $a(a){if(m)return V(13,1,a)}function ab(a,b){if(m)return V(14,1,a,b)}function bb(a,b,c){if(m)return V(15,1,a,b,c)}var cb=()=>O("");
function db(a){Aa(a>>>0,!k,1,!da,131072,!1);Ba()}var kc=a=>{if(!z)try{if(a(),!(0<T))try{m?dc(A):Fb(A)}catch(b){b instanceof Nb||"unwind"==b||q(1,b)}}catch(b){b instanceof Nb||"unwind"==b||q(1,b)}};function Ca(a){a>>>=0;"function"===typeof Atomics.fb&&(Atomics.fb(G(),a>>>2,a).value.then(K),a+=128,Atomics.store(G(),a>>>2,1))}var K=()=>{var a=J();a&&(Ca(a),kc(lc))};function eb(a,b){a>>>=0;a==b>>>0?setTimeout(K):m?postMessage({Ta:a,Ra:"checkMailbox"}):(a=S[a])&&a.postMessage({Ra:"checkMailbox"})}
var mc=[];function fb(a,b,c,d,e){b>>>=0;d/=2;mc.length=d;c=e>>>0>>>3;for(e=0;e<d;e++)mc[e]=C[c+2*e]?C[c+2*e+1]:I()[c+2*e+1>>>0];return(b?Mb[b]:nc[a])(...mc)}var gb=()=>{T=0};function hb(a){a>>>=0;m?postMessage({Ra:"cleanupThread",cb:a}):ac(S[a])}function ib(a){l&&S[a>>>0].ref()}
function jb(a,b){a=-9007199254740992>a||9007199254740992<a?NaN:Number(a);b>>>=0;a=new Date(1E3*a);G()[b>>>2>>>0]=a.getUTCSeconds();G()[b+4>>>2>>>0]=a.getUTCMinutes();G()[b+8>>>2>>>0]=a.getUTCHours();G()[b+12>>>2>>>0]=a.getUTCDate();G()[b+16>>>2>>>0]=a.getUTCMonth();G()[b+20>>>2>>>0]=a.getUTCFullYear()-1900;G()[b+24>>>2>>>0]=a.getUTCDay();a=(a.getTime()-Date.UTC(a.getUTCFullYear(),0,1,0,0,0,0))/864E5|0;G()[b+28>>>2>>>0]=a}
var oc=a=>0===a%4&&(0!==a%100||0===a%400),pc=[0,31,60,91,121,152,182,213,244,274,305,335],qc=[0,31,59,90,120,151,181,212,243,273,304,334];
function kb(a,b){a=-9007199254740992>a||9007199254740992<a?NaN:Number(a);b>>>=0;a=new Date(1E3*a);G()[b>>>2>>>0]=a.getSeconds();G()[b+4>>>2>>>0]=a.getMinutes();G()[b+8>>>2>>>0]=a.getHours();G()[b+12>>>2>>>0]=a.getDate();G()[b+16>>>2>>>0]=a.getMonth();G()[b+20>>>2>>>0]=a.getFullYear()-1900;G()[b+24>>>2>>>0]=a.getDay();var c=(oc(a.getFullYear())?pc:qc)[a.getMonth()]+a.getDate()-1|0;G()[b+28>>>2>>>0]=c;G()[b+36>>>2>>>0]=-(60*a.getTimezoneOffset());c=(new Date(a.getFullYear(),6,1)).getTimezoneOffset();
var d=(new Date(a.getFullYear(),0,1)).getTimezoneOffset();a=(c!=d&&a.getTimezoneOffset()==Math.min(d,c))|0;G()[b+32>>>2>>>0]=a}
function lb(a){a>>>=0;var b=new Date(G()[a+20>>>2>>>0]+1900,G()[a+16>>>2>>>0],G()[a+12>>>2>>>0],G()[a+8>>>2>>>0],G()[a+4>>>2>>>0],G()[a>>>2>>>0],0),c=G()[a+32>>>2>>>0],d=b.getTimezoneOffset(),e=(new Date(b.getFullYear(),6,1)).getTimezoneOffset(),g=(new Date(b.getFullYear(),0,1)).getTimezoneOffset(),h=Math.min(g,e);0>c?G()[a+32>>>2>>>0]=Number(e!=g&&h==d):0<c!=(h==d)&&(e=Math.max(g,e),b.setTime(b.getTime()+6E4*((0<c?h:e)-d)));G()[a+24>>>2>>>0]=b.getDay();c=(oc(b.getFullYear())?pc:qc)[b.getMonth()]+
b.getDate()-1|0;G()[a+28>>>2>>>0]=c;G()[a>>>2>>>0]=b.getSeconds();G()[a+4>>>2>>>0]=b.getMinutes();G()[a+8>>>2>>>0]=b.getHours();G()[a+12>>>2>>>0]=b.getDate();G()[a+16>>>2>>>0]=b.getMonth();G()[a+20>>>2>>>0]=b.getYear();a=b.getTime();return BigInt(isNaN(a)?-1:a/1E3)}function mb(a,b,c,d,e,g,h){return m?V(16,1,a,b,c,d,e,g,h):-52}function nb(a,b,c,d,e,g){if(m)return V(17,1,a,b,c,d,e,g)}var Y={},xb=()=>performance.timeOrigin+performance.now();
function ob(a,b){if(m)return V(18,1,a,b);Y[a]&&(clearTimeout(Y[a].id),delete Y[a]);if(!b)return 0;var c=setTimeout(()=>{delete Y[a];kc(()=>rc(a,performance.timeOrigin+performance.now()))},b);Y[a]={id:c,mb:b};return 0}
function pb(a,b,c,d){a>>>=0;b>>>=0;c>>>=0;d>>>=0;var e=(new Date).getFullYear(),g=(new Date(e,0,1)).getTimezoneOffset();e=(new Date(e,6,1)).getTimezoneOffset();var h=Math.max(g,e);H()[a>>>2>>>0]=60*h;G()[b>>>2>>>0]=Number(g!=e);b=p=>{var v=Math.abs(p);return`UTC${0<=p?"-":"+"}${String(Math.floor(v/60)).padStart(2,"0")}${String(v%60).padStart(2,"0")}`};a=b(g);b=b(e);e<g?(X(a,c,17),X(b,d,17)):(X(a,d,17),X(b,c,17))}var tb=()=>Date.now(),sc=1;
function qb(a,b,c){if(!(0<=a&&3>=a))return 28;if(0===a)a=Date.now();else if(sc)a=performance.timeOrigin+performance.now();else return 52;C[c>>>0>>>3]=BigInt(Math.round(1E6*a));return 0}var tc=[];function rb(a,b,c){a>>>=0;b>>>=0;c>>>=0;tc.length=0;for(var d;d=F()[b++>>>0];){var e=105!=d;e&=112!=d;c+=e&&c%8?4:0;tc.push(112==d?H()[c>>>2>>>0]:106==d?C[c>>>3]:105==d?G()[c>>>2>>>0]:I()[c>>>3>>>0]);c+=e?8:4}return Mb[a](...tc)}var sb=()=>{};function ub(a,b){return w(Lb(a>>>0,b>>>0))}
var vb=()=>{T+=1;throw"unwind";};function wb(){return 4294901760}var yb=()=>l?require("os").cpus().length:navigator.hardwareConcurrency;function zb(){O("Cannot use emscripten_pc_get_function without -sUSE_OFFSET_CONVERTER");return 0}
function Ab(a){a>>>=0;var b=F().length;if(a<=b||4294901760<a)return!1;for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,a+100663296);a:{d=(Math.min(4294901760,65536*Math.ceil(Math.max(a,d)/65536))-y.buffer.byteLength+65535)/65536|0;try{y.grow(d);E();var e=1;break a}catch(g){}e=void 0}if(e)return!0}return!1}var uc=()=>{O("Cannot use convertFrameToPC (needed by __builtin_return_address) without -sUSE_OFFSET_CONVERTER");return 0},Z={},vc=a=>{a.forEach(b=>{var c=uc();c&&(Z[c]=b)})};
function Bb(){var a=Error().stack.toString().split("\n");"Error"==a[0]&&a.shift();vc(a);Z.Wa=uc();Z.$a=a;return Z.Wa}function Cb(a,b,c){a>>>=0;b>>>=0;if(Z.Wa==a)var d=Z.$a;else d=Error().stack.toString().split("\n"),"Error"==d[0]&&d.shift(),vc(d);for(var e=3;d[e]&&uc()!=a;)++e;for(a=0;a<c&&d[a+e];++a)G()[b+4*a>>>2>>>0]=uc();return a}
var wc={},yc=()=>{if(!xc){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"==typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:fa||"./this.program"},b;for(b in wc)void 0===wc[b]?delete a[b]:a[b]=wc[b];var c=[];for(b in a)c.push(`${b}=${a[b]}`);xc=c}return xc},xc;
function Db(a,b){if(m)return V(19,1,a,b);a>>>=0;b>>>=0;var c=0;yc().forEach((d,e)=>{var g=b+c;e=H()[a+4*e>>>2>>>0]=g;for(g=0;g<d.length;++g)D()[e++>>>0]=d.charCodeAt(g);D()[e>>>0]=0;c+=d.length+1});return 0}function Eb(a,b){if(m)return V(20,1,a,b);a>>>=0;b>>>=0;var c=yc();H()[a>>>2>>>0]=c.length;var d=0;c.forEach(e=>d+=e.length+1);H()[b>>>2>>>0]=d;return 0}function Gb(a){return m?V(21,1,a):52}function Hb(a,b,c,d){return m?V(22,1,a,b,c,d):52}function Ib(a,b,c,d){return m?V(23,1,a,b,c,d):70}
var zc=[null,[],[]];function Jb(a,b,c,d){if(m)return V(24,1,a,b,c,d);b>>>=0;c>>>=0;d>>>=0;for(var e=0,g=0;g<c;g++){var h=H()[b>>>2>>>0],p=H()[b+4>>>2>>>0];b+=8;for(var v=0;v<p;v++){var P=F()[h+v>>>0],ua=zc[a];0===P||10===P?((1===a?la:w)(jc(ua)),ua.length=0):ua.push(P)}e+=p}H()[d>>>2>>>0]=e;return 0}m||Yb();var nc=[Kb,Wb,hc,Qa,Ra,Sa,Ta,Ua,Va,Wa,Xa,Ya,Za,$a,ab,bb,mb,nb,ob,Db,Eb,Gb,Hb,Ib,Jb],Ma,L;
(async function(){function a(d,e){L=d.exports;L=Ac();Xb.push(L.Da);cc=L.Ea;ma=e;Ga();return L}M++;var b=La();if(f.instantiateWasm)return new Promise(d=>{f.instantiateWasm(b,(e,g)=>{a(e,g);d(e.exports)})});if(m)return new Promise(d=>{wa=e=>{var g=new WebAssembly.Instance(e,La());d(a(g,e))}});Ha??=f.locateFile?f.locateFile?f.locateFile("ort-wasm-simd-threaded.wasm",r):r+"ort-wasm-simd-threaded.wasm":(new URL("ort-wasm-simd-threaded.wasm",import.meta.url)).href;try{var c=await Ka(b);return a(c.instance,
c.module)}catch(d){return ba(d),Promise.reject(d)}})();f._OrtInit=(a,b)=>(f._OrtInit=L.aa)(a,b);f._OrtGetLastError=(a,b)=>(f._OrtGetLastError=L.ba)(a,b);f._OrtCreateSessionOptions=(a,b,c,d,e,g,h,p,v,P)=>(f._OrtCreateSessionOptions=L.ca)(a,b,c,d,e,g,h,p,v,P);f._OrtAppendExecutionProvider=(a,b,c,d,e)=>(f._OrtAppendExecutionProvider=L.da)(a,b,c,d,e);f._OrtAddFreeDimensionOverride=(a,b,c)=>(f._OrtAddFreeDimensionOverride=L.ea)(a,b,c);
f._OrtAddSessionConfigEntry=(a,b,c)=>(f._OrtAddSessionConfigEntry=L.fa)(a,b,c);f._OrtReleaseSessionOptions=a=>(f._OrtReleaseSessionOptions=L.ga)(a);f._OrtCreateSession=(a,b,c)=>(f._OrtCreateSession=L.ha)(a,b,c);f._OrtReleaseSession=a=>(f._OrtReleaseSession=L.ia)(a);f._OrtGetInputOutputCount=(a,b,c)=>(f._OrtGetInputOutputCount=L.ja)(a,b,c);f._OrtGetInputOutputMetadata=(a,b,c,d)=>(f._OrtGetInputOutputMetadata=L.ka)(a,b,c,d);f._OrtFree=a=>(f._OrtFree=L.la)(a);
f._OrtCreateTensor=(a,b,c,d,e,g)=>(f._OrtCreateTensor=L.ma)(a,b,c,d,e,g);f._OrtGetTensorData=(a,b,c,d,e)=>(f._OrtGetTensorData=L.na)(a,b,c,d,e);f._OrtReleaseTensor=a=>(f._OrtReleaseTensor=L.oa)(a);f._OrtCreateRunOptions=(a,b,c,d)=>(f._OrtCreateRunOptions=L.pa)(a,b,c,d);f._OrtAddRunConfigEntry=(a,b,c)=>(f._OrtAddRunConfigEntry=L.qa)(a,b,c);f._OrtReleaseRunOptions=a=>(f._OrtReleaseRunOptions=L.ra)(a);f._OrtCreateBinding=a=>(f._OrtCreateBinding=L.sa)(a);
f._OrtBindInput=(a,b,c)=>(f._OrtBindInput=L.ta)(a,b,c);f._OrtBindOutput=(a,b,c,d)=>(f._OrtBindOutput=L.ua)(a,b,c,d);f._OrtClearBoundOutputs=a=>(f._OrtClearBoundOutputs=L.va)(a);f._OrtReleaseBinding=a=>(f._OrtReleaseBinding=L.wa)(a);f._OrtRunWithBinding=(a,b,c,d,e)=>(f._OrtRunWithBinding=L.xa)(a,b,c,d,e);f._OrtRun=(a,b,c,d,e,g,h,p)=>(f._OrtRun=L.ya)(a,b,c,d,e,g,h,p);f._OrtEndProfiling=a=>(f._OrtEndProfiling=L.za)(a);var J=()=>(J=L.Aa)();f._free=a=>(f._free=L.Ba)(a);f._malloc=a=>(f._malloc=L.Ca)(a);
var Aa=(a,b,c,d,e,g)=>(Aa=L.Fa)(a,b,c,d,e,g),Ea=()=>(Ea=L.Ga)(),Vb=(a,b,c,d,e)=>(Vb=L.Ha)(a,b,c,d,e),$b=a=>($b=L.Ia)(a),dc=a=>(dc=L.Ja)(a),rc=(a,b)=>(rc=L.Ka)(a,b),lc=()=>(lc=L.La)(),bc=(a,b)=>(bc=L.Ma)(a,b),U=a=>(U=L.Na)(a),Ub=a=>(Ub=L.Oa)(a),Tb=()=>(Tb=L.Pa)();function Ac(){var a=L;a=Object.assign({},a);var b=d=>()=>d()>>>0,c=d=>e=>d(e)>>>0;a.Aa=b(a.Aa);a.Ca=c(a.Ca);a.Oa=c(a.Oa);a.Pa=b(a.Pa);a.__cxa_get_exception_ptr=c(a.__cxa_get_exception_ptr);return a}f.stackSave=()=>Tb();f.stackRestore=a=>U(a);
f.stackAlloc=a=>Ub(a);f.setValue=function(a,b,c="i8"){c.endsWith("*")&&(c="*");switch(c){case "i1":D()[a>>>0]=b;break;case "i8":D()[a>>>0]=b;break;case "i16":ta()[a>>>1>>>0]=b;break;case "i32":G()[a>>>2>>>0]=b;break;case "i64":C[a>>>3]=BigInt(b);break;case "float":va()[a>>>2>>>0]=b;break;case "double":I()[a>>>3>>>0]=b;break;case "*":H()[a>>>2>>>0]=b;break;default:O(`invalid type for setValue: ${c}`)}};
f.getValue=function(a,b="i8"){b.endsWith("*")&&(b="*");switch(b){case "i1":return D()[a>>>0];case "i8":return D()[a>>>0];case "i16":return ta()[a>>>1>>>0];case "i32":return G()[a>>>2>>>0];case "i64":return C[a>>>3];case "float":return va()[a>>>2>>>0];case "double":return I()[a>>>3>>>0];case "*":return H()[a>>>2>>>0];default:O(`invalid type for getValue: ${b}`)}};f.UTF8ToString=Lb;f.stringToUTF8=X;
f.lengthBytesUTF8=a=>{for(var b=0,c=0;c<a.length;++c){var d=a.charCodeAt(c);127>=d?b++:2047>=d?b+=2:55296<=d&&57343>=d?(b+=4,++c):b+=3}return b};function Bc(){if(0<M)N=Bc;else if(m)aa(f),Fa();else{for(;0<Pb.length;)Pb.shift()(f);0<M?N=Bc:(f.calledRun=!0,z||(Fa(),aa(f)))}}Bc();f.PTR_SIZE=4;moduleRtn=ca;
return moduleRtn;
}
);
})();
export default ortWasmThreaded;
var isPthread = globalThis.self?.name?.startsWith('em-pthread');
var isNode = typeof globalThis.process?.versions?.node == 'string';
if (isNode) isPthread = (await import('worker_threads')).workerData === 'em-pthread';
// When running as a pthread, construct a new instance on startup
isPthread && ortWasmThreaded();

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,35 @@
// 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.
// End-to-end task 1: Sentiment analysis, so NLP.
globalThis.initPipeline = async function(pipeline) {
return await pipeline(
'sentiment-analysis',
'Xenova/distilbert-base-uncased-finetuned-sst-2-english',
// Use quantized models for smaller model weights.
{ dtype: 'uint8' }
);
}
globalThis.doTask = async function(pipeline) {
const inputs = [
'I love transformers!',
'Benchmarking is hard.',
];
const outputs = await pipeline(inputs);
return outputs;
}
globalThis.validate = function(outputs) {
if (outputs.length !== 2) {
throw new Error('Expected output to be an array matching the inputs, but got:' + outputs);
}
if (outputs[0].label !== 'POSITIVE' || outputs[0].score < 0.9) {
throw new Error('Expected positive sentiment for first input, but got: ' + outputs[0]);
}
if (outputs[1].label !== 'NEGATIVE' || outputs[1].score < 0.9) {
throw new Error('Expected negative sentiment for second input, but got: ' + outputs[1]);
}
}

View file

@ -0,0 +1,28 @@
// 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.
// End-to-end task 2: Speech recognition.
// Based on the example https://huggingface.co/Xenova/whisper-tiny.en
// Convert audio inputs first with `convert-audio.mjs`.
globalThis.initPipeline = async function(pipeline) {
return await pipeline(
'automatic-speech-recognition',
'Xenova/whisper-tiny.en',
// Use quantized model because of smaller weights.
{ dtype: 'q8' }
);
}
globalThis.doTask = async function(pipeline, inputFileBuffer) {
const input = new Float32Array(inputFileBuffer);
const output = await pipeline(input);
return output.text.trim();
}
globalThis.validate = function(output) {
if (output !== 'Ask not what your country can do for you.') {
throw new Error('Wrong output, got: ' + output);
}
}

View file

@ -0,0 +1,17 @@
diff --git a/transformersjs/build/transformers.js b/transformersjs/build/transformers.js
index b8e1342..0c702e8 100644
--- a/transformersjs/build/transformers.js
+++ b/transformersjs/build/transformers.js
@@ -37868,11 +37868,10 @@ function quantize_embeddings(tensor, precision) {
/******/
/******/ /* webpack/runtime/publicPath */
/******/ (() => {
-/******/ var scriptUrl;
+/******/ var scriptUrl = '';
/******/ if (typeof import.meta.url === "string") scriptUrl = import.meta.url
/******/ // When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration
/******/ // or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.
-/******/ if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser");
/******/ scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\?.*$/, "").replace(/\/[^\/]+$/, "/");
/******/ __webpack_require__.p = scriptUrl;
/******/ })();

View file

@ -0,0 +1,63 @@
// 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.
// Convert an WAV audio file to a format that Transformers.js Whisper model can
// work with.
// Write the output as raw float values, so we don't need any additional
// WAV/RIFF parser but can directly use it.
// See https://huggingface.co/docs/transformers.js/guides/node-audio-processing
import wavefile from 'wavefile';
import * as fs from 'fs';
if (process.argv.length < 5) {
throw new Error("Usage: node convert-audio.mjs input.wav output.raw <start_sample> <end_sample>");
}
const inputFilename = process.argv[2];
const outputFilename = process.argv[3];
const outputStartSample = parseInt(process.argv[4]);
const outputEndSample = parseInt(process.argv[5]);
const wav = new wavefile.WaveFile(fs.readFileSync(inputFilename));
const inputChannelCount = wav.fmt.numChannels;
const inputSampleRate = wav.fmt.sampleRate;
const inputBitDepth = wav.fmt.bitsPerSample;
// Pipeline expects input as a Float32Array.
wav.toBitDepth('32f');
// Whisper expects audio with a sampling rate of 16000.
const outputSampleRate = 16000;
wav.toSampleRate(outputSampleRate);
// Explicitly set output type to `Float32Array` to make the raw output smaller.
// (Otherwise, `Float64Array` is the default.)
let audioData = wav.getSamples(/* interleaved */ false, Float32Array);
// Downmix to mono, if necessary.
const outputChannelCount = 1;
if (Array.isArray(audioData)) {
if (audioData.length > 1) {
const SCALING_FACTOR = Math.sqrt(2);
for (let i = 0; i < audioData[0].length; ++i) {
audioData[0][i] = SCALING_FACTOR * (audioData[0][i] + audioData[1][i]) / 2;
}
}
audioData = audioData[0];
}
const inputSampleCount = audioData.length;
// Select samples as specified by outputStartSample and outputEndSample.
console.assert(outputStartSample < outputEndSample, "Given start is smaller than end.");
console.assert(outputEndSample <= audioData.length, "Given end is longer than input file.");
audioData = audioData.slice(outputStartSample, outputEndSample);
const outputSampleCount = audioData.length;
const outputSizeBytes = audioData.byteLength;
fs.writeFileSync(outputFilename, audioData);
const durationSeconds = outputSampleCount / outputSampleRate;
console.log(`Converted ${durationSeconds}s of audio`);
console.log(` from '${inputFilename}', ${inputChannelCount} channel(s), ${inputSampleRate} Hz, ${inputBitDepth} bit, ${inputSampleCount} samples`);
console.log(` to '${outputFilename}', ${outputChannelCount} channel(s), ${outputSampleRate} Hz, 32 bit float, ${outputSampleCount} samples, ${outputSizeBytes} bytes`);

View file

@ -0,0 +1,10 @@
{
"name": "transformersjs-wasm",
"version": "0.0.1",
"author": "Daniel Lehmann <dlehmann@chromium.org>",
"license": "MIT",
"dependencies": {
"@huggingface/transformers": "^3.7.2",
"wavefile": "^11.0.0"
}
}

View file

@ -0,0 +1,57 @@
// 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.
// This is set up as an NPM project, so we can test Transformers.js in the
// environment it is supposed to work in (Node.js or a full browser) and
// download the dist build and model data.
// Note that Transformers.js in Node.js actually uses a binding to a native
// implementation of ONNX runtime on either the CPU or CUDA, i.e., NOT Wasm.
// It also runs multi-threaded by default.
// Performance is thus NOT comparable to the Wasm version in shells / browsers.
import { env, pipeline } from '@huggingface/transformers';
import { readFileSync } from 'fs';
env.cacheDir = 'build/models/'
env.localModelPath = 'build/models/'
// Uncomment the following line, if you want to make sure it runs without
// internet connection.
// env.allowRemoteModels = false;
{
console.log('Sentiment analysis / text classification with BERT model.');
await import('../task-bert.js');
let start = performance.now();
let pipe = await initPipeline(pipeline);
console.log('initPipeline took ' + (performance.now() - start) + ' ms.');
for (let i = 0; i < 20; ++i) {
start = performance.now();
const output = await doTask(pipe);
validate(output)
console.log('output:', output);
console.log('doTask took ' + (performance.now() - start) + ' ms.');
}
}
{
console.log('Automatic speech recognition with Whisper model.');
await import('../task-whisper.js');
const inputAudioBuffer = readFileSync('build/inputs/jfk.raw').buffer;
let start = performance.now();
let pipe = await initPipeline(pipeline);
console.log('initPipeline took ' + (performance.now() - start) + ' ms.');
for (let i = 0; i < 5; ++i) {
start = performance.now();
const output = await doTask(pipe, inputAudioBuffer);
validate(output)
console.log('output:', output);
console.log('doTask took ' + (performance.now() - start) + ' ms.');
}
}

View file

@ -4,6 +4,20 @@ Tests [.NET on WebAssembly](https://github.com/dotnet/runtime). This benchmark t
on .NET implementation of String, JSON serialization, specifics of .NET exceptions and computation on .NET implementation of String, JSON serialization, specifics of .NET exceptions and computation
of a 3D scene using Mono Interpreter & AOT. Source code: [.NET](wasm/dotnet) of a 3D scene using Mono Interpreter & AOT. Source code: [.NET](wasm/dotnet)
## The Benchmark
Consists of two halves:
1) BenchTasks, which is a series of micro-benchmarks:
1) Exception throw/catch.
1) JSON serialization/deserialization.
1) String operations.
1) RayTracer
They run for different iterations/sizes, respectively for each half, depending on the variant (AOT/interpreter). The values to adjust the workloads are passed in from benchmark.js.
## Build instructions ## Build instructions
Download .NET SDK 9.0.3xx Download .NET SDK 9.0.3xx

View file

@ -128,11 +128,16 @@ class Benchmark {
this.api = await this.dotnet.withModuleConfig({ locateFile: e => e }).withConfig(config).create(); this.api = await this.dotnet.withModuleConfig({ locateFile: e => e }).withConfig(config).create();
this.exports = await this.api.getAssemblyExports("dotnet.dll"); this.exports = await this.api.getAssemblyExports("dotnet.dll");
// This drives the workload size for BenchTasks half of the test.
this.benchTasksBatchSize = dotnetFlavor === "aot" ? 50 : 10;
// These drive the workload size for RayTrace half of the test.
this.hardwareConcurrency = 1; this.hardwareConcurrency = 1;
this.sceneWidth = dotnetFlavor === "aot" ? 300 : 150; this.sceneWidth = dotnetFlavor === "aot" ? 100 : 50;
this.sceneHeight = dotnetFlavor === "aot" ? 200 : 100; this.sceneHeight = dotnetFlavor === "aot" ? 100 : 50;
} }
async runIteration() { async runIteration() {
await this.exports.Interop.RunIteration(this.sceneWidth, this.sceneHeight, this.hardwareConcurrency); await this.exports.Interop.RunIteration(this.benchTasksBatchSize, this.sceneWidth, this.sceneHeight, this.hardwareConcurrency);
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

0
wasm/dotnet/build-aot/wwwroot/_framework/icudt_CJK.dat Normal file → Executable file
View file

View file

View file

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show more