1703 lines
52 KiB
JavaScript
1703 lines
52 KiB
JavaScript
"use strict";
|
|
|
|
/*
|
|
* Copyright (C) 2018 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.
|
|
*/
|
|
|
|
const preloadResources = !isInBrowser;
|
|
const measureTotalTimeAsSubtest = false; // Once we move to preloading all resources, it would be good to turn this on.
|
|
|
|
if (typeof RAMification === "undefined")
|
|
var RAMification = false;
|
|
|
|
if (typeof testIterationCount === "undefined")
|
|
var testIterationCount = undefined;
|
|
|
|
if (typeof testIterationCountMap === "undefined")
|
|
var testIterationCountMap = new Map;
|
|
|
|
if (typeof testWorstCaseCountMap === "undefined")
|
|
var testWorstCaseCountMap = new Map;
|
|
|
|
if (typeof dumpJSONResults === "undefined")
|
|
var dumpJSONResults = false;
|
|
|
|
// Used for the promise representing the current benchmark run.
|
|
this.currentResolve = null;
|
|
this.currentReject = null;
|
|
|
|
const defaultIterationCount = 120;
|
|
const defaultWorstCaseCount = 4;
|
|
|
|
let showScoreDetails = false;
|
|
let categoryScores = null;
|
|
|
|
function displayCategoryScores() {
|
|
if (!categoryScores)
|
|
return;
|
|
|
|
let summaryElement = document.getElementById("result-summary");
|
|
for (let [category, scores] of categoryScores)
|
|
summaryElement.innerHTML += `<p> ${category}: ${uiFriendlyNumber(geomean(scores))}</p>`
|
|
|
|
categoryScores = null;
|
|
}
|
|
|
|
function getIterationCount(plan) {
|
|
if (testIterationCountMap.has(plan.name))
|
|
return testIterationCountMap.get(plan.name);
|
|
if (testIterationCount)
|
|
return testIterationCount;
|
|
if (plan.iterations)
|
|
return plan.iterations;
|
|
return defaultIterationCount;
|
|
}
|
|
|
|
function getWorstCaseCount(plan) {
|
|
if (testWorstCaseCountMap.has(plan.name))
|
|
return testWorstCaseCountMap.get(plan.name);
|
|
if (plan.worstCaseCount)
|
|
return plan.worstCaseCount;
|
|
return defaultWorstCaseCount;
|
|
}
|
|
|
|
if (isInBrowser) {
|
|
document.onkeydown = (keyboardEvent) => {
|
|
let key = keyboardEvent.key;
|
|
if (key === "d" || key === "D") {
|
|
showScoreDetails = true;
|
|
|
|
displayCategoryScores();
|
|
}
|
|
};
|
|
}
|
|
|
|
function assert(b, m = "") {
|
|
if (!b)
|
|
throw new Error("Bad assertion: " + m);
|
|
}
|
|
|
|
function firstID(benchmark) {
|
|
return `results-cell-${benchmark.name}-first`;
|
|
}
|
|
|
|
function worst4ID(benchmark) {
|
|
return `results-cell-${benchmark.name}-worst4`;
|
|
}
|
|
|
|
function avgID(benchmark) {
|
|
return `results-cell-${benchmark.name}-avg`;
|
|
}
|
|
|
|
function scoreID(benchmark) {
|
|
return `results-cell-${benchmark.name}-score`;
|
|
}
|
|
|
|
function mean(values) {
|
|
assert(values instanceof Array);
|
|
let sum = 0;
|
|
for (let x of values)
|
|
sum += x;
|
|
return sum / values.length;
|
|
}
|
|
|
|
function geomean(values) {
|
|
assert(values instanceof Array);
|
|
let product = 1;
|
|
for (let x of values)
|
|
product *= x;
|
|
return product ** (1 / values.length);
|
|
}
|
|
|
|
function toScore(timeValue) {
|
|
return 5000 / timeValue;
|
|
}
|
|
|
|
function toTimeValue(score) {
|
|
return 5000 / score;
|
|
}
|
|
|
|
function updateUI() {
|
|
return new Promise((resolve) => {
|
|
if (isInBrowser)
|
|
requestAnimationFrame(() => setTimeout(resolve, 0));
|
|
else
|
|
resolve();
|
|
});
|
|
}
|
|
|
|
function uiFriendlyNumber(num) {
|
|
if (Number.isInteger(num))
|
|
return num;
|
|
return num.toFixed(3);
|
|
}
|
|
|
|
function uiFriendlyDuration(time)
|
|
{
|
|
let minutes = time.getMinutes();
|
|
let seconds = time.getSeconds();
|
|
let milliSeconds = time.getMilliseconds();
|
|
let result = "" + minutes + ":";
|
|
|
|
result = result + (seconds < 10 ? "0" : "") + seconds + ".";
|
|
result = result + (milliSeconds < 10 ? "00" : (milliSeconds < 100 ? "0" : "")) + milliSeconds;
|
|
|
|
return result;
|
|
}
|
|
|
|
const fileLoader = (function() {
|
|
class Loader {
|
|
constructor() {
|
|
this.requests = new Map;
|
|
}
|
|
|
|
async _loadInternal(url) {
|
|
if (!isInBrowser)
|
|
return Promise.resolve(readFile(url));
|
|
|
|
let fetchResponse = await fetch(new Request(url));
|
|
if (url.indexOf(".js") !== -1)
|
|
return await fetchResponse.text();
|
|
else if (url.indexOf(".wasm") !== -1)
|
|
return await fetchResponse.arrayBuffer();
|
|
|
|
throw new Error("should not be reached!");
|
|
}
|
|
|
|
async load(url) {
|
|
if (this.requests.has(url))
|
|
return (await this.requests.get(url));
|
|
|
|
let promise = this._loadInternal(url);
|
|
this.requests.set(url, promise);
|
|
return (await promise);
|
|
}
|
|
}
|
|
return new Loader;
|
|
})();
|
|
|
|
class Driver {
|
|
constructor() {
|
|
this.benchmarks = [];
|
|
}
|
|
|
|
addPlan(plan, BenchmarkClass = DefaultBenchmark) {
|
|
this.benchmarks.push(new BenchmarkClass(plan));
|
|
}
|
|
|
|
async start() {
|
|
let statusElement = false;
|
|
let summaryElement = false;
|
|
if (isInBrowser) {
|
|
statusElement = document.getElementById("status");
|
|
summaryElement = document.getElementById("result-summary");
|
|
statusElement.innerHTML = `<label>Running...</label>`;
|
|
} else {
|
|
console.log("Starting JetStream2");
|
|
}
|
|
|
|
await updateUI();
|
|
|
|
let start = Date.now();
|
|
for (let benchmark of this.benchmarks) {
|
|
benchmark.updateUIBeforeRun();
|
|
|
|
await updateUI();
|
|
|
|
try {
|
|
await benchmark.run();
|
|
} catch(e) {
|
|
JetStream.reportError(benchmark);
|
|
throw e;
|
|
}
|
|
|
|
benchmark.updateUIAfterRun();
|
|
}
|
|
|
|
let totalTime = Date.now() - start;
|
|
if (measureTotalTimeAsSubtest) {
|
|
if (isInBrowser)
|
|
document.getElementById("benchmark-total-time-score").innerHTML = uiFriendlyNumber(totalTime);
|
|
else
|
|
console.log("Total time:", uiFriendlyNumber(totalTime));
|
|
allScores.push(totalTime);
|
|
}
|
|
|
|
let allScores = [];
|
|
for (let benchmark of this.benchmarks)
|
|
allScores.push(benchmark.score);
|
|
|
|
categoryScores = new Map;
|
|
for (let benchmark of this.benchmarks) {
|
|
for (let category of Object.keys(benchmark.subTimes()))
|
|
categoryScores.set(category, []);
|
|
}
|
|
|
|
for (let benchmark of this.benchmarks) {
|
|
for (let [category, value] of Object.entries(benchmark.subTimes())) {
|
|
let arr = categoryScores.get(category);
|
|
arr.push(value);
|
|
}
|
|
}
|
|
|
|
if (isInBrowser) {
|
|
summaryElement.classList.add('done');
|
|
summaryElement.innerHTML = "<div class=\"score\">" + uiFriendlyNumber(geomean(allScores)) + "</div><label>Score</label>";
|
|
summaryElement.onclick = displayCategoryScores;
|
|
if (showScoreDetails)
|
|
displayCategoryScores();
|
|
statusElement.innerHTML = '';
|
|
} else {
|
|
console.log("\n");
|
|
for (let [category, scores] of categoryScores)
|
|
console.log(`${category}: ${uiFriendlyNumber(geomean(scores))}`);
|
|
|
|
console.log("\nTotal Score: ", uiFriendlyNumber(geomean(allScores)), "\n");
|
|
}
|
|
|
|
this.reportScoreToRunBenchmarkRunner();
|
|
this.dumpJSONResultsIfNeeded();
|
|
}
|
|
|
|
runCode(string)
|
|
{
|
|
if (!isInBrowser) {
|
|
let scripts = string;
|
|
let globalObject;
|
|
let realm;
|
|
if (isD8) {
|
|
realm = Realm.createAllowCrossRealmAccess();
|
|
globalObject = Realm.global(realm);
|
|
globalObject.loadString = function(s) {
|
|
return Realm.eval(realm, s);
|
|
};
|
|
globalObject.readFile = read;
|
|
} else
|
|
globalObject = runString("");
|
|
|
|
globalObject.console = {log:globalObject.print}
|
|
globalObject.top = {
|
|
currentResolve,
|
|
currentReject
|
|
};
|
|
for (let script of scripts)
|
|
globalObject.loadString(script);
|
|
|
|
return isD8 ? realm : globalObject;
|
|
}
|
|
|
|
var magic = document.getElementById("magic");
|
|
magic.contentDocument.body.textContent = "";
|
|
magic.contentDocument.body.innerHTML = "<iframe id=\"magicframe\" frameborder=\"0\">";
|
|
|
|
var magicFrame = magic.contentDocument.getElementById("magicframe");
|
|
magicFrame.contentDocument.open();
|
|
magicFrame.contentDocument.write("<!DOCTYPE html><head><title>benchmark payload</title></head><body>\n" + string + "</body></html>");
|
|
|
|
return magicFrame;
|
|
}
|
|
|
|
prepareToRun()
|
|
{
|
|
this.benchmarks.sort((a, b) => a.plan.name.toLowerCase() < b.plan.name.toLowerCase() ? 1 : -1);
|
|
|
|
let text = "";
|
|
let newBenchmarks = [];
|
|
for (let benchmark of this.benchmarks) {
|
|
let id = JSON.stringify(benchmark.constructor.scoreDescription());
|
|
let description = JSON.parse(id);
|
|
|
|
newBenchmarks.push(benchmark);
|
|
let scoreIds = benchmark.scoreIdentifiers()
|
|
let overallScoreId = scoreIds.pop();
|
|
|
|
if (isInBrowser) {
|
|
text +=
|
|
`<div class="benchmark" id="benchmark-${benchmark.name}">
|
|
<h3 class="benchmark-name"><a href="in-depth.html#${benchmark.name}">${benchmark.name}</a></h3>
|
|
<h4 class="score" id="${overallScoreId}">___</h4><p>`;
|
|
for (let i = 0; i < scoreIds.length; i++) {
|
|
let id = scoreIds[i];
|
|
let label = description[i];
|
|
text += `<span class="result"><span id="${id}">___</span><label>${label}</label></span>`
|
|
}
|
|
text += `</p></div>`;
|
|
}
|
|
}
|
|
|
|
if (!isInBrowser)
|
|
return;
|
|
|
|
for (let f = 0; f < 5; f++)
|
|
text += `<div class="benchmark fill"></div>`;
|
|
|
|
let timestamp = Date.now();
|
|
document.getElementById('jetstreams').style.backgroundImage = `url('jetstreams.svg?${timestamp}')`;
|
|
let resultsTable = document.getElementById("results");
|
|
resultsTable.innerHTML = text;
|
|
|
|
document.getElementById("magic").textContent = "";
|
|
document.addEventListener('keypress', function (e) {
|
|
if (e.which === 13)
|
|
JetStream.start();
|
|
});
|
|
}
|
|
|
|
reportError(benchmark)
|
|
{
|
|
for (let id of benchmark.scoreIdentifiers())
|
|
document.getElementById(id).innerHTML = "error";
|
|
}
|
|
|
|
async initialize() {
|
|
await this.fetchResources();
|
|
this.prepareToRun();
|
|
if (isInBrowser && window.location.search == '?report=true') {
|
|
setTimeout(() => this.start(), 4000);
|
|
}
|
|
}
|
|
|
|
async fetchResources() {
|
|
for (let benchmark of this.benchmarks)
|
|
await benchmark.fetchResources();
|
|
|
|
if (!isInBrowser)
|
|
return;
|
|
|
|
let statusElement = document.getElementById("status");
|
|
statusElement.classList.remove('loading');
|
|
statusElement.innerHTML = `<a href="javascript:JetStream.start()" class="button">Start Test</a>`;
|
|
statusElement.onclick = () => {
|
|
statusElement.onclick = null;
|
|
JetStream.start();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
resultsJSON()
|
|
{
|
|
let results = {};
|
|
for (let benchmark of this.benchmarks) {
|
|
const subResults = {}
|
|
const subTimes = benchmark.subTimes();
|
|
for (const name in subTimes) {
|
|
subResults[name] = {"metrics": {"Time": {"current": [toTimeValue(subTimes[name])]}}};
|
|
}
|
|
results[benchmark.name] = {
|
|
"metrics" : {
|
|
"Score" : {"current" : [benchmark.score]},
|
|
"Time": ["Geometric"],
|
|
},
|
|
"tests": subResults,
|
|
};
|
|
}
|
|
|
|
results = {"JetStream2.0": {"metrics" : {"Score" : ["Geometric"]}, "tests" : results}};
|
|
|
|
return JSON.stringify(results);
|
|
}
|
|
|
|
dumpJSONResultsIfNeeded()
|
|
{
|
|
if (dumpJSONResults) {
|
|
console.log("\n");
|
|
console.log(this.resultsJSON());
|
|
console.log("\n");
|
|
}
|
|
}
|
|
|
|
async reportScoreToRunBenchmarkRunner()
|
|
{
|
|
if (!isInBrowser)
|
|
return;
|
|
|
|
if (window.location.search !== '?report=true')
|
|
return;
|
|
|
|
const content = this.resultsJSON();
|
|
await fetch("/report", {
|
|
method: "POST",
|
|
heeaders: {
|
|
"Content-Type": "application/json",
|
|
"Content-Length": content.length,
|
|
"Connection": "close",
|
|
},
|
|
body: content,
|
|
});
|
|
}
|
|
};
|
|
|
|
class Benchmark {
|
|
constructor(plan)
|
|
{
|
|
this.plan = plan;
|
|
this.iterations = getIterationCount(plan);
|
|
this.isAsync = !!plan.isAsync;
|
|
|
|
this.scripts = null;
|
|
|
|
this._resourcesPromise = null;
|
|
this.fetchResources();
|
|
}
|
|
|
|
get name() { return this.plan.name; }
|
|
|
|
get runnerCode() {
|
|
return `
|
|
let __benchmark = new Benchmark(${this.iterations});
|
|
let results = [];
|
|
for (let i = 0; i < ${this.iterations}; i++) {
|
|
if (__benchmark.prepareForNextIteration)
|
|
__benchmark.prepareForNextIteration();
|
|
|
|
let start = Date.now();
|
|
__benchmark.runIteration();
|
|
let end = Date.now();
|
|
|
|
results.push(Math.max(1, end - start));
|
|
}
|
|
if (__benchmark.validate)
|
|
__benchmark.validate();
|
|
top.currentResolve(results);`;
|
|
}
|
|
|
|
processResults() {
|
|
throw new Error("Subclasses need to implement this");
|
|
}
|
|
|
|
get score() {
|
|
throw new Error("Subclasses need to implement this");
|
|
}
|
|
|
|
get prerunCode() { return null; }
|
|
|
|
async run() {
|
|
let code;
|
|
if (isInBrowser)
|
|
code = "";
|
|
else
|
|
code = [];
|
|
|
|
let addScript = (text) => {
|
|
if (isInBrowser)
|
|
code += `<script>${text}</script>`;
|
|
else
|
|
code.push(text);
|
|
};
|
|
|
|
let addScriptWithURL = (url) => {
|
|
if (isInBrowser)
|
|
code += `<script src="${url}"></script>`;
|
|
else
|
|
assert(false, "Should not reach here in CLI");
|
|
};
|
|
|
|
addScript(`const isInBrowser = ${isInBrowser}; let performance = {now: Date.now.bind(Date)};`);
|
|
|
|
if (!!this.plan.deterministicRandom) {
|
|
addScript(`
|
|
Math.random = (function() {
|
|
var seed = 49734321;
|
|
return function() {
|
|
// Robert Jenkins' 32 bit integer hash function.
|
|
seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff;
|
|
seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;
|
|
seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff;
|
|
seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;
|
|
seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff;
|
|
seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;
|
|
return (seed & 0xfffffff) / 0x10000000;
|
|
};
|
|
})();
|
|
`);
|
|
|
|
}
|
|
|
|
if (this.plan.preload) {
|
|
let str = "";
|
|
for (let [variableName, blobUrl] of this.preloads)
|
|
str += `const ${variableName} = "${blobUrl}";\n`;
|
|
addScript(str);
|
|
}
|
|
|
|
let prerunCode = this.prerunCode;
|
|
if (prerunCode)
|
|
addScript(prerunCode);
|
|
|
|
if (preloadResources) {
|
|
assert(this.scripts && this.scripts.length === this.plan.files.length);
|
|
|
|
for (let text of this.scripts)
|
|
addScript(text);
|
|
} else {
|
|
for (let file of this.plan.files)
|
|
addScriptWithURL(file);
|
|
}
|
|
|
|
let promise = new Promise((resolve, reject) => {
|
|
currentResolve = resolve;
|
|
currentReject = reject;
|
|
});
|
|
|
|
if (isInBrowser) {
|
|
code = `
|
|
<script> window.onerror = top.currentReject; </script>
|
|
${code}
|
|
`;
|
|
}
|
|
addScript(this.runnerCode);
|
|
|
|
this.startTime = new Date();
|
|
|
|
if (RAMification)
|
|
resetMemoryPeak();
|
|
|
|
let magicFrame;
|
|
try {
|
|
magicFrame = JetStream.runCode(code);
|
|
} catch(e) {
|
|
console.log("Error in runCode: ", e);
|
|
throw e;
|
|
}
|
|
let results = await promise;
|
|
|
|
this.endTime = new Date();
|
|
|
|
if (RAMification) {
|
|
let memoryFootprint = MemoryFootprint();
|
|
this.currentFootprint = memoryFootprint.current;
|
|
this.peakFootprint = memoryFootprint.peak;
|
|
}
|
|
|
|
this.processResults(results);
|
|
if (isInBrowser)
|
|
magicFrame.contentDocument.close();
|
|
else if (isD8)
|
|
Realm.dispose(magicFrame);
|
|
}
|
|
|
|
fetchResources() {
|
|
if (this._resourcesPromise)
|
|
return this._resourcesPromise;
|
|
|
|
let filePromises = preloadResources ? this.plan.files.map((file) => fileLoader.load(file)) : [];
|
|
let preloads = [];
|
|
let preloadVariableNames = [];
|
|
|
|
if (isInBrowser && this.plan.preload) {
|
|
for (let prop of Object.getOwnPropertyNames(this.plan.preload)) {
|
|
preloadVariableNames.push(prop);
|
|
preloads.push(this.plan.preload[prop]);
|
|
}
|
|
}
|
|
|
|
preloads = preloads.map((file) => fileLoader.load(file));
|
|
|
|
let p1 = Promise.all(filePromises).then((texts) => {
|
|
if (!preloadResources)
|
|
return;
|
|
this.scripts = [];
|
|
assert(texts.length === this.plan.files.length);
|
|
for (let text of texts)
|
|
this.scripts.push(text);
|
|
});
|
|
|
|
let p2 = Promise.all(preloads).then((data) => {
|
|
this.preloads = [];
|
|
this.blobs = [];
|
|
for (let i = 0; i < data.length; ++i) {
|
|
let item = data[i];
|
|
|
|
let blob;
|
|
if (typeof item === "string") {
|
|
blob = new Blob([item], {type : 'application/javascript'});
|
|
} else if (item instanceof ArrayBuffer) {
|
|
blob = new Blob([item], {type : 'application/octet-stream'});
|
|
} else
|
|
throw new Error("Unexpected item!");
|
|
|
|
this.blobs.push(blob);
|
|
this.preloads.push([preloadVariableNames[i], URL.createObjectURL(blob)]);
|
|
}
|
|
});
|
|
|
|
this._resourcesPromise = Promise.all([p1, p2]);
|
|
return this._resourcesPromise;
|
|
}
|
|
|
|
static scoreDescription() { throw new Error("Must be implemented by subclasses."); }
|
|
scoreIdentifiers() { throw new Error("Must be implemented by subclasses"); }
|
|
|
|
updateUIBeforeRun() {
|
|
if (!isInBrowser) {
|
|
console.log(`Running ${this.name}:`);
|
|
return;
|
|
}
|
|
|
|
let containerUI = document.getElementById("results");
|
|
let resultsBenchmarkUI = document.getElementById(`benchmark-${this.name}`);
|
|
containerUI.insertBefore(resultsBenchmarkUI, containerUI.firstChild);
|
|
resultsBenchmarkUI.classList.add("benchmark-running");
|
|
|
|
for (let id of this.scoreIdentifiers())
|
|
document.getElementById(id).innerHTML = "...";
|
|
}
|
|
|
|
updateUIAfterRun() {
|
|
if (!isInBrowser)
|
|
return;
|
|
|
|
let benchmarkResultsUI = document.getElementById(`benchmark-${this.name}`);
|
|
benchmarkResultsUI.classList.remove("benchmark-running");
|
|
benchmarkResultsUI.classList.add("benchmark-done");
|
|
|
|
}
|
|
};
|
|
|
|
class DefaultBenchmark extends Benchmark {
|
|
constructor(...args) {
|
|
super(...args);
|
|
|
|
this.worstCaseCount = getWorstCaseCount(this.plan);
|
|
this.firstIteration = null;
|
|
this.worst4 = null;
|
|
this.average = null;
|
|
}
|
|
|
|
processResults(results) {
|
|
function copyArray(a) {
|
|
let result = [];
|
|
for (let x of a)
|
|
result.push(x);
|
|
return result;
|
|
}
|
|
results = copyArray(results);
|
|
|
|
this.firstIteration = toScore(results[0]);
|
|
|
|
results = results.slice(1);
|
|
results.sort((a, b) => a < b ? 1 : -1);
|
|
for (let i = 0; i + 1 < results.length; ++i)
|
|
assert(results[i] >= results[i + 1]);
|
|
|
|
let worstCase = [];
|
|
for (let i = 0; i < this.worstCaseCount; ++i)
|
|
worstCase.push(results[i]);
|
|
this.worst4 = toScore(mean(worstCase));
|
|
this.average = toScore(mean(results));
|
|
}
|
|
|
|
get score() {
|
|
return geomean([this.firstIteration, this.worst4, this.average]);
|
|
}
|
|
|
|
subTimes() {
|
|
return {
|
|
"First": this.firstIteration,
|
|
"Worst": this.worst4,
|
|
"Average": this.average,
|
|
};
|
|
}
|
|
|
|
static scoreDescription() {
|
|
return ["First", "Worst", "Average", "Score"];
|
|
}
|
|
|
|
scoreIdentifiers() {
|
|
return [firstID(this), worst4ID(this), avgID(this), scoreID(this)];
|
|
}
|
|
|
|
updateUIAfterRun() {
|
|
super.updateUIAfterRun();
|
|
|
|
if (isInBrowser) {
|
|
document.getElementById(firstID(this)).innerHTML = uiFriendlyNumber(this.firstIteration);
|
|
document.getElementById(worst4ID(this)).innerHTML = uiFriendlyNumber(this.worst4);
|
|
document.getElementById(avgID(this)).innerHTML = uiFriendlyNumber(this.average);
|
|
document.getElementById(scoreID(this)).innerHTML = uiFriendlyNumber(this.score);
|
|
return;
|
|
}
|
|
|
|
console.log(" Startup:", uiFriendlyNumber(this.firstIteration));
|
|
console.log(" Worst Case:", uiFriendlyNumber(this.worst4));
|
|
console.log(" Average:", uiFriendlyNumber(this.average));
|
|
console.log(" Score:", uiFriendlyNumber(this.score));
|
|
if (RAMification) {
|
|
console.log(" Current Footprint:", uiFriendlyNumber(this.currentFootprint));
|
|
console.log(" Peak Footprint:", uiFriendlyNumber(this.peakFootprint));
|
|
}
|
|
console.log(" Wall time:", uiFriendlyDuration(new Date(this.endTime - this.startTime)));
|
|
}
|
|
}
|
|
|
|
class AsyncBenchmark extends DefaultBenchmark {
|
|
get runnerCode() {
|
|
return `
|
|
async function doRun() {
|
|
let __benchmark = new Benchmark();
|
|
let results = [];
|
|
for (let i = 0; i < ${this.iterations}; i++) {
|
|
let start = Date.now();
|
|
await __benchmark.runIteration();
|
|
let end = Date.now();
|
|
results.push(Math.max(1, end - start));
|
|
}
|
|
if (__benchmark.validate)
|
|
__benchmark.validate();
|
|
top.currentResolve(results);
|
|
}
|
|
doRun();`
|
|
}
|
|
};
|
|
|
|
class WSLBenchmark extends Benchmark {
|
|
constructor(...args) {
|
|
super(...args);
|
|
|
|
this.stdlib = null;
|
|
this.mainRun = null;
|
|
}
|
|
|
|
processResults(results) {
|
|
this.stdlib = toScore(results[0]);
|
|
this.mainRun = toScore(results[1]);
|
|
}
|
|
|
|
get score() {
|
|
return geomean([this.stdlib, this.mainRun]);
|
|
}
|
|
|
|
get runnerCode() {
|
|
return `
|
|
let benchmark = new Benchmark();
|
|
let results = [];
|
|
{
|
|
let start = Date.now();
|
|
benchmark.buildStdlib();
|
|
results.push(Date.now() - start);
|
|
}
|
|
|
|
{
|
|
let start = Date.now();
|
|
benchmark.run();
|
|
results.push(Date.now() - start);
|
|
}
|
|
|
|
top.currentResolve(results);
|
|
`;
|
|
}
|
|
|
|
subTimes() {
|
|
return {
|
|
"Stdlib": this.stdlib,
|
|
"MainRun": this.mainRun,
|
|
};
|
|
}
|
|
|
|
static scoreDescription() {
|
|
return ["Stdlib", "MainRun", "Score"];
|
|
}
|
|
|
|
scoreIdentifiers() {
|
|
return ["wsl-stdlib-score", "wsl-tests-score", "wsl-score-score"];
|
|
}
|
|
|
|
updateUIAfterRun() {
|
|
super.updateUIAfterRun();
|
|
|
|
if (isInBrowser) {
|
|
document.getElementById("wsl-stdlib-score").innerHTML = uiFriendlyNumber(this.stdlib);
|
|
document.getElementById("wsl-tests-score").innerHTML = uiFriendlyNumber(this.mainRun);
|
|
document.getElementById("wsl-score-score").innerHTML = uiFriendlyNumber(this.score);
|
|
return;
|
|
}
|
|
|
|
console.log(" Stdlib:", uiFriendlyNumber(this.stdlib));
|
|
console.log(" Tests:", uiFriendlyNumber(this.mainRun));
|
|
console.log(" Score:", uiFriendlyNumber(this.score));
|
|
if (RAMification) {
|
|
console.log(" Current Footprint:", uiFriendlyNumber(this.currentFootprint));
|
|
console.log(" Peak Footprint:", uiFriendlyNumber(this.peakFootprint));
|
|
}
|
|
console.log(" Wall time:", uiFriendlyDuration(new Date(this.endTime - this.startTime)));
|
|
}
|
|
};
|
|
|
|
class WasmBenchmark extends Benchmark {
|
|
constructor(...args) {
|
|
super(...args);
|
|
|
|
this.startupTime = null;
|
|
this.runTime = null;
|
|
}
|
|
|
|
processResults(results) {
|
|
this.startupTime = toScore(results[0]);
|
|
this.runTime = toScore(results[1]);
|
|
}
|
|
|
|
get score() {
|
|
return geomean([this.startupTime, this.runTime]);
|
|
}
|
|
|
|
get wasmPath() {
|
|
return this.plan.wasmPath;
|
|
}
|
|
|
|
get prerunCode() {
|
|
let str = `
|
|
let verbose = false;
|
|
|
|
let compileTime = null;
|
|
let runTime = null;
|
|
|
|
let globalObject = this;
|
|
|
|
globalObject.benchmarkTime = Date.now.bind(Date);
|
|
|
|
globalObject.reportCompileTime = (t) => {
|
|
if (compileTime !== null)
|
|
throw new Error("called report compile time twice");
|
|
compileTime = t;
|
|
};
|
|
|
|
globalObject.reportRunTime = (t) => {
|
|
if (runTime !== null)
|
|
throw new Error("called report run time twice")
|
|
runTime = t;
|
|
top.currentResolve([compileTime, runTime]);
|
|
};
|
|
|
|
abort = quit = function() {
|
|
if (verbose)
|
|
console.log('Intercepted quit/abort');
|
|
};
|
|
|
|
oldPrint = globalObject.print;
|
|
globalObject.print = globalObject.printErr = (...args) => {
|
|
if (verbose)
|
|
console.log('Intercepted print: ', ...args);
|
|
};
|
|
|
|
let Module = {
|
|
preRun: [],
|
|
postRun: [],
|
|
print: function() { },
|
|
printErr: function() { },
|
|
setStatus: function(text) {
|
|
},
|
|
totalDependencies: 0,
|
|
monitorRunDependencies: function(left) {
|
|
this.totalDependencies = Math.max(this.totalDependencies, left);
|
|
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
|
|
}
|
|
};
|
|
globalObject.Module = Module;
|
|
`;
|
|
return str;
|
|
}
|
|
|
|
get runnerCode() {
|
|
let str = "";
|
|
if (isInBrowser) {
|
|
str += `
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.open('GET', wasmBlobURL, true);
|
|
xhr.responseType = 'arraybuffer';
|
|
xhr.onload = function() {
|
|
Module.wasmBinary = xhr.response;
|
|
doRun();
|
|
};
|
|
xhr.send(null);
|
|
`;
|
|
} else {
|
|
str += `
|
|
Module.wasmBinary = read("${this.wasmPath}", "binary");
|
|
globalObject.read = (...args) => {
|
|
console.log("should not be inside read: ", ...args);
|
|
throw new Error;
|
|
};
|
|
|
|
Module.setStatus = null;
|
|
Module.monitorRunDependencies = null;
|
|
|
|
Promise.resolve(42).then(() => {
|
|
try {
|
|
doRun();
|
|
} catch(e) {
|
|
console.log("error running wasm:", e);
|
|
throw e;
|
|
}
|
|
})
|
|
`;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
subTimes() {
|
|
return {
|
|
"Startup": this.startupTime,
|
|
"Runtime": this.runTime,
|
|
};
|
|
}
|
|
|
|
static scoreDescription() {
|
|
return ["Startup", "Runtime", "Score"];
|
|
}
|
|
|
|
get startupID() {
|
|
return `wasm-startup-id${this.name}`;
|
|
}
|
|
get runID() {
|
|
return `wasm-run-id${this.name}`;
|
|
}
|
|
get scoreID() {
|
|
return `wasm-score-id${this.name}`;
|
|
}
|
|
|
|
scoreIdentifiers() {
|
|
return [this.startupID, this.runID, this.scoreID];
|
|
}
|
|
|
|
updateUIAfterRun() {
|
|
super.updateUIAfterRun();
|
|
|
|
if (isInBrowser) {
|
|
document.getElementById(this.startupID).innerHTML = uiFriendlyNumber(this.startupTime);
|
|
document.getElementById(this.runID).innerHTML = uiFriendlyNumber(this.runTime);
|
|
document.getElementById(this.scoreID).innerHTML = uiFriendlyNumber(this.score);
|
|
return;
|
|
}
|
|
console.log(" Startup:", uiFriendlyNumber(this.startupTime));
|
|
console.log(" Run time:", uiFriendlyNumber(this.runTime));
|
|
if (RAMification) {
|
|
console.log(" Current Footprint:", uiFriendlyNumber(this.currentFootprint));
|
|
console.log(" Peak Footprint:", uiFriendlyNumber(this.peakFootprint));
|
|
}
|
|
console.log(" Score:", uiFriendlyNumber(this.score));
|
|
}
|
|
};
|
|
|
|
const ARESGroup = Symbol.for("ARES");
|
|
const CDJSGroup = Symbol.for("CDJS");
|
|
const CodeLoadGroup = Symbol.for("CodeLoad");
|
|
const LuaJSFightGroup = Symbol.for("LuaJSFight");
|
|
const OctaneGroup = Symbol.for("Octane");
|
|
const RexBenchGroup = Symbol.for("RexBench");
|
|
const SeaMonsterGroup = Symbol.for("SeaMonster");
|
|
const SimpleGroup = Symbol.for("Simple");
|
|
const SunSpiderGroup = Symbol.for("SunSpider");
|
|
const WasmGroup = Symbol.for("Wasm");
|
|
const WorkerTestsGroup = Symbol.for("WorkerTests");
|
|
const WSLGroup = Symbol.for("WSL");
|
|
const WTBGroup = Symbol.for("WTB");
|
|
|
|
|
|
let testPlans = [
|
|
// ARES
|
|
{
|
|
name: "Air",
|
|
files: [
|
|
"./ARES-6/Air/symbols.js"
|
|
, "./ARES-6/Air/tmp_base.js"
|
|
, "./ARES-6/Air/arg.js"
|
|
, "./ARES-6/Air/basic_block.js"
|
|
, "./ARES-6/Air/code.js"
|
|
, "./ARES-6/Air/frequented_block.js"
|
|
, "./ARES-6/Air/inst.js"
|
|
, "./ARES-6/Air/opcode.js"
|
|
, "./ARES-6/Air/reg.js"
|
|
, "./ARES-6/Air/stack_slot.js"
|
|
, "./ARES-6/Air/tmp.js"
|
|
, "./ARES-6/Air/util.js"
|
|
, "./ARES-6/Air/custom.js"
|
|
, "./ARES-6/Air/liveness.js"
|
|
, "./ARES-6/Air/insertion_set.js"
|
|
, "./ARES-6/Air/allocate_stack.js"
|
|
, "./ARES-6/Air/payload-gbemu-executeIteration.js"
|
|
, "./ARES-6/Air/payload-imaging-gaussian-blur-gaussianBlur.js"
|
|
, "./ARES-6/Air/payload-airjs-ACLj8C.js"
|
|
, "./ARES-6/Air/payload-typescript-scanIdentifier.js"
|
|
, "./ARES-6/Air/benchmark.js"
|
|
],
|
|
testGroup: ARESGroup
|
|
},
|
|
{
|
|
name: "Basic",
|
|
files: [
|
|
"./ARES-6/Basic/ast.js"
|
|
, "./ARES-6/Basic/basic.js"
|
|
, "./ARES-6/Basic/caseless_map.js"
|
|
, "./ARES-6/Basic/lexer.js"
|
|
, "./ARES-6/Basic/number.js"
|
|
, "./ARES-6/Basic/parser.js"
|
|
, "./ARES-6/Basic/random.js"
|
|
, "./ARES-6/Basic/state.js"
|
|
, "./ARES-6/Basic/util.js"
|
|
, "./ARES-6/Basic/benchmark.js"
|
|
],
|
|
testGroup: ARESGroup
|
|
},
|
|
{
|
|
name: "ML",
|
|
files: [
|
|
"./ARES-6/ml/index.js"
|
|
, "./ARES-6/ml/benchmark.js"
|
|
],
|
|
iterations: 60,
|
|
testGroup: ARESGroup
|
|
},
|
|
{
|
|
name: "Babylon",
|
|
files: [
|
|
"./ARES-6/Babylon/index.js"
|
|
, "./ARES-6/Babylon/benchmark.js"
|
|
],
|
|
preload: {
|
|
airBlob: "./ARES-6/Babylon/air-blob.js",
|
|
basicBlob: "./ARES-6/Babylon/basic-blob.js",
|
|
inspectorBlob: "./ARES-6/Babylon/inspector-blob.js",
|
|
babylonBlob: "./ARES-6/Babylon/babylon-blob.js"
|
|
},
|
|
testGroup: ARESGroup
|
|
},
|
|
// CDJS
|
|
{
|
|
name: "cdjs",
|
|
files: [
|
|
"./cdjs/constants.js"
|
|
, "./cdjs/util.js"
|
|
, "./cdjs/red_black_tree.js"
|
|
, "./cdjs/call_sign.js"
|
|
, "./cdjs/vector_2d.js"
|
|
, "./cdjs/vector_3d.js"
|
|
, "./cdjs/motion.js"
|
|
, "./cdjs/reduce_collision_set.js"
|
|
, "./cdjs/simulator.js"
|
|
, "./cdjs/collision.js"
|
|
, "./cdjs/collision_detector.js"
|
|
, "./cdjs/benchmark.js"
|
|
],
|
|
iterations: 60,
|
|
worstCaseCount: 3,
|
|
testGroup: CDJSGroup
|
|
},
|
|
// CodeLoad
|
|
{
|
|
name: "first-inspector-code-load",
|
|
files: [
|
|
"./code-load/code-first-load.js"
|
|
],
|
|
preload: {
|
|
inspectorPayloadBlob: "./code-load/inspector-payload-minified.js"
|
|
},
|
|
testGroup: CodeLoadGroup
|
|
},
|
|
{
|
|
name: "multi-inspector-code-load",
|
|
files: [
|
|
"./code-load/code-multi-load.js"
|
|
],
|
|
preload: {
|
|
inspectorPayloadBlob: "./code-load/inspector-payload-minified.js"
|
|
},
|
|
testGroup: CodeLoadGroup
|
|
},
|
|
// Octane
|
|
{
|
|
name: "Box2D",
|
|
files: [
|
|
"./Octane/box2d.js"
|
|
],
|
|
deterministicRandom: true,
|
|
testGroup: OctaneGroup
|
|
},
|
|
{
|
|
name: "octane-code-load",
|
|
files: [
|
|
"./Octane/code-first-load.js"
|
|
],
|
|
deterministicRandom: true,
|
|
testGroup: OctaneGroup
|
|
},
|
|
{
|
|
name: "crypto",
|
|
files: [
|
|
"./Octane/crypto.js"
|
|
],
|
|
deterministicRandom: true,
|
|
testGroup: OctaneGroup
|
|
},
|
|
{
|
|
name: "delta-blue",
|
|
files: [
|
|
"./Octane/deltablue.js"
|
|
],
|
|
deterministicRandom: true,
|
|
testGroup: OctaneGroup
|
|
},
|
|
{
|
|
name: "earley-boyer",
|
|
files: [
|
|
"./Octane/earley-boyer.js"
|
|
],
|
|
deterministicRandom: true,
|
|
testGroup: OctaneGroup
|
|
},
|
|
{
|
|
name: "gbemu",
|
|
files: [
|
|
"./Octane/gbemu-part1.js"
|
|
, "./Octane/gbemu-part2.js"
|
|
],
|
|
deterministicRandom: true,
|
|
testGroup: OctaneGroup
|
|
},
|
|
{
|
|
name: "mandreel",
|
|
files: [
|
|
"./Octane/mandreel.js"
|
|
],
|
|
iterations: 80,
|
|
deterministicRandom: true,
|
|
testGroup: OctaneGroup
|
|
},
|
|
{
|
|
name: "navier-stokes",
|
|
files: [
|
|
"./Octane/navier-stokes.js"
|
|
],
|
|
deterministicRandom: true,
|
|
testGroup: OctaneGroup
|
|
},
|
|
{
|
|
name: "pdfjs",
|
|
files: [
|
|
"./Octane/pdfjs.js"
|
|
],
|
|
deterministicRandom: true,
|
|
testGroup: OctaneGroup
|
|
},
|
|
{
|
|
name: "raytrace",
|
|
files: [
|
|
"./Octane/raytrace.js"
|
|
],
|
|
deterministicRandom: true,
|
|
testGroup: OctaneGroup
|
|
},
|
|
{
|
|
name: "regexp",
|
|
files: [
|
|
"./Octane/regexp.js"
|
|
],
|
|
deterministicRandom: true,
|
|
testGroup: OctaneGroup
|
|
},
|
|
{
|
|
name: "richards",
|
|
files: [
|
|
"./Octane/richards.js"
|
|
],
|
|
deterministicRandom: true,
|
|
testGroup: OctaneGroup
|
|
},
|
|
{
|
|
name: "splay",
|
|
files: [
|
|
"./Octane/splay.js"
|
|
],
|
|
deterministicRandom: true,
|
|
testGroup: OctaneGroup
|
|
},
|
|
{
|
|
name: "typescript",
|
|
files: [
|
|
"./Octane/typescript-compiler.js"
|
|
, "./Octane/typescript-input.js"
|
|
, "./Octane/typescript.js"
|
|
],
|
|
iterations: 15,
|
|
worstCaseCount: 2,
|
|
deterministicRandom: true,
|
|
testGroup: OctaneGroup
|
|
},
|
|
{
|
|
name: "octane-zlib",
|
|
files: [
|
|
"./Octane/zlib-data.js"
|
|
, "./Octane/zlib.js"
|
|
],
|
|
iterations: 15,
|
|
worstCaseCount: 2,
|
|
deterministicRandom: true,
|
|
testGroup: OctaneGroup
|
|
},
|
|
// RexBench
|
|
{
|
|
name: "FlightPlanner",
|
|
files: [
|
|
"./RexBench/FlightPlanner/airways.js"
|
|
, "./RexBench/FlightPlanner/waypoints.js"
|
|
, "./RexBench/FlightPlanner/flight_planner.js"
|
|
, "./RexBench/FlightPlanner/expectations.js"
|
|
, "./RexBench/FlightPlanner/benchmark.js"
|
|
],
|
|
testGroup: RexBenchGroup
|
|
},
|
|
{
|
|
name: "OfflineAssembler",
|
|
files: [
|
|
"./RexBench/OfflineAssembler/registers.js"
|
|
, "./RexBench/OfflineAssembler/instructions.js"
|
|
, "./RexBench/OfflineAssembler/ast.js"
|
|
, "./RexBench/OfflineAssembler/parser.js"
|
|
, "./RexBench/OfflineAssembler/file.js"
|
|
, "./RexBench/OfflineAssembler/LowLevelInterpreter.js"
|
|
, "./RexBench/OfflineAssembler/LowLevelInterpreter32_64.js"
|
|
, "./RexBench/OfflineAssembler/LowLevelInterpreter64.js"
|
|
, "./RexBench/OfflineAssembler/InitBytecodes.js"
|
|
, "./RexBench/OfflineAssembler/expected.js"
|
|
, "./RexBench/OfflineAssembler/benchmark.js"
|
|
],
|
|
iterations: 80,
|
|
testGroup: RexBenchGroup
|
|
},
|
|
{
|
|
name: "UniPoker",
|
|
files: [
|
|
"./RexBench/UniPoker/poker.js"
|
|
, "./RexBench/UniPoker/expected.js"
|
|
, "./RexBench/UniPoker/benchmark.js"
|
|
],
|
|
deterministicRandom: true,
|
|
testGroup: RexBenchGroup
|
|
},
|
|
// Simple
|
|
{
|
|
name: "async-fs",
|
|
files: [
|
|
"./simple/file-system.js"
|
|
],
|
|
iterations: 40,
|
|
worstCaseCount: 3,
|
|
benchmarkClass: AsyncBenchmark,
|
|
testGroup: SimpleGroup
|
|
},
|
|
{
|
|
name: "float-mm.c",
|
|
files: [
|
|
"./simple/float-mm.c.js"
|
|
],
|
|
iterations: 15,
|
|
worstCaseCount: 2,
|
|
testGroup: SimpleGroup
|
|
},
|
|
{
|
|
name: "hash-map",
|
|
files: [
|
|
"./simple/hash-map.js"
|
|
],
|
|
testGroup: SimpleGroup
|
|
},
|
|
// SeaMonster
|
|
{
|
|
name: "ai-astar",
|
|
files: [
|
|
"./SeaMonster/ai-astar.js"
|
|
],
|
|
testGroup: SeaMonsterGroup
|
|
},
|
|
{
|
|
name: "gaussian-blur",
|
|
files: [
|
|
"./SeaMonster/gaussian-blur.js"
|
|
],
|
|
testGroup: SeaMonsterGroup
|
|
},
|
|
{
|
|
name: "stanford-crypto-aes",
|
|
files: [
|
|
"./SeaMonster/sjlc.js"
|
|
, "./SeaMonster/stanford-crypto-aes.js"
|
|
],
|
|
testGroup: SeaMonsterGroup
|
|
},
|
|
{
|
|
name: "stanford-crypto-pbkdf2",
|
|
files: [
|
|
"./SeaMonster/sjlc.js"
|
|
, "./SeaMonster/stanford-crypto-pbkdf2.js"
|
|
],
|
|
testGroup: SeaMonsterGroup
|
|
},
|
|
{
|
|
name: "stanford-crypto-sha256",
|
|
files: [
|
|
"./SeaMonster/sjlc.js"
|
|
, "./SeaMonster/stanford-crypto-sha256.js"
|
|
],
|
|
testGroup: SeaMonsterGroup
|
|
},
|
|
{
|
|
name: "json-stringify-inspector",
|
|
files: [
|
|
"./SeaMonster/inspector-json-payload.js"
|
|
, "./SeaMonster/json-stringify-inspector.js"
|
|
],
|
|
iterations: 20,
|
|
worstCaseCount: 2,
|
|
testGroup: SeaMonsterGroup
|
|
},
|
|
{
|
|
name: "json-parse-inspector",
|
|
files: [
|
|
"./SeaMonster/inspector-json-payload.js"
|
|
, "./SeaMonster/json-parse-inspector.js"
|
|
],
|
|
iterations: 20,
|
|
worstCaseCount: 2,
|
|
testGroup: SeaMonsterGroup
|
|
},
|
|
// Wasm
|
|
{
|
|
name: "HashSet-wasm",
|
|
wasmPath: "./wasm/HashSet.wasm",
|
|
files: [
|
|
"./wasm/HashSet.js"
|
|
],
|
|
preload: {
|
|
wasmBlobURL: "./wasm/HashSet.wasm"
|
|
},
|
|
benchmarkClass: WasmBenchmark,
|
|
testGroup: WasmGroup
|
|
},
|
|
{
|
|
name: "tsf-wasm",
|
|
wasmPath: "./wasm/tsf.wasm",
|
|
files: [
|
|
"./wasm/tsf.js"
|
|
],
|
|
preload: {
|
|
wasmBlobURL: "./wasm/tsf.wasm"
|
|
},
|
|
benchmarkClass: WasmBenchmark,
|
|
testGroup: WasmGroup
|
|
},
|
|
{
|
|
name: "quicksort-wasm",
|
|
wasmPath: "./wasm/quicksort.wasm",
|
|
files: [
|
|
"./wasm/quicksort.js"
|
|
],
|
|
preload: {
|
|
wasmBlobURL: "./wasm/quicksort.wasm"
|
|
},
|
|
benchmarkClass: WasmBenchmark,
|
|
testGroup: WasmGroup
|
|
},
|
|
{
|
|
name: "gcc-loops-wasm",
|
|
wasmPath: "./wasm/gcc-loops.wasm",
|
|
files: [
|
|
"./wasm/gcc-loops.js"
|
|
],
|
|
preload: {
|
|
wasmBlobURL: "./wasm/gcc-loops.wasm"
|
|
},
|
|
benchmarkClass: WasmBenchmark,
|
|
testGroup: WasmGroup
|
|
},
|
|
{
|
|
name: "richards-wasm",
|
|
wasmPath: "./wasm/richards.wasm",
|
|
files: [
|
|
"./wasm/richards.js"
|
|
],
|
|
preload: {
|
|
wasmBlobURL: "./wasm/richards.wasm"
|
|
},
|
|
benchmarkClass: WasmBenchmark,
|
|
testGroup: WasmGroup
|
|
},
|
|
// WorkerTests
|
|
{
|
|
name: "bomb-workers",
|
|
files: [
|
|
"./worker/bomb.js"
|
|
],
|
|
iterations: 80,
|
|
preload: {
|
|
rayTrace3D: "./worker/bomb-subtests/3d-raytrace.js"
|
|
, accessNbody: "./worker/bomb-subtests/access-nbody.js"
|
|
, morph3D: "./worker/bomb-subtests/3d-morph.js"
|
|
, cube3D: "./worker/bomb-subtests/3d-cube.js"
|
|
, accessFunnkuch: "./worker/bomb-subtests/access-fannkuch.js"
|
|
, accessBinaryTrees: "./worker/bomb-subtests/access-binary-trees.js"
|
|
, accessNsieve: "./worker/bomb-subtests/access-nsieve.js"
|
|
, bitopsBitwiseAnd: "./worker/bomb-subtests/bitops-bitwise-and.js"
|
|
, bitopsNsieveBits: "./worker/bomb-subtests/bitops-nsieve-bits.js"
|
|
, controlflowRecursive: "./worker/bomb-subtests/controlflow-recursive.js"
|
|
, bitops3BitBitsInByte: "./worker/bomb-subtests/bitops-3bit-bits-in-byte.js"
|
|
, botopsBitsInByte: "./worker/bomb-subtests/bitops-bits-in-byte.js"
|
|
, cryptoAES: "./worker/bomb-subtests/crypto-aes.js"
|
|
, cryptoMD5: "./worker/bomb-subtests/crypto-md5.js"
|
|
, cryptoSHA1: "./worker/bomb-subtests/crypto-sha1.js"
|
|
, dateFormatTofte: "./worker/bomb-subtests/date-format-tofte.js"
|
|
, dateFormatXparb: "./worker/bomb-subtests/date-format-xparb.js"
|
|
, mathCordic: "./worker/bomb-subtests/math-cordic.js"
|
|
, mathPartialSums: "./worker/bomb-subtests/math-partial-sums.js"
|
|
, mathSpectralNorm: "./worker/bomb-subtests/math-spectral-norm.js"
|
|
, stringBase64: "./worker/bomb-subtests/string-base64.js"
|
|
, stringFasta: "./worker/bomb-subtests/string-fasta.js"
|
|
, stringValidateInput: "./worker/bomb-subtests/string-validate-input.js"
|
|
, stringTagcloud: "./worker/bomb-subtests/string-tagcloud.js"
|
|
, stringUnpackCode: "./worker/bomb-subtests/string-unpack-code.js"
|
|
, regexpDNA: "./worker/bomb-subtests/regexp-dna.js"
|
|
},
|
|
benchmarkClass: AsyncBenchmark,
|
|
testGroup: WorkerTestsGroup
|
|
},
|
|
{
|
|
name: "segmentation",
|
|
files: [
|
|
"./worker/segmentation.js"
|
|
],
|
|
preload: {
|
|
asyncTaskBlob: "./worker/async-task.js"
|
|
},
|
|
iterations: 36,
|
|
worstCaseCount: 3,
|
|
benchmarkClass: AsyncBenchmark,
|
|
testGroup: WorkerTestsGroup
|
|
},
|
|
// WSL
|
|
{
|
|
name: "WSL",
|
|
files: ["./WSL/Node.js" ,"./WSL/Type.js" ,"./WSL/ReferenceType.js" ,"./WSL/Value.js" ,"./WSL/Expression.js" ,"./WSL/Rewriter.js" ,"./WSL/Visitor.js" ,"./WSL/CreateLiteral.js" ,"./WSL/CreateLiteralType.js" ,"./WSL/PropertyAccessExpression.js" ,"./WSL/AddressSpace.js" ,"./WSL/AnonymousVariable.js" ,"./WSL/ArrayRefType.js" ,"./WSL/ArrayType.js" ,"./WSL/Assignment.js" ,"./WSL/AutoWrapper.js" ,"./WSL/Block.js" ,"./WSL/BoolLiteral.js" ,"./WSL/Break.js" ,"./WSL/CallExpression.js" ,"./WSL/CallFunction.js" ,"./WSL/Check.js" ,"./WSL/CheckLiteralTypes.js" ,"./WSL/CheckLoops.js" ,"./WSL/CheckRecursiveTypes.js" ,"./WSL/CheckRecursion.js" ,"./WSL/CheckReturns.js" ,"./WSL/CheckUnreachableCode.js" ,"./WSL/CheckWrapped.js" ,"./WSL/Checker.js" ,"./WSL/CloneProgram.js" ,"./WSL/CommaExpression.js" ,"./WSL/ConstexprFolder.js" ,"./WSL/ConstexprTypeParameter.js" ,"./WSL/Continue.js" ,"./WSL/ConvertPtrToArrayRefExpression.js" ,"./WSL/DereferenceExpression.js" ,"./WSL/DoWhileLoop.js" ,"./WSL/DotExpression.js" ,"./WSL/DoubleLiteral.js" ,"./WSL/DoubleLiteralType.js" ,"./WSL/EArrayRef.js" ,"./WSL/EBuffer.js" ,"./WSL/EBufferBuilder.js" ,"./WSL/EPtr.js" ,"./WSL/EnumLiteral.js" ,"./WSL/EnumMember.js" ,"./WSL/EnumType.js" ,"./WSL/EvaluationCommon.js" ,"./WSL/Evaluator.js" ,"./WSL/ExpressionFinder.js" ,"./WSL/ExternalOrigin.js" ,"./WSL/Field.js" ,"./WSL/FindHighZombies.js" ,"./WSL/FlattenProtocolExtends.js" ,"./WSL/FlattenedStructOffsetGatherer.js" ,"./WSL/FloatLiteral.js" ,"./WSL/FloatLiteralType.js" ,"./WSL/FoldConstexprs.js" ,"./WSL/ForLoop.js" ,"./WSL/Func.js" ,"./WSL/FuncDef.js" ,"./WSL/FuncInstantiator.js" ,"./WSL/FuncParameter.js" ,"./WSL/FunctionLikeBlock.js" ,"./WSL/HighZombieFinder.js" ,"./WSL/IdentityExpression.js" ,"./WSL/IfStatement.js" ,"./WSL/IndexExpression.js" ,"./WSL/InferTypesForCall.js" ,"./WSL/Inline.js" ,"./WSL/Inliner.js" ,"./WSL/InstantiateImmediates.js" ,"./WSL/IntLiteral.js" ,"./WSL/IntLiteralType.js" ,"./WSL/Intrinsics.js" ,"./WSL/LateChecker.js" ,"./WSL/Lexer.js" ,"./WSL/LexerToken.js" ,"./WSL/LiteralTypeChecker.js" ,"./WSL/LogicalExpression.js" ,"./WSL/LogicalNot.js" ,"./WSL/LoopChecker.js" ,"./WSL/MakeArrayRefExpression.js" ,"./WSL/MakePtrExpression.js" ,"./WSL/NameContext.js" ,"./WSL/NameFinder.js" ,"./WSL/NameResolver.js" ,"./WSL/NativeFunc.js" ,"./WSL/NativeFuncInstance.js" ,"./WSL/NativeType.js" ,"./WSL/NativeTypeInstance.js" ,"./WSL/NormalUsePropertyResolver.js" ,"./WSL/NullLiteral.js" ,"./WSL/NullType.js" ,"./WSL/OriginKind.js" ,"./WSL/OverloadResolutionFailure.js" ,"./WSL/Parse.js" ,"./WSL/Prepare.js" ,"./WSL/Program.js" ,"./WSL/ProgramWithUnnecessaryThingsRemoved.js" ,"./WSL/PropertyResolver.js" ,"./WSL/Protocol.js" ,"./WSL/ProtocolDecl.js" ,"./WSL/ProtocolFuncDecl.js" ,"./WSL/ProtocolRef.js" ,"./WSL/PtrType.js" ,"./WSL/ReadModifyWriteExpression.js" ,"./WSL/RecursionChecker.js" ,"./WSL/RecursiveTypeChecker.js" ,"./WSL/ResolveNames.js" ,"./WSL/ResolveOverloadImpl.js" ,"./WSL/ResolveProperties.js" ,"./WSL/ResolveTypeDefs.js" ,"./WSL/Return.js" ,"./WSL/ReturnChecker.js" ,"./WSL/ReturnException.js" ,"./WSL/StandardLibrary.js" ,"./WSL/StatementCloner.js" ,"./WSL/StructLayoutBuilder.js" ,"./WSL/StructType.js" ,"./WSL/Substitution.js" ,"./WSL/SwitchCase.js" ,"./WSL/SwitchStatement.js" ,"./WSL/SynthesizeEnumFunctions.js" ,"./WSL/SynthesizeStructAccessors.js" ,"./WSL/TrapStatement.js" ,"./WSL/TypeDef.js" ,"./WSL/TypeDefResolver.js" ,"./WSL/TypeOrVariableRef.js" ,"./WSL/TypeParameterRewriter.js" ,"./WSL/TypeRef.js" ,"./WSL/TypeVariable.js" ,"./WSL/TypeVariableTracker.js" ,"./WSL/TypedValue.js" ,"./WSL/UintLiteral.js" ,"./WSL/UintLiteralType.js" ,"./WSL/UnificationContext.js" ,"./WSL/UnreachableCodeChecker.js" ,"./WSL/VariableDecl.js" ,"./WSL/VariableRef.js" ,"./WSL/VisitingSet.js" ,"./WSL/WSyntaxError.js" ,"./WSL/WTrapError.js" ,"./WSL/WTypeError.js" ,"./WSL/WhileLoop.js" ,"./WSL/WrapChecker.js", "./WSL/Test.js"],
|
|
benchmarkClass: WSLBenchmark,
|
|
testGroup: WSLGroup
|
|
}
|
|
];
|
|
|
|
// LuaJSFight tests
|
|
let luaJSFightTests = [
|
|
"hello_world"
|
|
, "list_search"
|
|
, "lists"
|
|
, "string_lists"
|
|
];
|
|
for (let test of luaJSFightTests) {
|
|
testPlans.push({
|
|
name: `${test}-LJF`,
|
|
files: [
|
|
`./LuaJSFight/${test}.js`
|
|
],
|
|
testGroup: LuaJSFightGroup
|
|
});
|
|
}
|
|
|
|
// SunSpider tests
|
|
let sunSpiderTests = [
|
|
"3d-cube"
|
|
, "3d-raytrace"
|
|
, "base64"
|
|
, "crypto-aes"
|
|
, "crypto-md5"
|
|
, "crypto-sha1"
|
|
, "date-format-tofte"
|
|
, "date-format-xparb"
|
|
, "n-body"
|
|
, "regex-dna"
|
|
, "string-unpack-code"
|
|
, "tagcloud"
|
|
];
|
|
for (let test of sunSpiderTests) {
|
|
testPlans.push({
|
|
name: `${test}-SP`,
|
|
files: [
|
|
`./SunSpider/${test}.js`
|
|
],
|
|
testGroup: SunSpiderGroup
|
|
});
|
|
}
|
|
|
|
// WTB (Web Tooling Benchmark) tests
|
|
let WTBTests = [
|
|
"acorn"
|
|
, "babylon"
|
|
, "chai"
|
|
, "coffeescript"
|
|
, "espree"
|
|
, "jshint"
|
|
, "lebab"
|
|
, "prepack"
|
|
, "uglify-js"
|
|
];
|
|
for (let name of WTBTests) {
|
|
testPlans.push({
|
|
name: `${name}-wtb`,
|
|
files: [
|
|
isInBrowser ? "./web-tooling-benchmark/browser.js" : "./web-tooling-benchmark/cli.js"
|
|
, `./web-tooling-benchmark/${name}.js`
|
|
],
|
|
iterations: 5,
|
|
worstCaseCount: 1,
|
|
testGroup: WTBGroup
|
|
});
|
|
}
|
|
|
|
|
|
let testsByName = new Map();
|
|
let testsByGroup = new Map();
|
|
|
|
for (let plan of testPlans) {
|
|
let testName = plan.name;
|
|
|
|
if (testsByName.has(plan.name))
|
|
throw "Duplicate test plan with name \"" + testName + "\"";
|
|
else
|
|
testsByName.set(testName, plan);
|
|
|
|
let group = plan.testGroup;
|
|
|
|
if (testsByGroup.has(group))
|
|
testsByGroup.get(group).push(testName);
|
|
else
|
|
testsByGroup.set(group, [testName]);
|
|
}
|
|
|
|
this.JetStream = new Driver();
|
|
|
|
function addTestByName(testName)
|
|
{
|
|
let plan = testsByName.get(testName);
|
|
|
|
if (plan)
|
|
JetStream.addPlan(plan, plan.benchmarkClass);
|
|
else
|
|
throw "Couldn't find test named \"" + testName + "\"";
|
|
}
|
|
|
|
function addTestsByGroup(group)
|
|
{
|
|
let testList = testsByGroup.get(group);
|
|
|
|
if (!testList)
|
|
throw "Couldn't find test group named: \"" + Symbol.keyFor(group) + "\"";
|
|
|
|
for (let testName of testList)
|
|
addTestByName(testName);
|
|
}
|
|
|
|
function processTestList(testList)
|
|
{
|
|
let tests = [];
|
|
|
|
if (testList instanceof Array)
|
|
tests = testList;
|
|
else
|
|
tests = testList.split(/[\s,]/);
|
|
|
|
for (let testName of tests) {
|
|
let groupTest = testsByGroup.get(Symbol.for(testName));
|
|
|
|
if (groupTest) {
|
|
for (let testName of groupTest)
|
|
addTestByName(testName);
|
|
} else
|
|
addTestByName(testName);
|
|
}
|
|
}
|
|
|
|
let runOctane = true;
|
|
let runARES = true;
|
|
let runWSL = true;
|
|
let runRexBench = true;
|
|
let runWTB = true;
|
|
let runSunSpider = true;
|
|
let runSimple = true;
|
|
let runCDJS = true;
|
|
let runWorkerTests = !!isInBrowser;
|
|
let runSeaMonster = true;
|
|
let runCodeLoad = true;
|
|
let runWasm = true;
|
|
if (typeof WebAssembly === "undefined")
|
|
runWasm = false;
|
|
|
|
if (false) {
|
|
runOctane = false;
|
|
runARES = false;
|
|
runWSL = false;
|
|
runRexBench = false;
|
|
runWTB = false;
|
|
runSunSpider = false;
|
|
runSimple = false;
|
|
runCDJS = false;
|
|
runWorkerTests = false;
|
|
runSeaMonster = false;
|
|
runCodeLoad = false;
|
|
runWasm = false;
|
|
}
|
|
|
|
if (typeof testList !== "undefined") {
|
|
processTestList(testList);
|
|
} else {
|
|
if (runARES)
|
|
addTestsByGroup(ARESGroup);
|
|
|
|
if (runCDJS)
|
|
addTestsByGroup(CDJSGroup);
|
|
|
|
if (runCodeLoad)
|
|
addTestsByGroup(CodeLoadGroup);
|
|
|
|
if (runOctane)
|
|
addTestsByGroup(OctaneGroup);
|
|
|
|
if (runRexBench)
|
|
addTestsByGroup(RexBenchGroup);
|
|
|
|
if (runSeaMonster)
|
|
addTestsByGroup(SeaMonsterGroup);
|
|
|
|
if (runSimple)
|
|
addTestsByGroup(SimpleGroup);
|
|
|
|
if (runSunSpider)
|
|
addTestsByGroup(SunSpiderGroup);
|
|
|
|
if (runWasm)
|
|
addTestsByGroup(WasmGroup);
|
|
|
|
if (runWorkerTests)
|
|
addTestsByGroup(WorkerTestsGroup);
|
|
|
|
if (runWSL)
|
|
addTestsByGroup(WSLGroup);
|
|
|
|
if (runWTB)
|
|
addTestsByGroup(WTBGroup);
|
|
}
|