Move bench results to own folder and generate per-test graphs

This commit is contained in:
Wilson Lin 2020-01-19 01:14:30 +11:00
parent 3e7072c7e4
commit e683eed0b5
12 changed files with 236 additions and 149 deletions

View File

@ -17,7 +17,7 @@ Available as:
Speed and effectiveness of Node.js version compared to [html-minfier](https://github.com/kangax/html-minifier) and [minimize](https://github.com/Swaagie/minimize). See [bench](./bench) folder for more details.
<img width="435" alt="Chart showing speed of HTML minifiers" src="./bench/speed.png"> <img width="435" alt="Chart showing effectiveness of HTML minifiers" src="./bench/minification.png">
<img width="435" alt="Chart showing speed of HTML minifiers" src="./bench/results/average-speeds.png"> <img width="435" alt="Chart showing effectiveness of HTML minifiers" src="./bench/results/average-sizes.png">
## Usage

View File

@ -1,9 +1,9 @@
const benchmark = require('benchmark');
const childProcess = require('child_process');
const fs = require('fs');
const minimist = require('minimist');
const path = require('path');
const programs = require('./minifiers');
const minifiers = require('./minifiers');
const results = require('./results');
const tests = require('./tests');
const args = minimist(process.argv.slice(2));
@ -59,24 +59,24 @@ const setSize = (program, test, result) => {
// Run once to set sizes.
for (const t of tests) {
for (const p of Object.keys(programs)) {
for (const m of Object.keys(minifiers)) {
try {
setSize(p, t.name, programs[p](t.contentAsString, t.contentAsBuffer).length);
setSize(m, t.name, minifiers[m](t.contentAsString, t.contentAsBuffer).length);
} catch (err) {
console.error(`Failed to run ${p} on test ${t.name}:`);
console.error(`Failed to run ${m} on test ${t.name}:`);
console.error(err);
process.exit(1);
}
}
}
fs.writeFileSync(path.join(__dirname, 'minification.json'), JSON.stringify(sizes, null, 2));
results.writeSizeResults(sizes);
const runTest = test => new Promise((resolve, reject) => {
// Run JS libraries.
const suite = new benchmark.Suite();
for (const p of Object.keys(programs)) {
suite.add(p, () => {
programs[p](test.contentAsString, test.contentAsBuffer);
for (const m of Object.keys(minifiers)) {
suite.add(m, () => {
minifiers[m](test.contentAsString, test.contentAsBuffer);
});
}
suite
@ -87,7 +87,7 @@ const runTest = test => new Promise((resolve, reject) => {
});
(async () => {
const results = fromEntries(tests.map(t => [t.name, {}]));
const speeds = fromEntries(tests.map(t => [t.name, {}]));
// Run Rust library.
if (shouldRunRust) {
@ -96,12 +96,12 @@ const runTest = test => new Promise((resolve, reject) => {
'--iterations', 512,
'--tests', path.join(__dirname, 'tests'),
))) {
Object.assign(results[testName], {hyperbuild: testOps});
Object.assign(speeds[testName], {hyperbuild: testOps});
}
}
for (const t of tests) {
Object.assign(results[t.name], await runTest(t));
Object.assign(speeds[t.name], await runTest(t));
}
fs.writeFileSync(path.join(__dirname, 'speed.json'), JSON.stringify(results, null, 2));
results.writeSpeedResults(speeds);
})();

View File

@ -1,7 +1,5 @@
const chartjs = require('chartjs-node');
const fs = require('fs');
const path = require('path');
const tests = require('./tests');
const results = require('./results');
const colours = {
'hyperbuild': '#041f60',
@ -10,8 +8,6 @@ const colours = {
'html-minifier': '#2ca02c',
};
const programNames = ['minimize', 'html-minifier', 'hyperbuild-nodejs'];
const chartOptions = (title, displayLegend, yTick = t => t) => ({
options: {
title: {
@ -45,67 +41,73 @@ const chartOptions = (title, displayLegend, yTick = t => t) => ({
legend: {
display: displayLegend,
labels: {
fontFamily: 'Arial, sans-serif',
fontColor: '#000',
},
},
},
});
const renderChart = async (file, cfg) => {
const renderChart = async (cfg) => {
const chart = new chartjs(900, 650);
await chart.drawChart(cfg);
await chart.writeImageToFile('image/png', path.join(__dirname, `${file}.png`));
return chart.getImageBuffer('image/png');
};
(async () => {
const testNames = tests.map(t => t.name).sort();
const speedResults = JSON.parse(fs.readFileSync(path.join(__dirname, 'speed.json'), 'utf8'));
const speedData = programNames.map(program => [
program,
testNames
// Get OP/s for each test.
.map(test => speedResults[test][program] / speedResults[test]['hyperbuild-nodejs'])
// Sum all test OP/s.
.reduce((sum, c) => sum + c)
// Divide by tests count to get average OP/s.
/ testNames.length,
]).sort((a, b) => a[1] - b[1]);
await renderChart('speed', {
const averageSpeeds = results.getSpeedResults().getAverageRelativeSpeedPerMinifier('hyperbuild-nodejs');
results.writeAverageSpeedsGraph(await renderChart({
type: 'bar',
data: {
labels: speedData.map(([n]) => n),
// Node.js version is close enough to Rust version, so leave out Rust results.
// Include it this if this situation changes.
labels: averageSpeeds.map(([n]) => n),
datasets: [{
label: 'Average relative OP/s',
backgroundColor: '#1f77b4',
data: speedData.map(([_, v]) => v),
data: averageSpeeds.map(([_, v]) => v),
}],
},
...chartOptions('Average operations per second (higher is better)', false, tick => `${tick * 100}%`),
});
}));
const sizes = JSON.parse(fs.readFileSync(path.join(__dirname, 'minification.json'), 'utf8'));
const sizeData = programNames.map(program => [
program,
testNames
.map(test => sizes[test][program].relative)
.reduce((sum, c) => sum + c)
/ testNames.length,
]).sort((a, b) => b[1] - a[1]);
await renderChart('minification', {
const speeds = results.getSpeedResults().getRelativeFileSpeedsPerMinifier('hyperbuild-nodejs');
results.writeSpeedsGraph(await renderChart({
type: 'bar',
data: {
labels: speeds[0][1].map(([n]) => n),
datasets: speeds.map(([minifier, fileSpeeds]) => ({
label: minifier,
backgroundColor: colours[minifier],
data: fileSpeeds.map(([_, speed]) => speed),
})),
},
...chartOptions('Operations per second (higher is better)', true, tick => `${tick * 100}%`),
}));
const averageSizes = results.getSizeResults().getAverageRelativeSizePerMinifier();
results.writeAverageSizesGraph(await renderChart({
type: 'bar',
scaleFontColor: 'red',
data: {
labels: sizeData.map(([n]) => n),
labels: averageSizes.map(([n]) => n),
datasets: [{
label: 'Average minified size',
backgroundColor: '#2ca02c',
data: sizeData.map(([_, v]) => v),
data: averageSizes.map(([_, v]) => v),
}],
},
...chartOptions('Average minified size (lower is better)', false, tick => `${tick * 100}%`),
});
}));
const sizes = results.getSizeResults().getRelativeFileSizesPerMinifier();
results.writeSizesGraph(await renderChart({
type: 'bar',
data: {
labels: sizes[0][1].map(([n]) => n),
datasets: sizes.map(([minifier, fileSizes]) => ({
label: minifier,
backgroundColor: colours[minifier],
data: fileSizes.map(([_, size]) => size),
})),
},
...chartOptions('Minified size (lower is better)', true, tick => `${tick * 100}%`),
}));
})();

85
bench/results.js Normal file
View File

@ -0,0 +1,85 @@
const {join} = require('path');
const {readFileSync, writeFileSync} = require('fs');
const minifiers = require('./minifiers');
const tests = require('./tests');
const RESULTS_DIR = join(__dirname, 'results');
const SPEEDS_JSON = join(RESULTS_DIR, 'speeds.json');
const SPEEDS_GRAPH = join(RESULTS_DIR, 'speeds.png');
const AVERAGE_SPEEDS_GRAPH = join(RESULTS_DIR, 'average-speeds.png');
const SIZES_JSON = join(RESULTS_DIR, 'sizes.json');
const SIZES_GRAPH = join(RESULTS_DIR, 'sizes.png');
const AVERAGE_SIZES_GRAPH = join(RESULTS_DIR, 'average-sizes.png');
const minifierNames = Object.keys(minifiers);
const testNames = tests.map(t => t.name);
module.exports = {
writeSpeedResults(speeds) {
writeFileSync(SPEEDS_JSON, JSON.stringify(speeds, null, 2));
},
writeSizeResults(sizes) {
writeFileSync(SIZES_JSON, JSON.stringify(sizes, null, 2));
},
writeAverageSpeedsGraph(data) {
writeFileSync(AVERAGE_SPEEDS_GRAPH, data);
},
writeSpeedsGraph(data) {
writeFileSync(SPEEDS_GRAPH, data);
},
writeAverageSizesGraph(data) {
writeFileSync(AVERAGE_SIZES_GRAPH, data);
},
writeSizesGraph(data) {
writeFileSync(SIZES_GRAPH, data);
},
getSpeedResults() {
const data = JSON.parse(readFileSync(SPEEDS_JSON, 'utf8'));
return {
// Get minifier-speed pairs sorted by speed ascending.
getAverageRelativeSpeedPerMinifier(baselineMinifier) {
return minifierNames.map(minifier => [
minifier,
testNames
// Get OP/s for each test.
.map(test => data[test][minifier] / data[test][baselineMinifier])
// Sum all test OP/s.
.reduce((sum, c) => sum + c)
// Divide by tests count to get average OP/s.
/ testNames.length,
]).sort((a, b) => a[1] - b[1]);
},
// Get minifier-speeds pairs.
getRelativeFileSpeedsPerMinifier(baselineMinifier) {
return minifierNames.map(minifier => [
minifier,
testNames.map(test => [test, data[test][minifier] / data[test][baselineMinifier]]),
]);
},
};
},
getSizeResults() {
const data = JSON.parse(readFileSync(SIZES_JSON, 'utf8'));
return {
// Get minifier-size pairs sorted by size descending.
getAverageRelativeSizePerMinifier() {
return minifierNames.map(minifier => [
minifier,
testNames
.map(test => data[test][minifier].relative)
.reduce((sum, c) => sum + c)
/ testNames.length,
]).sort((a, b) => b[1] - a[1]);
},
// Get minifier-sizes pairs.
getRelativeFileSizesPerMinifier() {
return minifierNames.map(minifier => [
minifier,
testNames.map(test => [test, data[test][minifier].relative]),
]);
},
};
},
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -5,8 +5,8 @@
"relative": 1
},
"hyperbuild-nodejs": {
"absolute": 353501,
"relative": 0.9584752288403974
"absolute": 353722,
"relative": 0.9590744436250054
},
"html-minifier": {
"absolute": 355185,
@ -23,8 +23,8 @@
"relative": 1
},
"hyperbuild-nodejs": {
"absolute": 231706,
"relative": 0.9410641832204925
"absolute": 231728,
"relative": 0.9411535352961006
},
"html-minifier": {
"absolute": 234306,
@ -59,8 +59,8 @@
"relative": 1
},
"hyperbuild-nodejs": {
"absolute": 270833,
"relative": 0.8743825506389188
"absolute": 272551,
"relative": 0.8799291022851277
},
"html-minifier": {
"absolute": 277911,
@ -77,8 +77,8 @@
"relative": 1
},
"hyperbuild-nodejs": {
"absolute": 79806,
"relative": 0.9430212222904949
"absolute": 80117,
"relative": 0.9466961289407761
},
"html-minifier": {
"absolute": 81446,
@ -95,8 +95,8 @@
"relative": 1
},
"hyperbuild-nodejs": {
"absolute": 5744290,
"relative": 0.9094262560459782
"absolute": 5761075,
"relative": 0.9120836287948699
},
"html-minifier": {
"absolute": 5785725,
@ -113,8 +113,8 @@
"relative": 1
},
"hyperbuild-nodejs": {
"absolute": 196577,
"relative": 0.9963860307162046
"absolute": 196585,
"relative": 0.996426580161184
},
"html-minifier": {
"absolute": 196600,
@ -131,8 +131,8 @@
"relative": 1
},
"hyperbuild-nodejs": {
"absolute": 28154,
"relative": 0.8275476911319479
"absolute": 28203,
"relative": 0.8289879780135798
},
"html-minifier": {
"absolute": 29086,
@ -149,8 +149,8 @@
"relative": 1
},
"hyperbuild-nodejs": {
"absolute": 1263682,
"relative": 0.9967251233205608
"absolute": 1263691,
"relative": 0.9967322220416869
},
"html-minifier": {
"absolute": 1263150,
@ -167,8 +167,8 @@
"relative": 1
},
"hyperbuild-nodejs": {
"absolute": 648004,
"relative": 0.9945163810263407
"absolute": 648011,
"relative": 0.994527124192536
},
"html-minifier": {
"absolute": 647822,
@ -185,8 +185,8 @@
"relative": 1
},
"hyperbuild-nodejs": {
"absolute": 86693,
"relative": 0.7724652273476552
"absolute": 87177,
"relative": 0.7767778381701699
},
"html-minifier": {
"absolute": 88366,
@ -203,8 +203,8 @@
"relative": 1
},
"hyperbuild-nodejs": {
"absolute": 272480,
"relative": 0.8645931830152878
"absolute": 272916,
"relative": 0.8659766336457732
},
"html-minifier": {
"absolute": 266639,
@ -221,8 +221,8 @@
"relative": 1
},
"hyperbuild-nodejs": {
"absolute": 1319432,
"relative": 0.9350014704267072
"absolute": 1320740,
"relative": 0.9359283707317765
},
"html-minifier": {
"absolute": 1327244,

BIN
bench/results/sizes.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

67
bench/results/speeds.json Normal file
View File

@ -0,0 +1,67 @@
{
"Amazon": {
"hyperbuild-nodejs": 496.69767089636576,
"html-minifier": 36.22644969886744,
"minimize": 113.46511363852734
},
"BBC": {
"hyperbuild-nodejs": 530.7581308772171,
"html-minifier": 49.92474956429014,
"minimize": 160.83375271616242
},
"Bing": {
"hyperbuild-nodejs": 2143.2738405584682,
"html-minifier": 224.01168295253845,
"minimize": 548.4301545148764
},
"Bootstrap": {
"hyperbuild-nodejs": 271.9278016916176,
"html-minifier": 8.063475087253176,
"minimize": 22.267478295105477
},
"Coding Horror": {
"hyperbuild-nodejs": 1023.4279917437968,
"html-minifier": 48.701642742613835,
"minimize": 186.3127301413615
},
"ECMA-262": {
"hyperbuild-nodejs": 15.516204982806318,
"html-minifier": 0.44813616072549084,
"minimize": 1.3379096629237164
},
"Google": {
"hyperbuild-nodejs": 1828.0608850870312,
"html-minifier": 243.05811241714517,
"minimize": 575.3743754787441
},
"Hacker News": {
"hyperbuild-nodejs": 2098.1972805031955,
"html-minifier": 73.59734557807897,
"minimize": 271.03920934848253
},
"NY Times": {
"hyperbuild-nodejs": 264.01040398999527,
"html-minifier": 36.483800875972946,
"minimize": 86.74545622195224
},
"Reddit": {
"hyperbuild-nodejs": 402.9146825613306,
"html-minifier": 44.44689052201565,
"minimize": 124.65890370904272
},
"Stack Overflow": {
"hyperbuild-nodejs": 822.1712538713629,
"html-minifier": 40.65363487825213,
"minimize": 158.40795613979014
},
"Twitter": {
"hyperbuild-nodejs": 279.88535523093344,
"html-minifier": 36.231829473085334,
"minimize": 167.45777652008428
},
"Wikipedia": {
"hyperbuild-nodejs": 54.31371594883309,
"html-minifier": 2.808823542495513,
"minimize": 8.687326663074192
}
}

BIN
bench/results/speeds.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View File

@ -1,17 +1,17 @@
const fs = require('fs');
const mkdirp = require('mkdirp');
const path = require('path');
const programs = require('./minifiers');
const minifiers = require('./minifiers');
const tests = require('./tests');
for (const t of tests) {
for (const p of Object.keys(programs)) {
for (const m of Object.keys(minifiers)) {
try {
const minPath = path.join(__dirname, 'min', p, `${t.name}.html`);
const minPath = path.join(__dirname, 'min', m, `${t.name}.html`);
mkdirp.sync(path.dirname(minPath));
fs.writeFileSync(minPath, programs[p](t.contentAsString, t.contentAsBuffer));
fs.writeFileSync(minPath, minifiers[m](t.contentAsString, t.contentAsBuffer));
} catch (err) {
console.error(`Failed to run ${p} on test ${t.name}:`);
console.error(`Failed to run ${m} on test ${t.name}:`);
console.error(err);
process.exit(1);
}

View File

@ -1,67 +0,0 @@
{
"Amazon": {
"hyperbuild-nodejs": 499.9428905642954,
"html-minifier": 36.3036627447208,
"minimize": 113.33527430279831
},
"BBC": {
"hyperbuild-nodejs": 534.5358584753244,
"html-minifier": 49.23362197831472,
"minimize": 161.96803168572117
},
"Bing": {
"hyperbuild-nodejs": 2137.27013270331,
"html-minifier": 223.08512226985184,
"minimize": 550.9181761277221
},
"Bootstrap": {
"hyperbuild-nodejs": 277.1391080206004,
"html-minifier": 8.043255283692064,
"minimize": 22.245439492019898
},
"Coding Horror": {
"hyperbuild-nodejs": 1096.4910673601032,
"html-minifier": 49.83595257976626,
"minimize": 188.32749988717788
},
"ECMA-262": {
"hyperbuild-nodejs": 16.200240897950334,
"html-minifier": 0.45522858062374655,
"minimize": 1.3356053866389666
},
"Google": {
"hyperbuild-nodejs": 1832.4626475818236,
"html-minifier": 242.1462398878334,
"minimize": 564.1884364813526
},
"Hacker News": {
"hyperbuild-nodejs": 2127.8084431041266,
"html-minifier": 74.78979361035866,
"minimize": 272.3995630011103
},
"NY Times": {
"hyperbuild-nodejs": 265.55362689112695,
"html-minifier": 37.146711151201565,
"minimize": 87.7133467873164
},
"Reddit": {
"hyperbuild-nodejs": 391.75000439723124,
"html-minifier": 45.067854272152125,
"minimize": 125.87983932864549
},
"Stack Overflow": {
"hyperbuild-nodejs": 818.3755008258345,
"html-minifier": 41.43093414076361,
"minimize": 159.71387801780298
},
"Twitter": {
"hyperbuild-nodejs": 274.57816497268476,
"html-minifier": 36.94949014023178,
"minimize": 168.81796573617953
},
"Wikipedia": {
"hyperbuild-nodejs": 54.852210553433345,
"html-minifier": 2.821530343574604,
"minimize": 8.66394750522524
}
}