diff --git a/README.md b/README.md
index 628be85..02837b5 100644
--- a/README.md
+++ b/README.md
@@ -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.
-
+
## Usage
diff --git a/bench/bench.js b/bench/bench.js
index d1797b2..9aec8dc 100644
--- a/bench/bench.js
+++ b/bench/bench.js
@@ -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);
})();
diff --git a/bench/graph.js b/bench/graph.js
index ab43cfc..e938659 100644
--- a/bench/graph.js
+++ b/bench/graph.js
@@ -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}%`),
+ }));
})();
diff --git a/bench/results.js b/bench/results.js
new file mode 100644
index 0000000..f5ee7bf
--- /dev/null
+++ b/bench/results.js
@@ -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]),
+ ]);
+ },
+ };
+ },
+};
diff --git a/bench/minification.png b/bench/results/average-sizes.png
similarity index 56%
rename from bench/minification.png
rename to bench/results/average-sizes.png
index 2b84b37..8837be9 100644
Binary files a/bench/minification.png and b/bench/results/average-sizes.png differ
diff --git a/bench/speed.png b/bench/results/average-speeds.png
similarity index 51%
rename from bench/speed.png
rename to bench/results/average-speeds.png
index 223a884..5fd95a6 100644
Binary files a/bench/speed.png and b/bench/results/average-speeds.png differ
diff --git a/bench/minification.json b/bench/results/sizes.json
similarity index 84%
rename from bench/minification.json
rename to bench/results/sizes.json
index 15ac9d7..86387b3 100644
--- a/bench/minification.json
+++ b/bench/results/sizes.json
@@ -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,
diff --git a/bench/results/sizes.png b/bench/results/sizes.png
new file mode 100644
index 0000000..3b4e58f
Binary files /dev/null and b/bench/results/sizes.png differ
diff --git a/bench/results/speeds.json b/bench/results/speeds.json
new file mode 100644
index 0000000..f2f438e
--- /dev/null
+++ b/bench/results/speeds.json
@@ -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
+ }
+}
\ No newline at end of file
diff --git a/bench/results/speeds.png b/bench/results/speeds.png
new file mode 100644
index 0000000..2322f1c
Binary files /dev/null and b/bench/results/speeds.png differ
diff --git a/bench/run.js b/bench/run.js
index a97294f..c8d3635 100644
--- a/bench/run.js
+++ b/bench/run.js
@@ -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);
}
diff --git a/bench/speed.json b/bench/speed.json
deleted file mode 100644
index 2c25fcf..0000000
--- a/bench/speed.json
+++ /dev/null
@@ -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
- }
-}
\ No newline at end of file