Skip to content

Commit

Permalink
Revamp HTML benchmark, save CSV
Browse files Browse the repository at this point in the history
  • Loading branch information
RReverser committed Dec 31, 2022
1 parent b8e7127 commit cf09b72
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 131 deletions.
1 change: 1 addition & 0 deletions benchmarks/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/node_modules
/results.csv
133 changes: 38 additions & 95 deletions benchmarks/index.html
Original file line number Diff line number Diff line change
@@ -1,97 +1,40 @@
<select id="suite">
<option value="parse">Parse</option>
<option value="serialize">Serialize</option>
</select>
<input type="button" id="run" disabled value="Run" />
<input type="button" id="singleRun" disabled value="Single run" />
<pre id="output"></pre>
<script type="module">
import 'https://unpkg.com/lodash';
import 'https://unpkg.com/benchmark';

const benches = (async () => {
const benches = await import('./pkg/serde_wasm_bindgen_benches.js');
await benches.default();
return benches;
})();

const inputs = ['canada', 'citm_catalog', 'twitter'].map(async input => {
const res = await fetch(`data/${input}.json`);
return {
input,
json: await res.json()
};
});

Promise.all([
benches,
Promise.all(inputs)
]).then(([benches, inputs]) => {
let suites = {};

function addSuite(name) {
suites[name] = new Benchmark.Suite(name, {
onError: event => output.textContent = target.error,
onCycle: event => output.textContent += event.target.toString() + '\n',
onComplete: endRun,
});
}

function addFunc(suite, name, func) {
Object.defineProperty(func, 'name', {
value: name
});
suite.add(name, func);
}

addSuite('parse');
addSuite('serialize');

for (let { input, json } of inputs) {
for (const lib of ['serde_json', 'serde_wasm_bindgen']) {
const parse = benches[`parse_${input}_with_${lib}`];
addFunc(suites.parse, `${input} x ${lib}`, () => parse(json).free());

const serialize = benches[`serialize_${input}_with_${lib}`];
let parsed = parse(json);
addFunc(suites.serialize, `${input} x ${lib}`, () => serialize(parsed), {
onComplete: () => parsed.free()
});
}
}

function startRun() {
let suite = suites[document.getElementById('suite').value];
output.textContent = `Running '${suite.name}' suite...\n`;
run.disabled = true;
singleRun.disabled = true;
return suite;
}

function endRun() {
output.textContent += 'Done!';
run.disabled = false;
singleRun.disabled = false;
}

run.onclick = () => {
startRun().run({ async: true });
};

singleRun.onclick = () => {
startRun().forEach(bench => {
let start = performance.now();
bench.fn();
let passed = (performance.now() - start);
output.textContent += `${bench.name} x ${(1000 / passed).toFixed(2)} ops/sec\n`;
});
endRun();
};

run.disabled = false;
singleRun.disabled = false;
}).catch(err => {
console.error(err);
output.textContent = err;
});
let currentGroups = [document.body];

let origGroup = console.group;
console.group = name => {
origGroup(name);
let group = document.createElement('fieldset');
let groupSummary = document.createElement('legend');
groupSummary.textContent = name;
group.appendChild(groupSummary);
currentGroups[0].appendChild(group);
currentGroups.unshift(group);
};

let origGroupEnd = console.groupEnd;
console.groupEnd = () => {
origGroupEnd();
currentGroups.shift();
};

let origLog = console.log;
console.log = (...args) => {
origLog(...args);
let div = document.createElement('div');
div.textContent = args.join(' ');
currentGroups[0].append(div);
};

let origError = console.error;
console.error = (...args) => {
origError(...args);
let div = document.createElement('div');
div.textContent = args.join(' ');
div.style.color = 'red';
currentGroups[0].append(div);
};
</script>
<script src="node_modules/lodash/lodash.js"></script>
<script src="node_modules/benchmark/benchmark.js"></script>
<script src="index.mjs" type="module"></script>
111 changes: 79 additions & 32 deletions benchmarks/index.mjs
Original file line number Diff line number Diff line change
@@ -1,46 +1,73 @@
import Benchmark from 'benchmark';
import { readdirSync, readFileSync } from 'fs';
import prettyBytes from 'pretty-bytes';
import { fileSync as brotliSize } from 'brotli-size';
import { createRequire } from 'module';
import { inspect } from 'util';

const { Suite } = Benchmark;
const require = createRequire(import.meta.url);
const { Suite } = globalThis.Benchmark ?? (await import('benchmark')).default;

let suiteOps = {
parse: {},
serialize: {}
};

const libs = readdirSync('pkg').map(dir => ({
name: dir,
impl: require(`./pkg/${dir}`)
}));
const libs = await Promise.all(
['serde-wasm-bindgen', 'serde-json', 'serde-wasm-bindgen-reftypes']
.map(async dir => {
try {
const impl = await import(`./pkg/${dir}/serde_wasm_bindgen_benches.js`);
await impl.default(); // Init Wasm
return { name: dir, impl };
} catch (err) {
console.warn(err);
return null;
}
})
.filter(Boolean)
);

