Generate benchmark graphs
This commit is contained in:
parent
e7fc519f05
commit
c9b0e7a50d
|
@ -1,6 +1,6 @@
|
|||
# hyperbuild
|
||||
|
||||
A fast one-pass in-place HTML minifier written in Rust with advanced whitespace handling.
|
||||
A fast one-pass in-place HTML minifier written in Rust with context-aware whitespace handling.
|
||||
|
||||
Available as:
|
||||
- CLI for Windows, macOS, and Linux.
|
||||
|
@ -13,6 +13,10 @@ Available as:
|
|||
- No extra heap memory is allocated during processing, which increases performance.
|
||||
- Context-aware whitespace handling allows maximum minification while retaining wanted spaces.
|
||||
|
||||
## Performance
|
||||
|
||||
![Chart showing speed of HTML minifiers](./bench/speed.png) ![Chart showing effectiveness of HTML minifiers](./bench/minification.png)
|
||||
|
||||
## Usage
|
||||
|
||||
### CLI
|
||||
|
|
209
bench/bench.js
209
bench/bench.js
|
@ -1,95 +1,174 @@
|
|||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const benchmark = require("benchmark");
|
||||
const chartjs = require('chartjs-node');
|
||||
const fs = require("fs");
|
||||
const htmlMinifier = require("html-minifier");
|
||||
const minimize = require("minimize");
|
||||
const hyperbuild = require("hyperbuild");
|
||||
const minimize = require("minimize");
|
||||
const path = require("path");
|
||||
|
||||
const tests_dir = path.join(__dirname, "tests");
|
||||
const tests = fs.readdirSync(tests_dir).map(name => ({
|
||||
const testsDir = path.join(__dirname, "tests");
|
||||
const tests = fs.readdirSync(testsDir).map(name => ({
|
||||
name,
|
||||
content: fs.readFileSync(path.join(tests_dir, name), "utf8"),
|
||||
content: fs.readFileSync(path.join(testsDir, name), "utf8"),
|
||||
}));
|
||||
|
||||
const programs = {
|
||||
'hyperbuild-nodejs': content => hyperbuild.minify(Buffer.from(content)),
|
||||
'html-minifier': content => htmlMinifier.minify(content, {
|
||||
caseSensitive: false,
|
||||
collapseBooleanAttributes: true,
|
||||
collapseInlineTagWhitespace: true,
|
||||
collapseWhitespace: true,
|
||||
conservativeWhitespace: false,
|
||||
customEventAttributes: [],
|
||||
decodeEntities: true,
|
||||
html5: true,
|
||||
ignoreCustomComments: [],
|
||||
ignoreCustomFragments: [],
|
||||
includeAutoGeneratedTags: true,
|
||||
keepClosingSlash: false,
|
||||
minifyCSS: false,
|
||||
minifyJS: false,
|
||||
minifyURLs: false,
|
||||
preserveLineBreaks: false,
|
||||
preventAttributesEscaping: false,
|
||||
processConditionalComments: true,
|
||||
processScripts: [],
|
||||
removeAttributeQuotes: true,
|
||||
removeComments: true,
|
||||
removeEmptyAttributes: false,
|
||||
removeEmptyElements: false,
|
||||
removeOptionalTags: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
removeTagWhitespace: true,
|
||||
sortAttributes: true,
|
||||
sortClassName: true,
|
||||
trimCustomFragments: false,
|
||||
useShortDoctype: true,
|
||||
}).length,
|
||||
'minimize': content => new minimize().parse(content).length,
|
||||
};
|
||||
|
||||
const colours = [
|
||||
{
|
||||
backgroundColor: '#9ad0f5',
|
||||
borderColor: '#47aaec',
|
||||
},
|
||||
{
|
||||
backgroundColor: '#ffb0c1',
|
||||
borderColor: '#ff87a1',
|
||||
},
|
||||
{
|
||||
backgroundColor: '#a4dfdf',
|
||||
borderColor: '#4bc0c0',
|
||||
},
|
||||
];
|
||||
const renderChart = async (file, cfg) => {
|
||||
const chart = new chartjs(450, 300);
|
||||
await chart.drawChart({
|
||||
...cfg,
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
barPercentage: 0.5,
|
||||
gridLines: {
|
||||
color: '#ccc',
|
||||
},
|
||||
ticks: {
|
||||
fontColor: '#222',
|
||||
},
|
||||
}],
|
||||
yAxes: [{
|
||||
gridLines: {
|
||||
color: '#666',
|
||||
},
|
||||
ticks: {
|
||||
fontColor: '#222',
|
||||
},
|
||||
}],
|
||||
},
|
||||
legend: {
|
||||
labels: {
|
||||
fontFamily: 'Ubuntu, sans-serif',
|
||||
fontColor: '#000',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
await chart.writeImageToFile('image/png', path.join(__dirname, `${file}.png`));
|
||||
};
|
||||
|
||||
const sizes = {};
|
||||
const setSize = (program, test, result) => {
|
||||
console.log(`Received result for ${program} - ${test}`);
|
||||
|
||||
if (!sizes[test]) {
|
||||
sizes[test] = {
|
||||
original: {
|
||||
result: tests.find(t => t.name === test).content.length,
|
||||
absolute: tests.find(t => t.name === test).content.length,
|
||||
relative: 1,
|
||||
},
|
||||
};
|
||||
}
|
||||
const original = sizes[test].original.result;
|
||||
const original = sizes[test].original.absolute;
|
||||
sizes[test][program] = {
|
||||
result: result,
|
||||
difference: `${((result - original) / original * 100).toFixed(2)}%`,
|
||||
absolute: result,
|
||||
relative: result / original,
|
||||
};
|
||||
};
|
||||
|
||||
const htmlMinifierSettings = {
|
||||
caseSensitive: false,
|
||||
collapseBooleanAttributes: true,
|
||||
collapseInlineTagWhitespace: true,
|
||||
collapseWhitespace: true,
|
||||
conservativeWhitespace: false,
|
||||
customEventAttributes: [],
|
||||
decodeEntities: true,
|
||||
html5: true,
|
||||
ignoreCustomComments: [],
|
||||
ignoreCustomFragments: [],
|
||||
includeAutoGeneratedTags: true,
|
||||
keepClosingSlash: false,
|
||||
minifyCSS: false,
|
||||
minifyJS: false,
|
||||
minifyURLs: false,
|
||||
preserveLineBreaks: false,
|
||||
preventAttributesEscaping: false,
|
||||
processConditionalComments: true,
|
||||
processScripts: [],
|
||||
removeAttributeQuotes: true,
|
||||
removeComments: true,
|
||||
removeEmptyAttributes: false,
|
||||
removeEmptyElements: false,
|
||||
removeOptionalTags: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
removeTagWhitespace: true,
|
||||
sortAttributes: true,
|
||||
sortClassName: true,
|
||||
trimCustomFragments: false,
|
||||
useShortDoctype: true,
|
||||
};
|
||||
// Run once to set sizes.
|
||||
for (const t of tests) {
|
||||
for (const p of Object.keys(programs)) {
|
||||
setSize(p, t.name, programs[p](t.content));
|
||||
}
|
||||
}
|
||||
|
||||
new benchmark.Suite()
|
||||
.add("hyperbuild", () => {
|
||||
const suite = new benchmark.Suite();
|
||||
for (const p of Object.keys(programs)) {
|
||||
suite.add(p, () => {
|
||||
for (const t of tests) {
|
||||
setSize("hyperbuild", t.name, hyperbuild.minify(Buffer.from(t.content)));
|
||||
programs[p](t.content);
|
||||
}
|
||||
})
|
||||
.add("html-minifier", () => {
|
||||
for (const t of tests) {
|
||||
setSize("html-minifier", t.name, htmlMinifier.minify(t.content, htmlMinifierSettings).length);
|
||||
}
|
||||
})
|
||||
.add("minimize", () => {
|
||||
for (const t of tests) {
|
||||
setSize("minimize", t.name, new minimize().parse(t.content).length);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
suite
|
||||
.on('cycle', event => {
|
||||
console.info(event.target.toString());
|
||||
})
|
||||
.on('complete', function () {
|
||||
console.info(`Fastest is ${this.filter('fastest').map('name')}`);
|
||||
Object.entries(sizes).forEach(([test, results]) => {
|
||||
console.info(test);
|
||||
console.table(results);
|
||||
.on('complete', async function () {
|
||||
const speedResults = this.map(b => ({
|
||||
name: b.name,
|
||||
count: b.count,
|
||||
ops: b.hz,
|
||||
})).sort((a, b) => a.hz - b.hz);
|
||||
await renderChart('speed', {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: speedResults.map(r => r.name),
|
||||
datasets: [{
|
||||
label: 'Operations per second',
|
||||
...colours[0],
|
||||
data: speedResults.map(r => r.ops),
|
||||
}],
|
||||
},
|
||||
});
|
||||
|
||||
const testNames = Object.keys(sizes);
|
||||
const programNames = Object.keys(programs);
|
||||
await renderChart('minification', {
|
||||
type: 'bar',
|
||||
scaleFontColor: 'red',
|
||||
data: {
|
||||
labels: testNames,
|
||||
datasets: programNames.map((program, i) => ({
|
||||
label: program,
|
||||
...colours[i],
|
||||
data: testNames.map(test => sizes[test][program].relative),
|
||||
})),
|
||||
},
|
||||
});
|
||||
})
|
||||
.run({'async': true});
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
|
@ -2,9 +2,11 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"benchmark": "2.1.4",
|
||||
"chart.js": "^2.9.3",
|
||||
"chartjs-node": "^1.7.1",
|
||||
"html-minifier": "3.5.19",
|
||||
"minimize": "2.2.0",
|
||||
"hyperbuild": "file:../nodejs"
|
||||
"hyperbuild": "file:../nodejs",
|
||||
"minimize": "2.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node bench.js"
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 7.9 KiB |
Loading…
Reference in New Issue