wasm-bindgen/benchmarks/index.js
Alex Crichton e4fd0fccb5 Add a page of microbenchmarks for wasm-bindgen
This commit starts to add a page of microbenchmarks for wasm-bindgen
which we can hopefully track and compare over time. Right now it's
primarily focused on data collection, making it easy to collect data
across a number of benchmarks for comparison. It doesn't currently do
much in the way of actually comparing the results for you (aka drawing
pretty graphs), so let's left for a future step.

It's hoped though that we can use this to track performance improvements
as well as ensuring that they work over time!
2019-05-03 12:30:11 -07:00

183 lines
7.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Benchmarking framework that we're using
import 'https://unpkg.com/lodash@4.17.11/lodash.js';
import 'https://unpkg.com/benchmark@2.1.4/benchmark.js';
// Import lots of functions from JS/wasm, and rename everything to have the
// namespace of where it's coming from.
import wbindgen_init, {
call_js_thunk_n_times as wbindgen_call_js_thunk_n_times,
call_js_add_n_times as wbindgen_call_js_add_n_times,
thunk as wbindgen_thunk,
add as wbindgen_add,
fibonacci as wbindgen_fibonacci,
call_node_first_child_n_times as wbindgen_call_node_first_child_n_times,
call_node_node_type_n_times as wbindgen_call_node_node_type_n_times,
count_node_types as wbindgen_count_node_types,
call_first_child_final_n_times as wbindgen_call_first_child_final_n_times,
call_first_child_structural_n_times as wbindgen_call_first_child_structural_n_times,
call_foo_bar_final_n_times as wbindgen_call_foo_bar_final_n_times,
call_foo_bar_structural_n_times as wbindgen_call_foo_bar_structural_n_times,
str_roundtrip as wbindgen_str_roundtrip,
} from './pkg/wasm_bindgen_benchmark.js';
import {
call_js_thunk_n_times as js_call_js_thunk_n_times,
call_js_add_n_times as js_call_js_add_n_times,
thunk as js_thunk,
add as js_add,
fibonacci as js_fibonacci,
call_node_first_child_n_times as js_call_node_first_child_n_times,
call_node_node_type_n_times as js_call_node_node_type_n_times,
count_node_types as js_count_node_types,
} from './js-benchmarks.js';
import * as globals from './globals.js';
import { Lock } from './utils.js';
// These are set for `raw.wasm`, which we import and configure manually:
let raw_call_js_thunk_n_times = null;
let raw_call_js_add_n_times = null;
let raw_thunk = null;
let raw_add = null;
// Create a `Map` of all benchmarks that we're going to execute, where the map
// is from a benchmark's name to a thunk to execute for the benchmark.
function makeBenchmarks() {
const benchmarks = new Map();
benchmarks.wbindgen_thunk = wbindgen_thunk;
benchmarks.raw_thunk = raw_thunk;
benchmarks.js_thunk = js_thunk;
benchmarks.wbindgen_fib_40 = () => wbindgen_fibonacci(40);
benchmarks.js_fib_40 = () => js_fibonacci(40);
benchmarks.wbindgen_add = () => wbindgen_add(2, 3);
benchmarks.raw_add = () => raw_add(2, 3);
benchmarks.js_add = () => js_add(2, 3);
benchmarks.js_call_js_thunk_n_times = () => js_call_js_thunk_n_times(10000);
benchmarks.raw_call_js_thunk_n_times = () => raw_call_js_thunk_n_times(10000);
benchmarks.wbindgen_call_js_thunk_n_times = () => wbindgen_call_js_thunk_n_times(10000);
benchmarks.js_call_js_add_n_times = () => js_call_js_add_n_times(10000, 2, 3);
benchmarks.raw_call_js_add_n_times = () => raw_call_js_add_n_times(10000, 2, 3);
benchmarks.wbindgen_call_js_add_n_times = () => wbindgen_call_js_add_n_times(10000, 2, 3);
const list = [];
for (let i = 0; i < 10; i++)
list.push(document.body);
benchmarks.wbindgen_call_node_first_child_n_times = () => wbindgen_call_node_first_child_n_times(1000, list);
benchmarks.js_call_node_first_child_n_times = () => js_call_node_first_child_n_times(1000, list);
benchmarks.wbindgen_call_node_node_type_n_times = () => wbindgen_call_node_node_type_n_times(1000, list);
benchmarks.js_call_node_node_type_n_times = () => js_call_node_node_type_n_times(1000, list);
const body = document.body;
benchmarks.wbindgen_count_node_types = () => wbindgen_count_node_types(body);
benchmarks.js_count_node_types = () => js_count_node_types(body);
benchmarks.wbindgen_call_first_child_final_n_times = () => wbindgen_call_first_child_final_n_times(10000, body);
benchmarks.wbindgen_call_first_child_structural_n_times = () => wbindgen_call_first_child_structural_n_times(10000, body);
const foo = new globals.Foo();
benchmarks.wbindgen_call_foo_bar_final_n_times = () => wbindgen_call_foo_bar_final_n_times(10000, foo);
benchmarks.wbindgen_call_foo_bar_structural_n_times = () => wbindgen_call_foo_bar_structural_n_times(10000, foo);
const strings = {
ascii_small: 'ja',
ascii_medium: 'aym0566x',
ascii_number: '505874924095815681',
ascii_date: 'Sun Aug 31 00:29:15 +0000 2014',
ascii_url: 'https://pbs.twimg.com/profile_images/497760886795153410/LDjAwR_y_normal.jpeg',
ascii_link: '<a href="http://twitter.com/download/iphone" rel="nofollow">Twitter for iPhone</a>',
unicode: '@aym0566x \n\n名前:前田あゆみ\n第一印象:なんか怖っ!\n今の印象:とりあえずキモい。噛み合わない\n好きなところ:ぶすでキモいとこ😋✨✨\n思い出:んーーー、ありすぎ😊❤️\nLINE交換できる:あぁ……ごめん✋\nトプ画をみて:照れますがな😘✨\n一言:お前は一生もんのダチ💖'
}
const template = document.querySelector('tr.str-benchmark');
template.remove();
const tbody = document.querySelector('tbody#wbindgen-body');
for (const bm in strings) {
const s = strings[bm];
const bm_name = `wbindgen_str_${bm}`;
benchmarks[bm_name] = () => wbindgen_str_roundtrip(s);
const row = template.cloneNode(true);
row.querySelector('.str').textContent = bm;
row.querySelector('td.bm').id = bm_name;
row.removeAttribute('style');
tbody.appendChild(row);
}
return benchmarks;
}
// Set up the page and initialize all event handlers and such.
function run() {
const benchmarks = makeBenchmarks();
const benchmarkLock = new Lock();
for (const td of document.querySelectorAll('td.bm')) {
const bm = benchmarks[td.id];
if (typeof bm !== 'function')
throw new Error(`no benchmark registered for ${td.id}`);
const run = document.createElement('a');
run.href = '#';
run.innerText = '(run)';
run.onclick = function() {
benchmarkLock.withLock(async () => {
await executeAndUpdate(td.id, bm, td);
});
run.remove();
td.innerText = 'executing ...';
return false;
};
td.appendChild(run);
}
for (const a of document.querySelectorAll('.about-open')) {
a.onclick = function() {
a.nextElementSibling.style.display = 'block';
a.remove();
return false;
};
}
}
async function executeAndUpdate(name, bm, td) {
const result = await executeBenchmark(name, bm);
console.log(result.target);
const rme = Math.round(result.target.stats.rme * 100) / 100;
td.innerText = `${Math.round(result.target.hz).toLocaleString()}/s ±${rme}%`;
}
function executeBenchmark(name, bm) {
return new Promise((resolve, reject) => {
const suite = new Benchmark.Suite();
suite.add(name, bm);
suite.on('cycle', resolve);
suite.run({ async: true });
});
}
// Load wasm files and when they're done (plus the DOM) then we initialize
// everything
const wasms = [];
wasms.push(wbindgen_init('./pkg/wasm_bindgen_benchmark_bg.wasm'));
wasms.push(fetch('./raw.wasm')
.then(r => r.arrayBuffer())
.then(m => WebAssembly.instantiate(m, { './globals.js': globals }))
.then(m => {
raw_call_js_thunk_n_times = m.instance.exports.call_js_thunk_n_times;
raw_call_js_add_n_times = m.instance.exports.call_js_add_n_times;
raw_thunk = m.instance.exports.thunk;
raw_add = m.instance.exports.add;
}));
Promise.all(wasms)
.then(() => {
if (document.readyState === 'loading')
document.addEventListener('DOMContentLoaded', run);
else
run();
})
.catch(console.error);