// 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}`) }