Add branch for JetStream v2.1
This is a copy of JetStream v2.1 as of https://commits.webkit.org/254294@main
This commit is contained in:
parent
60cdba17be
commit
1466de09a5
16 changed files with 1441 additions and 145 deletions
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2018-2022 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
|
@ -222,10 +222,8 @@ a.button {
|
|||
font-weight: 500;
|
||||
letter-spacing: -0.10rem;
|
||||
color: transparent;
|
||||
background-image: linear-gradient(132deg, #FFFFFF 0%, #FFFFFF 2%, #86D9FF 42%, #8BDAFF 84%, #FFFFFF 98%, #FFFFFF 100%);
|
||||
background-image: linear-gradient(132deg, #96E5FF 0%, #96E5FF 2%, #86D9FF 42%, #8BDAFF 84%, #96E5FF 98%, #96E5FF 100%);
|
||||
-webkit-background-clip: text;
|
||||
animation: shine 2s ease-in-out infinite;
|
||||
animation-delay: 2s;
|
||||
background-size: 1200px 100%;
|
||||
background-repeat: no-repeat;
|
||||
-webkit-touch-callout: none;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2018-2022 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
|
@ -25,7 +25,6 @@
|
|||
* 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")
|
||||
|
|
@ -34,6 +33,15 @@ if (typeof RAMification === "undefined")
|
|||
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;
|
||||
|
|
@ -41,6 +49,49 @@ 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);
|
||||
|
|
@ -124,11 +175,26 @@ const fileLoader = (function() {
|
|||
if (!isInBrowser)
|
||||
return Promise.resolve(readFile(url));
|
||||
|
||||
let fetchResponse = await fetch(new Request(url));
|
||||
let response;
|
||||
let tries = 3;
|
||||
while (tries--) {
|
||||
let hasError = false;
|
||||
try {
|
||||
response = await fetch(url);
|
||||
} catch (e) {
|
||||
hasError = true;
|
||||
}
|
||||
if (!hasError && response.ok)
|
||||
break;
|
||||
if (tries)
|
||||
continue;
|
||||
window.allIsGood = false;
|
||||
throw new Error("Fetch failed");
|
||||
}
|
||||
if (url.indexOf(".js") !== -1)
|
||||
return await fetchResponse.text();
|
||||
return await response.text();
|
||||
else if (url.indexOf(".wasm") !== -1)
|
||||
return await fetchResponse.arrayBuffer();
|
||||
return await response.arrayBuffer();
|
||||
|
||||
throw new Error("should not be reached!");
|
||||
}
|
||||
|
|
@ -148,6 +214,12 @@ const fileLoader = (function() {
|
|||
class Driver {
|
||||
constructor() {
|
||||
this.benchmarks = [];
|
||||
this.blobDataCache = { };
|
||||
this.loadCache = { };
|
||||
this.counter = { };
|
||||
this.counter.loadedResources = 0;
|
||||
this.counter.totalResources = 0;
|
||||
this.counter.failedPreloadResources = 0;
|
||||
}
|
||||
|
||||
addPlan(plan, BenchmarkClass = DefaultBenchmark) {
|
||||
|
|
@ -174,7 +246,6 @@ class Driver {
|
|||
await updateUI();
|
||||
|
||||
try {
|
||||
|
||||
await benchmark.run();
|
||||
} catch(e) {
|
||||
JetStream.reportError(benchmark);
|
||||
|
|
@ -182,6 +253,16 @@ class Driver {
|
|||
}
|
||||
|
||||
benchmark.updateUIAfterRun();
|
||||
|
||||
if (isInBrowser) {
|
||||
let cache = JetStream.blobDataCache;
|
||||
for (let file of benchmark.plan.files) {
|
||||
let blobData = cache[file];
|
||||
blobData.refCount--;
|
||||
if (!blobData.refCount)
|
||||
cache[file] = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let totalTime = Date.now() - start;
|
||||
|
|
@ -197,21 +278,54 @@ class Driver {
|
|||
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
|
||||
} 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 = runString("");
|
||||
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,
|
||||
|
|
@ -219,7 +333,8 @@ class Driver {
|
|||
};
|
||||
for (let script of scripts)
|
||||
globalObject.loadString(script);
|
||||
return globalObject;
|
||||
|
||||
return isD8 ? realm : globalObject;
|
||||
}
|
||||
|
||||
var magic = document.getElementById("magic");
|
||||
|
|
@ -286,6 +401,7 @@ class Driver {
|
|||
}
|
||||
|
||||
async initialize() {
|
||||
await this.prefetchResourcesForBrowser();
|
||||
await this.fetchResources();
|
||||
this.prepareToRun();
|
||||
if (isInBrowser && window.location.search == '?report=true') {
|
||||
|
|
@ -293,9 +409,40 @@ class Driver {
|
|||
}
|
||||
}
|
||||
|
||||
async fetchResources() {
|
||||
async prefetchResourcesForBrowser() {
|
||||
if (!isInBrowser)
|
||||
return;
|
||||
|
||||
var promises = [];
|
||||
for (let benchmark of this.benchmarks)
|
||||
await benchmark.fetchResources();
|
||||
promises.push(benchmark.prefetchResourcesForBrowser());
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
let counter = JetStream.counter;
|
||||
if (counter.failedPreloadResources || counter.loadedResources != counter.totalResources) {
|
||||
for (let benchmark of this.benchmarks) {
|
||||
let allFilesLoaded = await benchmark.retryPrefetchResourcesForBrowser(counter);
|
||||
if (allFilesLoaded)
|
||||
break;
|
||||
}
|
||||
|
||||
if (counter.failedPreloadResources || counter.loadedResources != counter.totalResources) {
|
||||
// If we've failed to prefetch resources even after a sequential 1 by 1 retry,
|
||||
// then fail out early rather than letting subtests fail with a hang.
|
||||
window.allIsGood = false;
|
||||
throw new Error("Fetch failed");
|
||||
}
|
||||
}
|
||||
|
||||
JetStream.loadCache = { }; // Done preloading all the files.
|
||||
}
|
||||
|
||||
async fetchResources() {
|
||||
var promises = [];
|
||||
for (let benchmark of this.benchmarks)
|
||||
promises.push(benchmark.fetchResources());
|
||||
await Promise.all(promises);
|
||||
|
||||
if (!isInBrowser)
|
||||
return;
|
||||
|
|
@ -310,14 +457,8 @@ class Driver {
|
|||
}
|
||||
}
|
||||
|
||||
async reportScoreToRunBenchmarkRunner()
|
||||
resultsJSON()
|
||||
{
|
||||
if (!isInBrowser)
|
||||
return;
|
||||
|
||||
if (window.location.search !== '?report=true')
|
||||
return;
|
||||
|
||||
let results = {};
|
||||
for (let benchmark of this.benchmarks) {
|
||||
const subResults = {}
|
||||
|
|
@ -331,12 +472,32 @@ class Driver {
|
|||
"Time": ["Geometric"],
|
||||
},
|
||||
"tests": subResults,
|
||||
};;
|
||||
};
|
||||
}
|
||||
|
||||
results = {"JetStream2.0": {"metrics" : {"Score" : ["Geometric"]}, "tests" : results}};
|
||||
|
||||
const content = JSON.stringify(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: {
|
||||
|
|
@ -353,7 +514,7 @@ class Benchmark {
|
|||
constructor(plan)
|
||||
{
|
||||
this.plan = plan;
|
||||
this.iterations = testIterationCount || plan.iterations || defaultIterationCount;
|
||||
this.iterations = getIterationCount(plan);
|
||||
this.isAsync = !!plan.isAsync;
|
||||
|
||||
this.scripts = null;
|
||||
|
|
@ -446,14 +607,15 @@ class Benchmark {
|
|||
if (prerunCode)
|
||||
addScript(prerunCode);
|
||||
|
||||
if (preloadResources) {
|
||||
if (!isInBrowser) {
|
||||
assert(this.scripts && this.scripts.length === this.plan.files.length);
|
||||
|
||||
for (let text of this.scripts)
|
||||
addScript(text);
|
||||
} else {
|
||||
let cache = JetStream.blobDataCache;
|
||||
for (let file of this.plan.files)
|
||||
addScriptWithURL(file);
|
||||
addScriptWithURL(cache[file].blobURL);
|
||||
}
|
||||
|
||||
let promise = new Promise((resolve, reject) => {
|
||||
|
|
@ -494,27 +656,163 @@ class Benchmark {
|
|||
this.processResults(results);
|
||||
if (isInBrowser)
|
||||
magicFrame.contentDocument.close();
|
||||
else if (isD8)
|
||||
Realm.dispose(magicFrame);
|
||||
}
|
||||
|
||||
async doLoadBlob(resource) {
|
||||
let response;
|
||||
let tries = 3;
|
||||
while (tries--) {
|
||||
let hasError = false;
|
||||
try {
|
||||
response = await fetch(resource, { cache: "no-store" });
|
||||
} catch (e) {
|
||||
hasError = true;
|
||||
}
|
||||
if (!hasError && response.ok)
|
||||
break;
|
||||
if (tries)
|
||||
continue;
|
||||
throw new Error("Fetch failed");
|
||||
}
|
||||
let blob = await response.blob();
|
||||
var blobData = JetStream.blobDataCache[resource];
|
||||
blobData.blob = blob;
|
||||
blobData.blobURL = URL.createObjectURL(blob);
|
||||
return blobData;
|
||||
}
|
||||
|
||||
async loadBlob(type, prop, resource, incrementRefCount = true) {
|
||||
var blobData = JetStream.blobDataCache[resource];
|
||||
if (!blobData) {
|
||||
blobData = {
|
||||
type: type,
|
||||
prop: prop,
|
||||
resource: resource,
|
||||
blob: null,
|
||||
blobURL: null,
|
||||
refCount: 0
|
||||
};
|
||||
JetStream.blobDataCache[resource] = blobData;
|
||||
}
|
||||
|
||||
if (incrementRefCount)
|
||||
blobData.refCount++;
|
||||
|
||||
let promise = JetStream.loadCache[resource];
|
||||
if (promise)
|
||||
return await promise;
|
||||
|
||||
promise = this.doLoadBlob(resource);
|
||||
JetStream.loadCache[resource] = promise;
|
||||
return await promise;
|
||||
}
|
||||
|
||||
updateCounter() {
|
||||
let counter = JetStream.counter;
|
||||
++counter.loadedResources;
|
||||
var statusElement = document.getElementById("status");
|
||||
statusElement.innerHTML = `Loading ${counter.loadedResources} of ${counter.totalResources} ...`;
|
||||
}
|
||||
|
||||
prefetchResourcesForBrowser() {
|
||||
if (!isInBrowser)
|
||||
return;
|
||||
let promises = this.plan.files.map((file) => this.loadBlob("file", null, file).then((blobData) => {
|
||||
if (!window.allIsGood)
|
||||
return;
|
||||
this.updateCounter();
|
||||
}).catch((error) => {
|
||||
// We'll try again later in retryPrefetchResourceForBrowser(). Don't throw an error.
|
||||
}));
|
||||
|
||||
if (this.plan.preload) {
|
||||
this.preloads = [];
|
||||
for (let prop of Object.getOwnPropertyNames(this.plan.preload)) {
|
||||
promises.push(this.loadBlob("preload", prop, this.plan.preload[prop]).then((blobData) => {
|
||||
if (!window.allIsGood)
|
||||
return;
|
||||
this.preloads.push([ blobData.prop, blobData.blobURL ]);
|
||||
this.updateCounter();
|
||||
}).catch((error) => {
|
||||
// We'll try again later in retryPrefetchResourceForBrowser(). Don't throw an error.
|
||||
if (!this.failedPreloads)
|
||||
this.failedPreloads = { };
|
||||
this.failedPreloads[prop] = true;
|
||||
JetStream.counter.failedPreloadResources++;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
JetStream.counter.totalResources += promises.length;
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
async retryPrefetchResource(type, prop, file) {
|
||||
let counter = JetStream.counter;
|
||||
var blobData = JetStream.blobDataCache[file];
|
||||
if (blobData.blob) {
|
||||
// The same preload blob may be used by multiple subtests. Though the blob is already loaded,
|
||||
// we still need to check if this subtest failed to load it before. If so, handle accordingly.
|
||||
if (type == "preload") {
|
||||
if (this.failedPreloads && this.failedPreloads[blobData.prop]) {
|
||||
this.failedPreloads[blobData.prop] = false;
|
||||
this.preloads.push([ blobData.prop, blobData.blobURL ]);
|
||||
counter.failedPreloadResources--;
|
||||
}
|
||||
}
|
||||
return !counter.failedPreloadResources && counter.loadedResources == counter.totalResources;
|
||||
}
|
||||
|
||||
// Retry fetching the resource.
|
||||
JetStream.loadCache[file] = null;
|
||||
await this.loadBlob(type, prop, file, false).then((blobData) => {
|
||||
if (!window.allIsGood)
|
||||
return;
|
||||
if (blobData.type == "preload")
|
||||
this.preloads.push([ blobData.prop, blobData.blobURL ]);
|
||||
this.updateCounter();
|
||||
});
|
||||
|
||||
if (!blobData.blob) {
|
||||
window.allIsGood = false;
|
||||
throw new Error("Fetch failed");
|
||||
}
|
||||
|
||||
return !counter.failedPreloadResources && counter.loadedResources == counter.totalResources;
|
||||
}
|
||||
|
||||
async retryPrefetchResourcesForBrowser() {
|
||||
if (!isInBrowser)
|
||||
return;
|
||||
|
||||
let counter = JetStream.counter;
|
||||
for (let resource of this.plan.files) {
|
||||
let allDone = await this.retryPrefetchResource("file", null, resource);
|
||||
if (allDone)
|
||||
return true; // All resources loaded, nothing more to do.
|
||||
}
|
||||
|
||||
if (this.plan.preload) {
|
||||
for (let prop of Object.getOwnPropertyNames(this.plan.preload)) {
|
||||
let resource = this.plan.preload[prop];
|
||||
let allDone = await this.retryPrefetchResource("preload", prop, resource);
|
||||
if (allDone)
|
||||
return true; // All resources loaded, nothing more to do.
|
||||
}
|
||||
}
|
||||
return !counter.failedPreloadResources && counter.loadedResources == counter.totalResources;
|
||||
}
|
||||
|
||||
fetchResources() {
|
||||
if (this._resourcesPromise)
|
||||
return this._resourcesPromise;
|
||||
|
||||
let filePromises = preloadResources ? this.plan.files.map((file) => fileLoader.load(file)) : [];
|
||||
let preloads = [];
|
||||
let preloadVariableNames = [];
|
||||
let filePromises = !isInBrowser ? this.plan.files.map((file) => fileLoader.load(file)) : [];
|
||||
|
||||
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)
|
||||
let promise = Promise.all(filePromises).then((texts) => {
|
||||
if (isInBrowser)
|
||||
return;
|
||||
this.scripts = [];
|
||||
assert(texts.length === this.plan.files.length);
|
||||
|
|
@ -522,26 +820,10 @@ class Benchmark {
|
|||
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];
|
||||
this.preloads = [];
|
||||
this.blobs = [];
|
||||
|
||||
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]);
|
||||
this._resourcesPromise = promise;
|
||||
return this._resourcesPromise;
|
||||
}
|
||||
|
||||
|
|
@ -578,7 +860,7 @@ class DefaultBenchmark extends Benchmark {
|
|||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
this.worstCaseCount = this.plan.worstCaseCount || defaultWorstCaseCount;
|
||||
this.worstCaseCount = getWorstCaseCount(this.plan);
|
||||
this.firstIteration = null;
|
||||
this.worst4 = null;
|
||||
this.average = null;
|
||||
|
|
@ -638,15 +920,15 @@ class DefaultBenchmark extends Benchmark {
|
|||
return;
|
||||
}
|
||||
|
||||
print(" Startup:", uiFriendlyNumber(this.firstIteration));
|
||||
print(" Worst Case:", uiFriendlyNumber(this.worst4));
|
||||
print(" Average:", uiFriendlyNumber(this.average));
|
||||
print(" Score:", uiFriendlyNumber(this.score));
|
||||
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) {
|
||||
print(" Current Footprint:", uiFriendlyNumber(this.currentFootprint));
|
||||
print(" Peak Footprint:", uiFriendlyNumber(this.peakFootprint));
|
||||
console.log(" Current Footprint:", uiFriendlyNumber(this.currentFootprint));
|
||||
console.log(" Peak Footprint:", uiFriendlyNumber(this.peakFootprint));
|
||||
}
|
||||
print(" Wall time:", uiFriendlyDuration(new Date(this.endTime - this.startTime)));
|
||||
console.log(" Wall time:", uiFriendlyDuration(new Date(this.endTime - this.startTime)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -732,14 +1014,14 @@ class WSLBenchmark extends Benchmark {
|
|||
return;
|
||||
}
|
||||
|
||||
print(" Stdlib:", uiFriendlyNumber(this.stdlib));
|
||||
print(" Tests:", uiFriendlyNumber(this.mainRun));
|
||||
print(" Score:", uiFriendlyNumber(this.score));
|
||||
console.log(" Stdlib:", uiFriendlyNumber(this.stdlib));
|
||||
console.log(" Tests:", uiFriendlyNumber(this.mainRun));
|
||||
console.log(" Score:", uiFriendlyNumber(this.score));
|
||||
if (RAMification) {
|
||||
print(" Current Footprint:", uiFriendlyNumber(this.currentFootprint));
|
||||
print(" Peak Footprint:", uiFriendlyNumber(this.peakFootprint));
|
||||
console.log(" Current Footprint:", uiFriendlyNumber(this.currentFootprint));
|
||||
console.log(" Peak Footprint:", uiFriendlyNumber(this.peakFootprint));
|
||||
}
|
||||
print(" Wall time:", uiFriendlyDuration(new Date(this.endTime - this.startTime)));
|
||||
console.log(" Wall time:", uiFriendlyDuration(new Date(this.endTime - this.startTime)));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -888,13 +1170,13 @@ class WasmBenchmark extends Benchmark {
|
|||
document.getElementById(this.scoreID).innerHTML = uiFriendlyNumber(this.score);
|
||||
return;
|
||||
}
|
||||
print(" Startup:", uiFriendlyNumber(this.startupTime));
|
||||
print(" Run time:", uiFriendlyNumber(this.runTime));
|
||||
console.log(" Startup:", uiFriendlyNumber(this.startupTime));
|
||||
console.log(" Run time:", uiFriendlyNumber(this.runTime));
|
||||
if (RAMification) {
|
||||
print(" Current Footprint:", uiFriendlyNumber(this.currentFootprint));
|
||||
print(" Peak Footprint:", uiFriendlyNumber(this.peakFootprint));
|
||||
console.log(" Current Footprint:", uiFriendlyNumber(this.currentFootprint));
|
||||
console.log(" Peak Footprint:", uiFriendlyNumber(this.peakFootprint));
|
||||
}
|
||||
print(" Score:", uiFriendlyNumber(this.score));
|
||||
console.log(" Score:", uiFriendlyNumber(this.score));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1542,6 +1824,8 @@ let runWorkerTests = !!isInBrowser;
|
|||
let runSeaMonster = true;
|
||||
let runCodeLoad = true;
|
||||
let runWasm = true;
|
||||
if (typeof WebAssembly === "undefined")
|
||||
runWasm = false;
|
||||
|
||||
if (false) {
|
||||
runOctane = false;
|
||||
|
|
|
|||
|
|
@ -1,5 +1 @@
|
|||
class Benchmark {
|
||||
runIteration() {
|
||||
print("Hello world!");
|
||||
}
|
||||
}
|
||||
print("Hello world!");
|
||||
|
|
|
|||
|
|
@ -8,16 +8,5 @@ function firstWhere(list, fn) {
|
|||
}
|
||||
nums = [1, 2, 3, 4, 5, 6, 7];
|
||||
function isEven(x) { return (x & 1) == 0; }
|
||||
|
||||
function run()
|
||||
{
|
||||
firstEven = firstWhere(nums, isEven);
|
||||
print('First even: ' + firstEven)
|
||||
}
|
||||
|
||||
class Benchmark {
|
||||
runIteration() {
|
||||
run();
|
||||
}
|
||||
}
|
||||
|
||||
firstEven = firstWhere(nums, isEven);
|
||||
print('First even: ' + firstEven)
|
||||
|
|
|
|||
|
|
@ -2,17 +2,7 @@ var n = 40000;
|
|||
var i = 0;
|
||||
var items = [];
|
||||
|
||||
function run()
|
||||
{
|
||||
items = [];
|
||||
while (i < n) {
|
||||
items.push(i);
|
||||
i = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
class Benchmark {
|
||||
runIteration() {
|
||||
run();
|
||||
}
|
||||
while (i < n) {
|
||||
items.push(i);
|
||||
i = i + 1;
|
||||
}
|
||||
|
|
|
|||
537
LuaJSFight/richards.js
Normal file
537
LuaJSFight/richards.js
Normal file
|
|
@ -0,0 +1,537 @@
|
|||
// Copyright 2006-2008 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * 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.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
|
||||
// OWNER 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.
|
||||
|
||||
|
||||
// This is a JavaScript implementation of the Richards
|
||||
// benchmark from:
|
||||
//
|
||||
// http://www.cl.cam.ac.uk/~mr10/Bench.html
|
||||
//
|
||||
// The benchmark was originally implemented in BCPL by
|
||||
// Martin Richards.
|
||||
|
||||
|
||||
/**
|
||||
* The Richards benchmark simulates the task dispatcher of an
|
||||
* operating system.
|
||||
**/
|
||||
function runRichards() {
|
||||
var scheduler = new Scheduler();
|
||||
scheduler.addIdleTask(ID_IDLE, 0, null, COUNT);
|
||||
|
||||
var queue = new Packet(null, ID_WORKER, KIND_WORK);
|
||||
queue = new Packet(queue, ID_WORKER, KIND_WORK);
|
||||
scheduler.addWorkerTask(ID_WORKER, 1000, queue);
|
||||
|
||||
queue = new Packet(null, ID_DEVICE_A, KIND_DEVICE);
|
||||
queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);
|
||||
queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);
|
||||
scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue);
|
||||
|
||||
queue = new Packet(null, ID_DEVICE_B, KIND_DEVICE);
|
||||
queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);
|
||||
queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);
|
||||
scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue);
|
||||
|
||||
scheduler.addDeviceTask(ID_DEVICE_A, 4000, null);
|
||||
|
||||
scheduler.addDeviceTask(ID_DEVICE_B, 5000, null);
|
||||
|
||||
scheduler.schedule();
|
||||
|
||||
if (scheduler.queueCount != EXPECTED_QUEUE_COUNT ||
|
||||
scheduler.holdCount != EXPECTED_HOLD_COUNT) {
|
||||
var msg =
|
||||
"Error during execution: queueCount = " + scheduler.queueCount +
|
||||
", holdCount = " + scheduler.holdCount + ".";
|
||||
throw new Error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
var COUNT = 1000;
|
||||
|
||||
/**
|
||||
* These two constants specify how many times a packet is queued and
|
||||
* how many times a task is put on hold in a correct run of richards.
|
||||
* They don't have any meaning a such but are characteristic of a
|
||||
* correct run so if the actual queue or hold count is different from
|
||||
* the expected there must be a bug in the implementation.
|
||||
**/
|
||||
var EXPECTED_QUEUE_COUNT = 2322;
|
||||
var EXPECTED_HOLD_COUNT = 928;
|
||||
|
||||
|
||||
/**
|
||||
* A scheduler can be used to schedule a set of tasks based on their relative
|
||||
* priorities. Scheduling is done by maintaining a list of task control blocks
|
||||
* which holds tasks and the data queue they are processing.
|
||||
* @constructor
|
||||
*/
|
||||
function Scheduler() {
|
||||
this.queueCount = 0;
|
||||
this.holdCount = 0;
|
||||
this.blocks = new Array(NUMBER_OF_IDS);
|
||||
this.list = null;
|
||||
this.currentTcb = null;
|
||||
this.currentId = null;
|
||||
}
|
||||
|
||||
var ID_IDLE = 0;
|
||||
var ID_WORKER = 1;
|
||||
var ID_HANDLER_A = 2;
|
||||
var ID_HANDLER_B = 3;
|
||||
var ID_DEVICE_A = 4;
|
||||
var ID_DEVICE_B = 5;
|
||||
var NUMBER_OF_IDS = 6;
|
||||
|
||||
var KIND_DEVICE = 0;
|
||||
var KIND_WORK = 1;
|
||||
|
||||
/**
|
||||
* Add an idle task to this scheduler.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
* @param {int} count the number of times to schedule the task
|
||||
*/
|
||||
Scheduler.prototype.addIdleTask = function (id, priority, queue, count) {
|
||||
this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count));
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a work task to this scheduler.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
*/
|
||||
Scheduler.prototype.addWorkerTask = function (id, priority, queue) {
|
||||
this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0));
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a handler task to this scheduler.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
*/
|
||||
Scheduler.prototype.addHandlerTask = function (id, priority, queue) {
|
||||
this.addTask(id, priority, queue, new HandlerTask(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a handler task to this scheduler.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
*/
|
||||
Scheduler.prototype.addDeviceTask = function (id, priority, queue) {
|
||||
this.addTask(id, priority, queue, new DeviceTask(this))
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the specified task and mark it as running.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
* @param {Task} task the task to add
|
||||
*/
|
||||
Scheduler.prototype.addRunningTask = function (id, priority, queue, task) {
|
||||
this.addTask(id, priority, queue, task);
|
||||
this.currentTcb.setRunning();
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the specified task to this scheduler.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
* @param {Task} task the task to add
|
||||
*/
|
||||
Scheduler.prototype.addTask = function (id, priority, queue, task) {
|
||||
this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task);
|
||||
this.list = this.currentTcb;
|
||||
this.blocks[id] = this.currentTcb;
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute the tasks managed by this scheduler.
|
||||
*/
|
||||
Scheduler.prototype.schedule = function () {
|
||||
this.currentTcb = this.list;
|
||||
while (this.currentTcb != null) {
|
||||
if (this.currentTcb.isHeldOrSuspended()) {
|
||||
this.currentTcb = this.currentTcb.link;
|
||||
} else {
|
||||
this.currentId = this.currentTcb.id;
|
||||
this.currentTcb = this.currentTcb.run();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Release a task that is currently blocked and return the next block to run.
|
||||
* @param {int} id the id of the task to suspend
|
||||
*/
|
||||
Scheduler.prototype.release = function (id) {
|
||||
var tcb = this.blocks[id];
|
||||
if (tcb == null) return tcb;
|
||||
tcb.markAsNotHeld();
|
||||
if (tcb.priority > this.currentTcb.priority) {
|
||||
return tcb;
|
||||
} else {
|
||||
return this.currentTcb;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Block the currently executing task and return the next task control block
|
||||
* to run. The blocked task will not be made runnable until it is explicitly
|
||||
* released, even if new work is added to it.
|
||||
*/
|
||||
Scheduler.prototype.holdCurrent = function () {
|
||||
this.holdCount++;
|
||||
this.currentTcb.markAsHeld();
|
||||
return this.currentTcb.link;
|
||||
};
|
||||
|
||||
/**
|
||||
* Suspend the currently executing task and return the next task control block
|
||||
* to run. If new work is added to the suspended task it will be made runnable.
|
||||
*/
|
||||
Scheduler.prototype.suspendCurrent = function () {
|
||||
this.currentTcb.markAsSuspended();
|
||||
return this.currentTcb;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the specified packet to the end of the worklist used by the task
|
||||
* associated with the packet and make the task runnable if it is currently
|
||||
* suspended.
|
||||
* @param {Packet} packet the packet to add
|
||||
*/
|
||||
Scheduler.prototype.queue = function (packet) {
|
||||
var t = this.blocks[packet.id];
|
||||
if (t == null) return t;
|
||||
this.queueCount++;
|
||||
packet.link = null;
|
||||
packet.id = this.currentId;
|
||||
return t.checkPriorityAdd(this.currentTcb, packet);
|
||||
};
|
||||
|
||||
/**
|
||||
* A task control block manages a task and the queue of work packages associated
|
||||
* with it.
|
||||
* @param {TaskControlBlock} link the preceding block in the linked block list
|
||||
* @param {int} id the id of this block
|
||||
* @param {int} priority the priority of this block
|
||||
* @param {Packet} queue the queue of packages to be processed by the task
|
||||
* @param {Task} task the task
|
||||
* @constructor
|
||||
*/
|
||||
function TaskControlBlock(link, id, priority, queue, task) {
|
||||
this.link = link;
|
||||
this.id = id;
|
||||
this.priority = priority;
|
||||
this.queue = queue;
|
||||
this.task = task;
|
||||
if (queue == null) {
|
||||
this.state = STATE_SUSPENDED;
|
||||
} else {
|
||||
this.state = STATE_SUSPENDED_RUNNABLE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The task is running and is currently scheduled.
|
||||
*/
|
||||
var STATE_RUNNING = 0;
|
||||
|
||||
/**
|
||||
* The task has packets left to process.
|
||||
*/
|
||||
var STATE_RUNNABLE = 1;
|
||||
|
||||
/**
|
||||
* The task is not currently running. The task is not blocked as such and may
|
||||
* be started by the scheduler.
|
||||
*/
|
||||
var STATE_SUSPENDED = 2;
|
||||
|
||||
/**
|
||||
* The task is blocked and cannot be run until it is explicitly released.
|
||||
*/
|
||||
var STATE_HELD = 4;
|
||||
|
||||
var STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE;
|
||||
var STATE_NOT_HELD = ~STATE_HELD;
|
||||
|
||||
TaskControlBlock.prototype.setRunning = function () {
|
||||
this.state = STATE_RUNNING;
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.markAsNotHeld = function () {
|
||||
this.state = this.state & STATE_NOT_HELD;
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.markAsHeld = function () {
|
||||
this.state = this.state | STATE_HELD;
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.isHeldOrSuspended = function () {
|
||||
return (this.state & STATE_HELD) != 0 || (this.state == STATE_SUSPENDED);
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.markAsSuspended = function () {
|
||||
this.state = this.state | STATE_SUSPENDED;
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.markAsRunnable = function () {
|
||||
this.state = this.state | STATE_RUNNABLE;
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs this task, if it is ready to be run, and returns the next task to run.
|
||||
*/
|
||||
TaskControlBlock.prototype.run = function () {
|
||||
var packet;
|
||||
if (this.state == STATE_SUSPENDED_RUNNABLE) {
|
||||
packet = this.queue;
|
||||
this.queue = packet.link;
|
||||
if (this.queue == null) {
|
||||
this.state = STATE_RUNNING;
|
||||
} else {
|
||||
this.state = STATE_RUNNABLE;
|
||||
}
|
||||
} else {
|
||||
packet = null;
|
||||
}
|
||||
return this.task.run(packet);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a packet to the worklist of this block's task, marks this as runnable if
|
||||
* necessary, and returns the next runnable object to run (the one
|
||||
* with the highest priority).
|
||||
*/
|
||||
TaskControlBlock.prototype.checkPriorityAdd = function (task, packet) {
|
||||
if (this.queue == null) {
|
||||
this.queue = packet;
|
||||
this.markAsRunnable();
|
||||
if (this.priority > task.priority) return this;
|
||||
} else {
|
||||
this.queue = packet.addTo(this.queue);
|
||||
}
|
||||
return task;
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.toString = function () {
|
||||
return "tcb { " + this.task + "@" + this.state + " }";
|
||||
};
|
||||
|
||||
/**
|
||||
* An idle task doesn't do any work itself but cycles control between the two
|
||||
* device tasks.
|
||||
* @param {Scheduler} scheduler the scheduler that manages this task
|
||||
* @param {int} v1 a seed value that controls how the device tasks are scheduled
|
||||
* @param {int} count the number of times this task should be scheduled
|
||||
* @constructor
|
||||
*/
|
||||
function IdleTask(scheduler, v1, count) {
|
||||
this.scheduler = scheduler;
|
||||
this.v1 = v1;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
IdleTask.prototype.run = function (packet) {
|
||||
this.count--;
|
||||
if (this.count == 0) return this.scheduler.holdCurrent();
|
||||
if ((this.v1 & 1) == 0) {
|
||||
this.v1 = this.v1 >> 1;
|
||||
return this.scheduler.release(ID_DEVICE_A);
|
||||
} else {
|
||||
this.v1 = (this.v1 >> 1) ^ 0xD008;
|
||||
return this.scheduler.release(ID_DEVICE_B);
|
||||
}
|
||||
};
|
||||
|
||||
IdleTask.prototype.toString = function () {
|
||||
return "IdleTask"
|
||||
};
|
||||
|
||||
/**
|
||||
* A task that suspends itself after each time it has been run to simulate
|
||||
* waiting for data from an external device.
|
||||
* @param {Scheduler} scheduler the scheduler that manages this task
|
||||
* @constructor
|
||||
*/
|
||||
function DeviceTask(scheduler) {
|
||||
this.scheduler = scheduler;
|
||||
this.v1 = null;
|
||||
}
|
||||
|
||||
DeviceTask.prototype.run = function (packet) {
|
||||
if (packet == null) {
|
||||
if (this.v1 == null) return this.scheduler.suspendCurrent();
|
||||
var v = this.v1;
|
||||
this.v1 = null;
|
||||
return this.scheduler.queue(v);
|
||||
} else {
|
||||
this.v1 = packet;
|
||||
return this.scheduler.holdCurrent();
|
||||
}
|
||||
};
|
||||
|
||||
DeviceTask.prototype.toString = function () {
|
||||
return "DeviceTask";
|
||||
};
|
||||
|
||||
/**
|
||||
* A task that manipulates work packets.
|
||||
* @param {Scheduler} scheduler the scheduler that manages this task
|
||||
* @param {int} v1 a seed used to specify how work packets are manipulated
|
||||
* @param {int} v2 another seed used to specify how work packets are manipulated
|
||||
* @constructor
|
||||
*/
|
||||
function WorkerTask(scheduler, v1, v2) {
|
||||
this.scheduler = scheduler;
|
||||
this.v1 = v1;
|
||||
this.v2 = v2;
|
||||
}
|
||||
|
||||
WorkerTask.prototype.run = function (packet) {
|
||||
if (packet == null) {
|
||||
return this.scheduler.suspendCurrent();
|
||||
} else {
|
||||
if (this.v1 == ID_HANDLER_A) {
|
||||
this.v1 = ID_HANDLER_B;
|
||||
} else {
|
||||
this.v1 = ID_HANDLER_A;
|
||||
}
|
||||
packet.id = this.v1;
|
||||
packet.a1 = 0;
|
||||
for (var i = 0; i < DATA_SIZE; i++) {
|
||||
this.v2++;
|
||||
if (this.v2 > 26) this.v2 = 1;
|
||||
packet.a2[i] = this.v2;
|
||||
}
|
||||
return this.scheduler.queue(packet);
|
||||
}
|
||||
};
|
||||
|
||||
WorkerTask.prototype.toString = function () {
|
||||
return "WorkerTask";
|
||||
};
|
||||
|
||||
/**
|
||||
* A task that manipulates work packets and then suspends itself.
|
||||
* @param {Scheduler} scheduler the scheduler that manages this task
|
||||
* @constructor
|
||||
*/
|
||||
function HandlerTask(scheduler) {
|
||||
this.scheduler = scheduler;
|
||||
this.v1 = null;
|
||||
this.v2 = null;
|
||||
}
|
||||
|
||||
HandlerTask.prototype.run = function (packet) {
|
||||
if (packet != null) {
|
||||
if (packet.kind == KIND_WORK) {
|
||||
this.v1 = packet.addTo(this.v1);
|
||||
} else {
|
||||
this.v2 = packet.addTo(this.v2);
|
||||
}
|
||||
}
|
||||
if (this.v1 != null) {
|
||||
var count = this.v1.a1;
|
||||
var v;
|
||||
if (count < DATA_SIZE) {
|
||||
if (this.v2 != null) {
|
||||
v = this.v2;
|
||||
this.v2 = this.v2.link;
|
||||
v.a1 = this.v1.a2[count];
|
||||
this.v1.a1 = count + 1;
|
||||
return this.scheduler.queue(v);
|
||||
}
|
||||
} else {
|
||||
v = this.v1;
|
||||
this.v1 = this.v1.link;
|
||||
return this.scheduler.queue(v);
|
||||
}
|
||||
}
|
||||
return this.scheduler.suspendCurrent();
|
||||
};
|
||||
|
||||
HandlerTask.prototype.toString = function () {
|
||||
return "HandlerTask";
|
||||
};
|
||||
|
||||
/* --- *
|
||||
* P a c k e t
|
||||
* --- */
|
||||
|
||||
var DATA_SIZE = 4;
|
||||
|
||||
/**
|
||||
* A simple package of data that is manipulated by the tasks. The exact layout
|
||||
* of the payload data carried by a packet is not importaint, and neither is the
|
||||
* nature of the work performed on packets by the tasks.
|
||||
*
|
||||
* Besides carrying data, packets form linked lists and are hence used both as
|
||||
* data and worklists.
|
||||
* @param {Packet} link the tail of the linked list of packets
|
||||
* @param {int} id an ID for this packet
|
||||
* @param {int} kind the type of this packet
|
||||
* @constructor
|
||||
*/
|
||||
function Packet(link, id, kind) {
|
||||
this.link = link;
|
||||
this.id = id;
|
||||
this.kind = kind;
|
||||
this.a1 = 0;
|
||||
this.a2 = new Array(DATA_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this packet to the end of a worklist, and return the worklist.
|
||||
* @param {Packet} queue the worklist to add this packet to
|
||||
*/
|
||||
Packet.prototype.addTo = function (queue) {
|
||||
this.link = null;
|
||||
if (queue == null) return this;
|
||||
var peek, next = queue;
|
||||
while ((peek = next.link) != null)
|
||||
next = peek;
|
||||
next.link = this;
|
||||
return queue;
|
||||
};
|
||||
|
||||
Packet.prototype.toString = function () {
|
||||
return "Packet";
|
||||
};
|
||||
|
||||
|
||||
runRichards();
|
||||
|
|
@ -2,17 +2,7 @@ var n = 40000;
|
|||
var i = 0;
|
||||
var items = [];
|
||||
|
||||
function run()
|
||||
{
|
||||
items = [];
|
||||
while (i < n) {
|
||||
items.push("digit" + i);
|
||||
i = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
class Benchmark {
|
||||
runIteration() {
|
||||
run();
|
||||
}
|
||||
while (i < n) {
|
||||
items.push("digit" + i);
|
||||
i = i + 1;
|
||||
}
|
||||
|
|
|
|||
376
RAMification.py
Normal file
376
RAMification.py
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
# Copyright (C) 2018-2019 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.
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
jitTests = ["3d-cube-SP", "3d-raytrace-SP", "acorn-wtb", "ai-astar", "Air", "async-fs", "Babylon", "babylon-wtb", "base64-SP", "Basic", "Box2D", "cdjs", "chai-wtb", "coffeescript-wtb", "crypto", "crypto-aes-SP", "crypto-md5-SP", "crypto-sha1-SP", "date-format-tofte-SP", "date-format-xparb-SP", "delta-blue", "earley-boyer", "espree-wtb", "first-inspector-code-load", "FlightPlanner", "float-mm.c", "gaussian-blur", "gbemu", "gcc-loops-wasm", "hash-map", "HashSet-wasm", "jshint-wtb", "json-parse-inspector", "json-stringify-inspector", "lebab-wtb", "mandreel", "ML", "multi-inspector-code-load", "n-body-SP", "navier-stokes", "octane-code-load", "octane-zlib", "OfflineAssembler", "pdfjs", "prepack-wtb", "quicksort-wasm", "raytrace", "regex-dna-SP", "regexp", "richards", "richards-wasm", "splay", "stanford-crypto-aes", "stanford-crypto-pbkdf2", "stanford-crypto-sha256", "string-unpack-code-SP", "tagcloud-SP", "tsf-wasm", "typescript", "uglify-js-wtb", "UniPoker", "WSL"]
|
||||
|
||||
nonJITTests = ["3d-cube-SP", "3d-raytrace-SP", "acorn-wtb", "ai-astar", "Air", "async-fs", "Babylon", "babylon-wtb", "base64-SP", "Basic", "Box2D", "cdjs", "chai-wtb", "coffeescript-wtb", "crypto-aes-SP", "delta-blue", "earley-boyer", "espree-wtb", "first-inspector-code-load", "gaussian-blur", "gbemu", "hash-map", "jshint-wtb", "json-parse-inspector", "json-stringify-inspector", "lebab-wtb", "mandreel", "ML", "multi-inspector-code-load", "octane-code-load", "OfflineAssembler", "pdfjs", "prepack-wtb", "raytrace", "regex-dna-SP", "regexp", "splay", "stanford-crypto-aes", "string-unpack-code-SP", "tagcloud-SP", "typescript", "uglify-js-wtb"]
|
||||
|
||||
# Run two groups of tests with each group in a single JSC instance to see how well memory recovers between tests.
|
||||
groupTests = ["typescript,acorn-wtb,Air,pdfjs,crypto-aes-SP", "splay,FlightPlanner,prepack-wtb,octane-zlib,3d-cube-SP"]
|
||||
|
||||
luaTests = [("hello_world-LJF", "LuaJSFight/hello_world.js", 5), ("list_search-LJF", "LuaJSFight/list_search.js", 5), ("lists-LJF", "LuaJSFight/lists.js", 5), ("string_lists-LJF", "LuaJSFight/string_lists.js", 5), ("richards", "LuaJSFight/richards.js", 5)]
|
||||
|
||||
oneMB = float(1024 * 1024)
|
||||
footprintRE = re.compile(r"Current Footprint: (\d+(?:.\d+)?)")
|
||||
peakFootprintRE = re.compile(r"Peak Footprint: (\d+(?:.\d+)?)")
|
||||
|
||||
TestResult = collections.namedtuple("TestResult", ["name", "returnCode", "footprint", "peakFootprint", "vmmapOutput", "smapsOutput"])
|
||||
|
||||
ramification_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
|
||||
|
||||
remoteHooks = {}
|
||||
|
||||
|
||||
def mean(values):
|
||||
if not len(values):
|
||||
return None
|
||||
|
||||
return sum(values) / len(values)
|
||||
|
||||
|
||||
def geomean(values):
|
||||
if not len(values):
|
||||
return None
|
||||
|
||||
product = 1.0
|
||||
|
||||
for x in values:
|
||||
product *= x
|
||||
|
||||
return math.pow(product, (1.0 / len(values)))
|
||||
|
||||
|
||||
def frameworkPathFromExecutablePath(execPath):
|
||||
if not os.path.abspath(execPath):
|
||||
execPath = os.path.isabs(execPath)
|
||||
|
||||
pathMatch = re.match("(.*?/WebKitBuild/(Release|Debug)+)/([a-zA-Z]+)$", execPath)
|
||||
if pathMatch:
|
||||
return pathMatch.group(1)
|
||||
|
||||
pathMatch = re.match("(.*?)/JavaScriptCore.framework/Resources/([a-zA-Z]+)$", execPath)
|
||||
if pathMatch:
|
||||
return pathMatch.group(1)
|
||||
|
||||
pathMatch = re.match("(.*?)/JavaScriptCore.framework/Helpers/([a-zA-Z]+)$", execPath)
|
||||
if pathMatch:
|
||||
return pathMatch.group(1)
|
||||
|
||||
pathMatch = re.match("(.*?/(Release|Debug)+)/([a-zA-Z]+)$", execPath)
|
||||
if pathMatch:
|
||||
return pathMatch.group(1)
|
||||
|
||||
pathMatch = re.match("(.*?)/JavaScriptCore.framework/(.*?)/Resources/([a-zA-Z]+)$", execPath)
|
||||
if pathMatch:
|
||||
return pathMatch.group(1)
|
||||
|
||||
|
||||
def parseArgs(parser=None):
|
||||
def optStrToBool(arg):
|
||||
if arg.lower() in ("true", "t", "yes", "y"):
|
||||
return True
|
||||
if arg.lower() in ("false", "f", "no", "n"):
|
||||
return False
|
||||
|
||||
raise argparse.ArgumentTypeError("Boolean value expected")
|
||||
|
||||
if not parser:
|
||||
parser = argparse.ArgumentParser(description="RAMification benchmark driver script")
|
||||
parser.set_defaults(runner=LocalRunner)
|
||||
|
||||
verbosityGroup = parser.add_mutually_exclusive_group()
|
||||
verbosityGroup.add_argument("-q", "--quiet", dest="verbose", action="store_false", help="Provide less output")
|
||||
verbosityGroup.add_argument("-v", "--verbose", dest="verbose", action="store_true", default=True, help="Provide more output")
|
||||
|
||||
parser.add_argument("-c", "--jsc", dest="jscCommand", type=str, default="/usr/local/bin/jsc", metavar="path-to-jsc", help="Path to jsc command")
|
||||
parser.add_argument("-d", "--jetstream2-dir", dest="testDir", type=str, default=ramification_dir, metavar="path-to-JetStream2-files", help="JetStream2 root directory")
|
||||
parser.add_argument("-e", "--env-var", dest="extraEnvVars", action="append", default=[], metavar="env-var=value", help="Specify additional environment variables")
|
||||
parser.add_argument("-f", "--format-json", dest="formatJSON", action="store_true", default=False, help="Format JSON with whitespace")
|
||||
parser.add_argument("-g", "--run-grouped-tests", dest="runGroupedTests", nargs="?", const=True, default=None, type=optStrToBool, metavar="true / false", help="Run grouped tests [default]")
|
||||
parser.add_argument("-j", "--run-jit", dest="runJITTests", nargs="?", const=True, default=None, type=optStrToBool, metavar="true / false", help="Run JIT tests [default]")
|
||||
parser.add_argument("-l", "--lua", dest="runLuaTests", nargs="?", const=True, default=None, type=optStrToBool, metavar="true / false", help="Run Lua comparison tests [default]")
|
||||
parser.add_argument("-n", "--run-no-jit", dest="runNoJITTests", nargs="?", const=True, default=None, type=optStrToBool, metavar="true / false", help="Run no JIT tests [default]")
|
||||
parser.add_argument("-o", "--output", dest="jsonFilename", type=str, default=None, metavar="JSON-output-file", help="Path to JSON output")
|
||||
parser.add_argument("-m", "--vmmap", dest="takeVmmap", action="store_true", default=False, help="Take a vmmap after each test")
|
||||
parser.add_argument("--smaps", dest="takeSmaps", action="store_true", default=False, help="Take a smaps rollup after each test")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
subtestArgs = [args.runGroupedTests, args.runJITTests, args.runLuaTests, args.runNoJITTests]
|
||||
allDefault = all([arg is None for arg in subtestArgs])
|
||||
anyTrue = any([arg is True for arg in subtestArgs])
|
||||
anyFalse = any([arg is False for arg in subtestArgs])
|
||||
|
||||
# Default behavior is to run all subtests.
|
||||
# If a test is explicitly specified not to run, skip that test and use the default behavior for the remaining tests.
|
||||
# If tests are explicitly specified to run, only run those tests.
|
||||
# If there is a mix of tests specified to run and not to run, also do not run any unspecified tests.
|
||||
getArgValue = lambda arg: True if allDefault else False if arg is None and anyTrue else True if arg is None and anyFalse else arg
|
||||
|
||||
args.runJITTests = getArgValue(args.runJITTests)
|
||||
args.runNoJITTests = getArgValue(args.runNoJITTests)
|
||||
args.runLuaTests = getArgValue(args.runLuaTests)
|
||||
args.runGroupedTests = getArgValue(args.runGroupedTests)
|
||||
|
||||
return args
|
||||
|
||||
|
||||
class BaseRunner:
|
||||
def __init__(self, args):
|
||||
self.rootDir = args.testDir
|
||||
self.environmentVars = {}
|
||||
self.vmmapOutput = "" if args.takeVmmap else None
|
||||
self.smapsOutput = "" if args.takeSmaps else None
|
||||
|
||||
def setup(self):
|
||||
pass
|
||||
|
||||
def setEnv(self, variable, value):
|
||||
self.environmentVars[variable] = value
|
||||
|
||||
def unsetEnv(self, variable):
|
||||
self.environmentVars.pop(variable, None)
|
||||
|
||||
def resetForTest(self, testName):
|
||||
self.testName = testName
|
||||
self.footprint = None
|
||||
self.peakFootprint = None
|
||||
self.returnCode = 0
|
||||
|
||||
def processLine(self, line):
|
||||
line = str(line.strip())
|
||||
|
||||
footprintMatch = re.match(footprintRE, line)
|
||||
if footprintMatch:
|
||||
self.footprint = int(footprintMatch.group(1))
|
||||
return
|
||||
|
||||
peakFootprintMatch = re.match(peakFootprintRE, line)
|
||||
if peakFootprintMatch:
|
||||
self.peakFootprint = int(peakFootprintMatch.group(1))
|
||||
|
||||
def setReturnCode(self, returnCode):
|
||||
self.returnCode = returnCode
|
||||
|
||||
def getResults(self):
|
||||
return TestResult(name=self.testName, returnCode=self.returnCode, footprint=self.footprint, peakFootprint=self.peakFootprint, vmmapOutput=self.vmmapOutput, smapsOutput=self.smapsOutput)
|
||||
|
||||
|
||||
class LocalRunner(BaseRunner):
|
||||
def __init__(self, args):
|
||||
BaseRunner.__init__(self, args)
|
||||
self.jscCommand = args.jscCommand
|
||||
|
||||
def runOneTest(self, test, extraOptions=None, useJetStream2Harness=True):
|
||||
self.resetForTest(test)
|
||||
|
||||
args = [self.jscCommand]
|
||||
if extraOptions:
|
||||
args.extend(extraOptions)
|
||||
|
||||
if useJetStream2Harness:
|
||||
args.extend(["-e", "testList='{test}'; runMode='RAMification'".format(test=test), "cli.js"])
|
||||
else:
|
||||
args.extend(["--footprint", "{test}".format(test=test)])
|
||||
|
||||
self.resetForTest(test)
|
||||
|
||||
proc = subprocess.Popen(args, cwd=self.rootDir, env=self.environmentVars, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=None, shell=False)
|
||||
while True:
|
||||
line = proc.stdout.readline()
|
||||
if sys.version_info[0] >= 3:
|
||||
line = str(line, "utf-8")
|
||||
|
||||
self.processLine(line)
|
||||
|
||||
if "js shell waiting for input to exit" in line:
|
||||
if self.vmmapOutput is not None:
|
||||
self.vmmapOutput = subprocess.Popen(['vmmap', '--summary', '{}'.format(proc.pid)], shell=False, stderr=subprocess.PIPE, stdout=subprocess.PIPE).stdout.read()
|
||||
if sys.version_info[0] >= 3:
|
||||
self.vmmapOutput = str(self.vmmapOutput, "utf-8")
|
||||
if self.smapsOutput is not None:
|
||||
self.smapsOutput = subprocess.Popen(['cat', '/proc/{}/smaps_rollup'.format(proc.pid)], shell=False, stderr=subprocess.PIPE, stdout=subprocess.PIPE).stdout.read()
|
||||
proc.stdin.write(b"done\n")
|
||||
proc.stdin.flush()
|
||||
|
||||
if line == "":
|
||||
break
|
||||
|
||||
self.setReturnCode(proc.wait())
|
||||
|
||||
return self.getResults()
|
||||
|
||||
|
||||
def main(parser=None):
|
||||
footprintValues = []
|
||||
peakFootprintValues = []
|
||||
testResultsDict = {}
|
||||
hasFailedRuns = False
|
||||
|
||||
args = parseArgs(parser=parser)
|
||||
|
||||
testRunner = args.runner(args)
|
||||
|
||||
if args.takeVmmap or args.takeSmaps:
|
||||
testRunner.setEnv("JS_SHELL_WAIT_FOR_INPUT_TO_EXIT", "1")
|
||||
|
||||
dyldFrameworkPath = frameworkPathFromExecutablePath(args.jscCommand)
|
||||
if dyldFrameworkPath:
|
||||
testRunner.setEnv("DYLD_FRAMEWORK_PATH", dyldFrameworkPath)
|
||||
|
||||
for envVar in args.extraEnvVars:
|
||||
envVarParts = envVar.split("=")
|
||||
if len(envVarParts) == 1:
|
||||
envVarParts[1] = "1"
|
||||
testRunner.setEnv(envVarParts[0], envVarParts[1])
|
||||
|
||||
testRunner.setup()
|
||||
|
||||
def runTestList(testList, extraOptions=None, useJetStream2Harness=True):
|
||||
testScoresDict = {}
|
||||
|
||||
for testInfo in testList:
|
||||
footprintScores = []
|
||||
peakFootprintScores = []
|
||||
if isinstance(testInfo, tuple):
|
||||
testName, test, weight = testInfo
|
||||
else:
|
||||
testName, test, weight = testInfo, testInfo, 1
|
||||
|
||||
sys.stdout.write("Running {}... ".format(testName))
|
||||
testResult = testRunner.runOneTest(test, extraOptions, useJetStream2Harness)
|
||||
|
||||
if testResult.returnCode == 0 and testResult.footprint and testResult.peakFootprint:
|
||||
if args.verbose:
|
||||
print("footprint: {}, peak footprint: {}".format(testResult.footprint, testResult.peakFootprint))
|
||||
if testResult.vmmapOutput:
|
||||
print(testResult.vmmapOutput)
|
||||
if testResult.smapsOutput:
|
||||
print(testResult.smapsOutput)
|
||||
else:
|
||||
print
|
||||
|
||||
if testResult.footprint:
|
||||
footprintScores.append(int(testResult.footprint))
|
||||
for count in range(0, weight):
|
||||
footprintValues.append(testResult.footprint / oneMB)
|
||||
if testResult.peakFootprint:
|
||||
peakFootprintScores.append(int(testResult.peakFootprint))
|
||||
for count in range(0, weight):
|
||||
peakFootprintValues.append(testResult.peakFootprint / oneMB)
|
||||
else:
|
||||
hasFailedRuns = True
|
||||
print("failed")
|
||||
if testResult.returnCode != 0:
|
||||
print(" exit code = {}".format(testResult.returnCode))
|
||||
if not testResult.footprint:
|
||||
print(" footprint = {}".format(testResult.footprint))
|
||||
if not testResult.peakFootprint:
|
||||
print(" peak footprint = {}".format(testResult.peakFootprint))
|
||||
print
|
||||
|
||||
testScoresDict[test] = {"metrics": {"Allocations": ["Geometric"]}, "tests": {"end": {"metrics": {"Allocations": {"current": footprintScores}}}, "peak": {"metrics": {"Allocations": {"current": peakFootprintScores}}}}}
|
||||
|
||||
return testScoresDict
|
||||
|
||||
current_path = os.getcwd()
|
||||
os.chdir(ramification_dir) # To allow JS libraries to load
|
||||
|
||||
if args.runLuaTests:
|
||||
if args.verbose:
|
||||
print("== LuaJSFight No JIT tests ==")
|
||||
|
||||
# Use system malloc for LuaJSFight tests
|
||||
testRunner.setEnv("Malloc", "X")
|
||||
|
||||
scoresDict = runTestList(luaTests, ["--useJIT=false", "--forceMiniVMMode=true"], useJetStream2Harness=False)
|
||||
|
||||
testResultsDict["LuaJSFight No JIT Tests"] = {"metrics": {"Allocations": ["Geometric"]}, "tests": scoresDict}
|
||||
|
||||
testRunner.unsetEnv("Malloc")
|
||||
|
||||
if args.runGroupedTests:
|
||||
if args.verbose:
|
||||
print("== Grouped tests ==")
|
||||
|
||||
scoresDict = runTestList(groupTests)
|
||||
|
||||
testResultsDict["Grouped Tests"] = {"metrics": {"Allocations": ["Geometric"]}, "tests": scoresDict}
|
||||
|
||||
if args.runJITTests:
|
||||
if args.verbose:
|
||||
print("== JIT tests ==")
|
||||
|
||||
scoresDict = runTestList(jitTests)
|
||||
|
||||
testResultsDict["JIT Tests"] = {"metrics": {"Allocations": ["Geometric"]}, "tests": scoresDict}
|
||||
|
||||
if args.runNoJITTests:
|
||||
if args.verbose:
|
||||
print("== No JIT tests ==")
|
||||
|
||||
scoresDict = runTestList(nonJITTests, ["--useJIT=false", "-e", "testIterationCount=1"])
|
||||
|
||||
testResultsDict["No JIT Tests"] = {"metrics": {"Allocations": ["Geometric"]}, "tests": scoresDict}
|
||||
|
||||
footprintGeomean = int(geomean(footprintValues) * oneMB)
|
||||
peakFootprintGeomean = int(geomean(peakFootprintValues) * oneMB)
|
||||
totalScore = int(geomean([footprintGeomean, peakFootprintGeomean]))
|
||||
|
||||
if footprintGeomean:
|
||||
print("Footprint geomean: {} ({:.3f} MB)".format(footprintGeomean, footprintGeomean / oneMB))
|
||||
|
||||
if peakFootprintGeomean:
|
||||
print("Peak Footprint geomean: {} ({:.3f} MB)".format(peakFootprintGeomean, peakFootprintGeomean / oneMB))
|
||||
|
||||
if footprintGeomean and peakFootprintGeomean:
|
||||
print("Score: {} ({:.3f} MB)".format(totalScore, totalScore / oneMB))
|
||||
|
||||
resultsDict = {"RAMification": {"metrics": {"Allocations": {"current": [totalScore]}}, "tests": testResultsDict}}
|
||||
|
||||
os.chdir(current_path) # Reset the path back to what it was before
|
||||
|
||||
if args.jsonFilename:
|
||||
with open(args.jsonFilename, "w") as jsonFile:
|
||||
if args.formatJSON:
|
||||
json.dump(resultsDict, jsonFile, indent=4, separators=(',', ': '))
|
||||
else:
|
||||
json.dump(resultsDict, jsonFile)
|
||||
|
||||
if hasFailedRuns:
|
||||
print("Detected failed run(s), exiting with non-zero return code")
|
||||
|
||||
return hasFailedRuns
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
|
|
@ -43,8 +43,9 @@ class Benchmark {
|
|||
{
|
||||
if (this._players.length != playerExpectations.length)
|
||||
throw "Expect " + playerExpectations.length + ", but actually have " + this._players.length;
|
||||
|
||||
for (let playerIdx = 0; playerIdx < playerExpectations.length; playerIdx++)
|
||||
playerExpectations[playerIdx].validate(this._players[playerIdx]);
|
||||
if (isInBrowser) {
|
||||
for (let playerIdx = 0; playerIdx < playerExpectations.length; playerIdx++)
|
||||
playerExpectations[playerIdx].validate(this._players[playerIdx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
0
__init__.py
Normal file
0
__init__.py
Normal file
4
cli.js
4
cli.js
|
|
@ -28,6 +28,10 @@ console = {
|
|||
log: print
|
||||
}
|
||||
|
||||
const isD8 = typeof Realm !== "undefined";
|
||||
if (isD8)
|
||||
readFile = read;
|
||||
|
||||
if (typeof testList === "undefined")
|
||||
testList = undefined;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<!--
|
||||
Copyright (C) 2019 Apple Inc. All rights reserved.
|
||||
Copyright (C) 2019-2022 Apple Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<title>JetStream 2 In-Depth Analysis</title>
|
||||
<title>JetStream 2.1 In-Depth Analysis</title>
|
||||
|
||||
<link rel="stylesheet" href="JetStream.css">
|
||||
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
<body>
|
||||
<h1 class="logo">
|
||||
<div id="jetstreams">
|
||||
<a href="index.html" class="logo-image">JetStream 2</a>
|
||||
<a href="index.html" class="logo-image">JetStream 2.1</a>
|
||||
</div>
|
||||
</h1>
|
||||
<main>
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
</p>
|
||||
|
||||
<p>
|
||||
All but one of JetStream'2 JavaScript benchmarks run for N iterations, where
|
||||
All but one of JetStream 2's JavaScript benchmarks run for N iterations, where
|
||||
N is usually 120. JetStream 2 reports the startup score as the time it takes to run the first iteration.
|
||||
The worst case score is the average of the worst M iterations, excluding the first iteration.
|
||||
M is always less than N, and is usually 4. The average case score is the average
|
||||
|
|
@ -113,12 +113,19 @@
|
|||
</p>
|
||||
|
||||
<p>
|
||||
Note that scores from JetStream 2 are not comparable to scores to other versions
|
||||
JetStream 2.1 runs the same benchmarks as JetStream 2, but updates the benchmark driver to
|
||||
improve score stability. This is achieved by pre-fetching network resources prior to running
|
||||
the benchmarks. This can reduce perturbations on the measurement of JavaScript execution
|
||||
time due to second order effects of pause times induced by network latency.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Note that scores from JetStream 2.1 are not comparable to scores to other versions
|
||||
of any JetStream benchmark.
|
||||
</p>
|
||||
|
||||
<h3>
|
||||
JetStream 2 has 64 subtests:
|
||||
JetStream 2.1 has 64 subtests:
|
||||
</h3>
|
||||
|
||||
<dl>
|
||||
|
|
|
|||
28
index.html
28
index.html
|
|
@ -1,5 +1,5 @@
|
|||
<!--
|
||||
Copyright (C) 2019 Apple Inc. All rights reserved.
|
||||
Copyright (C) 2019-2022 Apple Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
|
|
@ -27,20 +27,34 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<title>JetStream 2</title>
|
||||
<title>JetStream 2.1</title>
|
||||
|
||||
<link rel="stylesheet" href="JetStream.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes, viewport-fit=cover">
|
||||
|
||||
<script>
|
||||
const isInBrowser = true;
|
||||
const isD8 = false;
|
||||
var allIsGood = true;
|
||||
window.onerror = function() { allIsGood = false; }
|
||||
window.onerror = function(e) {
|
||||
if (e == "Script error.") {
|
||||
// This is a workaround for Firefox on iOS which has an uncaught exception from
|
||||
// its injected script. We should not stop the benchmark from running due to that
|
||||
// if it can otherwise run.
|
||||
return;
|
||||
}
|
||||
allIsGood = false;
|
||||
}
|
||||
|
||||
async function initialize() {
|
||||
if (allIsGood)
|
||||
await JetStream.initialize();
|
||||
else {
|
||||
if (allIsGood) {
|
||||
try {
|
||||
await JetStream.initialize();
|
||||
} catch (e) {
|
||||
allIsGood = false;
|
||||
}
|
||||
}
|
||||
if (!allIsGood) {
|
||||
let statusElement = document.getElementById("status");
|
||||
statusElement.classList.remove('loading');
|
||||
statusElement.classList.add('error');
|
||||
|
|
@ -60,7 +74,7 @@
|
|||
</h1>
|
||||
|
||||
<main>
|
||||
<p class="summary">JetStream 2 is a JavaScript and WebAssembly benchmark suite focused on the most advanced web applications. It rewards browsers that start up quickly, execute code quickly, and run smoothly. For more information, read the <a href="in-depth.html">in-depth analysis</a>. Bigger scores are better.</p>
|
||||
<p class="summary">JetStream 2.1 is a JavaScript and WebAssembly benchmark suite focused on the most advanced web applications. It rewards browsers that start up quickly, execute code quickly, and run smoothly. For more information, read the <a href="in-depth.html">in-depth analysis</a>. Bigger scores are better.</p>
|
||||
<p class="summary" id="mode-description"></p>
|
||||
|
||||
<div id="result-summary"></div>
|
||||
|
|
|
|||
45
wasm-cli.js
Normal file
45
wasm-cli.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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 isInBrowser = false;
|
||||
console = {
|
||||
log: () => { }
|
||||
}
|
||||
|
||||
testList = ["HashSet-wasm", "tsf-wasm", "quicksort-wasm", "gcc-loops-wasm", "richards-wasm"];
|
||||
|
||||
RAMification = false;
|
||||
|
||||
load("./JetStreamDriver.js");
|
||||
|
||||
async function runJetStream() {
|
||||
try {
|
||||
await JetStream.initialize();
|
||||
JetStream.start();
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
runJetStream();
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
} \
|
||||
} while(0)
|
||||
|
||||
#define ASSERT_DISABLED 1
|
||||
#define ASSERT_ENABLED 0
|
||||
|
||||
#define DUMP_HASHTABLE_STATS 0
|
||||
#define DUMP_HASHTABLE_STATS_PER_TABLE 0
|
||||
|
|
@ -1033,7 +1033,7 @@ struct CustomHashTraits : public GenericHashTraits<T> {
|
|||
template<typename HashTranslator, typename T> ValueType* lookup(const T&);
|
||||
template<typename HashTranslator, typename T> ValueType* inlineLookup(const T&);
|
||||
|
||||
#if !ASSERT_DISABLED
|
||||
#if ASSERT_ENABLED
|
||||
void checkTableConsistency() const;
|
||||
#else
|
||||
static void checkTableConsistency() { }
|
||||
|
|
@ -1085,7 +1085,7 @@ struct CustomHashTraits : public GenericHashTraits<T> {
|
|||
iterator makeKnownGoodIterator(ValueType* pos) { return iterator(this, pos, m_table + m_tableSize, HashItemKnownGood); }
|
||||
const_iterator makeKnownGoodConstIterator(ValueType* pos) const { return const_iterator(this, pos, m_table + m_tableSize, HashItemKnownGood); }
|
||||
|
||||
#if !ASSERT_DISABLED
|
||||
#if ASSERT_ENABLED
|
||||
void checkTableConsistencyExceptSize() const;
|
||||
#else
|
||||
static void checkTableConsistencyExceptSize() { }
|
||||
|
|
@ -1182,7 +1182,7 @@ struct CustomHashTraits : public GenericHashTraits<T> {
|
|||
return key;
|
||||
}
|
||||
|
||||
#if ASSERT_DISABLED
|
||||
#if !ASSERT_ENABLED
|
||||
|
||||
template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits>
|
||||
template<typename HashTranslator, typename T>
|
||||
|
|
@ -1951,7 +1951,7 @@ struct CustomHashTraits : public GenericHashTraits<T> {
|
|||
return *this;
|
||||
}
|
||||
|
||||
#if !ASSERT_DISABLED
|
||||
#if ASSERT_ENABLED
|
||||
|
||||
template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits>
|
||||
void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::checkTableConsistency() const
|
||||
|
|
@ -1993,7 +1993,7 @@ struct CustomHashTraits : public GenericHashTraits<T> {
|
|||
ASSERT(m_tableSize == m_tableSizeMask + 1);
|
||||
}
|
||||
|
||||
#endif // ASSERT_DISABLED
|
||||
#endif // ASSERT_ENABLED
|
||||
|
||||
#if CHECK_HASHTABLE_ITERATORS
|
||||
|
||||
|
|
|
|||
65
watch-cli.js
Normal file
65
watch-cli.js
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
dumpJSONResults = true;
|
||||
|
||||
testIterationCount = 15;
|
||||
|
||||
testList = [
|
||||
{name: "FlightPlanner"},
|
||||
{name: "UniPoker"},
|
||||
{name: "Air"},
|
||||
{name: "Basic"},
|
||||
{name: "ML", iterations: 7, worstCaseCount: 2},
|
||||
{name: "Babylon"},
|
||||
{name: "cdjs", iterations: 10, worstCaseCount: 2},
|
||||
{name: "first-inspector-code-load"},
|
||||
{name: "multi-inspector-code-load"},
|
||||
{name: "Box2D"},
|
||||
{name: "octane-code-load"},
|
||||
{name: "crypto"},
|
||||
{name: "delta-blue"},
|
||||
{name: "earley-boyer"},
|
||||
{name: "gbemu", iterations: 10, worstCaseCount: 2},
|
||||
{name: "navier-stokes"},
|
||||
{name: "pdfjs"},
|
||||
{name: "raytrace"},
|
||||
{name: "regexp"},
|
||||
{name: "richards"},
|
||||
{name: "splay"},
|
||||
{name: "ai-astar"},
|
||||
{name: "gaussian-blur", iterations: 10, worstCaseCount: 2},
|
||||
{name: "stanford-crypto-aes"},
|
||||
{name: "stanford-crypto-pbkdf2"},
|
||||
{name: "stanford-crypto-sha256"},
|
||||
{name: "json-stringify-inspector"},
|
||||
{name: "json-parse-inspector"},
|
||||
{name: "async-fs", iterations: 8, worstCaseCount: 2},
|
||||
{name: "hash-map", iterations: 12, worstCaseCount: 3},
|
||||
{name: "3d-cube-SP"},
|
||||
{name: "3d-raytrace-SP"},
|
||||
{name: "base64-SP"},
|
||||
{name: "crypto-aes-SP"},
|
||||
{name: "crypto-md5-SP"},
|
||||
{name: "crypto-sha1-SP"},
|
||||
{name: "date-format-tofte-SP"},
|
||||
{name: "date-format-xparb-SP"},
|
||||
{name: "n-body-SP"},
|
||||
{name: "regex-dna-SP"},
|
||||
{name: "string-unpack-code-SP"},
|
||||
{name: "tagcloud-SP"},
|
||||
{name: "chai-wtb", iterations: 5, worstCaseCount: 2},
|
||||
{name: "jshint-wtb", iterations: 5, worstCaseCount: 2},
|
||||
{name: "prepack-wtb", iterations: 5, worstCaseCount: 2}
|
||||
];
|
||||
|
||||
testIterationCountMap = new Map;
|
||||
testWorstCaseCountMap = new Map;
|
||||
for (let test of testList) {
|
||||
if (test.iterations)
|
||||
testIterationCountMap.set(test.name, test.iterations);
|
||||
if (test.worstCaseCount)
|
||||
testWorstCaseCountMap.set(test.name, test.worstCaseCount);
|
||||
}
|
||||
|
||||
|
||||
testList = testList.map(x => x.name);
|
||||
|
||||
load("./cli.js");
|
||||
Loading…
Reference in a new issue