let readFile,
filter;

const isNode = typeof process !== 'undefined';

if (isNode) {
const prettyBytes = (await import('pretty-bytes')).default;
const brotliSize = await import('brotli-size');

console.log('=== Sizes ===');
for (let { name } of libs) {
const [js, wasm] = [
'serde_wasm_bindgen_benches.js',
'serde_wasm_bindgen_benches_bg.wasm'
].map(file => prettyBytes(brotliSize.fileSync(`pkg/${name}/${file}`)));

console.log(`${name}: JS = ${js}, Wasm = ${wasm}`);
}
console.log();

console.log('=== Sizes ===');
for (let { name } of libs) {
const [js, wasm] = [
'serde_wasm_bindgen_benches.js',
'serde_wasm_bindgen_benches_bg.wasm'
].map(file => prettyBytes(brotliSize(`pkg/${name}/${file}`)));
const readFileImpl = (await import('fs/promises')).readFile;
readFile = name => readFileImpl(name, 'utf8');

console.log(`${name}: JS = ${js}, Wasm = ${wasm}`);
filter = process.argv[2];
} else {
readFile = name =>
fetch(name).then(res => {
if (!res.ok) {
throw new Error(`Failed to fetch ${name}`);
}
return res.text();
});

filter = new URLSearchParams(location.search).get('filter');
}
console.log();

function loadData(name) {
return JSON.parse(readFileSync(`./data/${name}.json`, 'utf8'));
filter = new RegExp(filter ?? '(?:)', 'i');

async function loadData(name) {
return JSON.parse(await readFile(`./data/${name}.json`, 'utf8'));
}

const datasets = {
Canada: loadData('canada'),
CitmCatalog: loadData('citm_catalog'),
Twitter: loadData('twitter')
Canada: await loadData('canada'),
CitmCatalog: await loadData('citm_catalog'),
Twitter: await loadData('twitter')
};

let filter = new RegExp(process.argv[2] || '(?:)');

for (let { name: libName, impl } of libs) {
for (let [dataName, json] of Object.entries(datasets)) {
let { parse } = impl[dataName];
Expand Down Expand Up @@ -68,15 +95,35 @@ for (let { name: libName, impl } of libs) {

console.log('=== Benchmarks ===');

let csv = '';

for (let [op, suites] of Object.entries(suiteOps)) {
console.group(op);
for (let suite of Object.values(suites)) {
console.group(suite.name);
suite
.on('error', event => console.error(event.target.error))
.on('cycle', event => console.log(event.target.toString()))
.run();
await new Promise((resolve, reject) => {
suite
.on('error', event => reject(event.target.error))
.on('cycle', event => {
console.log(event.target.toString());
csv += `${op},${suite.name},${event.target.name},${event.target.hz}\n`;
})
.on('complete', resolve)
.run({
async: true
});
});
console.groupEnd();
}
console.groupEnd();
}

if (isNode) {
(await import('fs')).writeFileSync('results.csv', csv);
} else {
let csvLink = document.createElement('a');
csvLink.href = URL.createObjectURL(new Blob([csv], { type: 'text/csv' }));
csvLink.download = 'results.csv';
csvLink.textContent = 'Download CSV';
document.body.append('Done! ', csvLink);
}
8 changes: 4 additions & 4 deletions benchmarks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
"pretty-bytes": "^6.0.0"
},
"scripts": {
"build:swb": "wasm-pack build -t nodejs --out-dir pkg/serde-wasm-bindgen -- --features serde-wasm-bindgen",
"build:swb-reftypes": "cross-env RUSTFLAGS=\"-C target-feature=+reference-types\" WASM_BINDGEN_EXTERNREF=1 wasm-pack build -t nodejs --out-dir pkg/serde-wasm-bindgen-reftypes -- --features serde-wasm-bindgen",
"build:json": "wasm-pack build -t nodejs --out-dir pkg/serde-json -- --features serde-json",
"build:msgpack": "wasm-pack build -t nodejs --out-dir pkg/msgpack -- --features msgpack",
"build:swb": "wasm-pack build -t web --out-dir pkg/serde-wasm-bindgen -- --features serde-wasm-bindgen",
"build:swb-reftypes": "cross-env RUSTFLAGS=\"-C target-feature=+reference-types\" WASM_BINDGEN_EXTERNREF=1 wasm-pack build -t web --out-dir pkg/serde-wasm-bindgen-reftypes -- --features serde-wasm-bindgen",
"build:json": "wasm-pack build -t web --out-dir pkg/serde-json -- --features serde-json",
"build:msgpack": "wasm-pack build -t web --out-dir pkg/msgpack -- --features msgpack",
"build": "npm run build:swb && npm run build:swb-reftypes && npm run build:json",
"test": "node index.mjs"
}
Expand Down

0 comments on commit cf09b72

Please sign in to comment.