blob: d1284a241d88fc04cced28a9fc8133cec6b2213a [file] [log] [blame]
// Benchmarking script. This runs on compiled bench.wat and prints out timings.
//
// Usage:
//
// * wasm-opt scripts/benchmarking/bench.wat -all --inline-functions-with-loops --always-inline-max-function-size=1000 --inlining --precompute-propagate --optimize-instructions --inlining --simplify-locals --coalesce-locals --vacuum --remove-unused-module-elements -o bench.wasm -g
// * Inspect the optimized wasm to see that inlining etc. worked properly
// (we rely on inlining to let us write bench.wat in a short/simple form, and
// we use very specific optimizations in order to not optimize away the
// differences we care about).
// * d8 bench.js -- bench.wasm
// etc.
//
// Shell integration.
if (typeof console === 'undefined') {
console = { log: print };
}
var tempRet0;
var binary;
if (typeof process === 'object' && typeof require === 'function' /* node.js detection */) {
var args = process.argv.slice(2);
binary = require('fs').readFileSync(args[0]);
if (!binary.buffer) binary = new Uint8Array(binary);
} else {
var args;
if (typeof scriptArgs != 'undefined') {
args = scriptArgs;
} else if (typeof arguments != 'undefined') {
args = arguments;
}
if (typeof readbuffer === 'function') {
binary = new Uint8Array(readbuffer(args[0]));
} else {
binary = read(args[0], 'binary');
}
}
// Create the wasm.
const module = new WebAssembly.Module(binary);
const instance = new WebAssembly.Instance(module, {});
const exports = instance.exports;
// Create the benchmarkers.
function makeBenchmarker(name) {
return {
name: name,
func: exports[name],
time: 0,
sum: 0,
iters: 0,
};
}
const benchmarkers = [
makeBenchmarker('len'),
makeBenchmarker('and'),
makeBenchmarker('iff-both'),
makeBenchmarker('or'),
makeBenchmarker('iff-either'),
makeBenchmarker('select'),
makeBenchmarker('iff-nextor'),
makeBenchmarker('select-three'),
makeBenchmarker('iff-three'),
];
// We'll call the benchmark functions in random orders. Random orders avoid any
// interaction between the benchmarks from causing bias in the results.
//
// An alternative to randomly ordering the benchmarks in each iteration would be
// to fully benchmark one, then do the next, so there are large amounts of time
// between them, but that also allows them to become more like microbenchmarks
// where the branch predictor etc. might display very favorable behavior.
// Interleaving them makes things slightly more realistic.
//
// If we have too many benchmarks then eventually computing all orders ahead of
// time will not work, but so long as we can, it is faster this way rather than
// to compute random orders on the fly as we go.
function makeOrders(prefix) {
// Given a prefix of an order, like [] or [0, 3], return all the possible
// orders beginning with that prefix.
// We cannot repeat anything already seen.
const seen = new Set();
for (var x of prefix) {
seen.add(x);
}
// Starting from the prefix, extend it by one item in all valid ways.
const extensions = [];
for (var i = 0; i < benchmarkers.length; i++) {
if (!seen.has(i)) {
extensions.push(prefix.concat(i));
}
}
if (prefix.length == benchmarkers.length - 1) {
// The extensions are complete orders; stop the recursion.
return extensions;
}
// Recursively generate the full orders.
const ret = [];
for (var extension of extensions) {
for (var order of makeOrders(extension)) {
ret.push(order);
}
}
return ret;
}
const orders = makeOrders([]);
// Params.
const M = 10000000;
const N = 100;
console.log('iters :', M);
console.log('list len :', N);
console.log('benchmarkers:', benchmarkers.length);
console.log('orderings :', orders.length);
// Create a long linked list of objects of both type $A and $B.
var list = null;
for (var i = 0; i < N; i++) {
list = Math.random() < 0.5 ? exports.makeA(list) : exports.makeB(list);
}
console.log('benchmarking...');
// Call the benchmark functions.
for (var i = 0; i < M; i++) {
const order = orders[Math.floor(Math.random() * orders.length)];
for (var k = 0; k < benchmarkers.length; k++) {
const benchmarker = benchmarkers[order[k]];
const start = performance.now();
const result = benchmarker.func(list);
benchmarker.time += performance.now() - start;
benchmarker.sum += result;
benchmarker.iters++;
}
}
for (var benchmarker of benchmarkers) {
if (benchmarker.iters != M) {
throw 'wat';
}
}
console.log();
for (var benchmarker of benchmarkers) {
console.log(`${benchmarker.name} time: \t${benchmarker.time}`)
}
console.log();
for (var benchmarker of benchmarkers) {
console.log(`${benchmarker.name} mean sum: \t${benchmarker.sum / M}`)
}