Improve bench

This commit is contained in:
Wilson Lin 2021-08-09 19:24:43 +10:00
parent 2e4015de40
commit 7d5ba5033b
6 changed files with 67 additions and 98 deletions

View File

@ -19,8 +19,7 @@ A Rust HTML minifier meticulously optimised for speed and effectiveness, with bi
Comparison with [html-minfier](https://github.com/kangax/html-minifier) and [minimize](https://github.com/Swaagie/minimize), run on the top web pages. [See the breakdown here.](./bench) Comparison with [html-minfier](https://github.com/kangax/html-minifier) and [minimize](https://github.com/Swaagie/minimize), run on the top web pages. [See the breakdown here.](./bench)
<img width="415" alt="Chart showing speed of HTML minifiers" src="https://wilsonl.in/minify-html/bench/0.6.1/core/average-speeds.png"> <img width="415" alt="Chart showing speed of HTML minifiers" src="https://wilsonl.in/minify-html/bench/0.6.1/core/average-speeds.png"><img width="415" alt="Chart showing compression of HTML minifiers" src="https://wilsonl.in/minify-html/bench/0.6.1/core/average-sizes.png">
<img width="415" alt="Chart showing compression of HTML minifiers" src="https://wilsonl.in/minify-html/bench/0.6.1/core/average-sizes.png">
The [onepass](https://github.com/wilsonzlin/minify-html/tree/master/rust/onepass) variant is even more optimised for speed. See its [README](https://github.com/wilsonzlin/minify-html/tree/master/rust/onepass) for more details. The [onepass](https://github.com/wilsonzlin/minify-html/tree/master/rust/onepass) variant is even more optimised for speed. See its [README](https://github.com/wilsonzlin/minify-html/tree/master/rust/onepass) for more details.

View File

@ -6,12 +6,7 @@ It also contains a set of common web pages as tests for benchmarking.
## Comparison ## Comparison
The [Node.js version of minify-html](../nodejs) is tested against [html-minfier](https://github.com/kangax/html-minifier) and [minimize](https://github.com/Swaagie/minimize) in two dimensions: Each minifier is run against each file in the [inputs](./inputs) folder, which are HTML pages fetched from popular websites:
- Speed as operations per second.
- Minified file size compared to the original.
[Benchmark.js](https://benchmarkjs.com) is used to determine speed. Each minifier is run against each file in the [tests](./tests) folder, which are HTML pages fetched from popular websites:
|File name|URL| |File name|URL|
|---|---| |---|---|
@ -35,37 +30,14 @@ For more information on how the tests are fetched, see [fetch.js](./fetch.js).
On this [project's README](../README.md), average graphs are shown. Graphs showing per-test results are shown below: On this [project's README](../README.md), average graphs are shown. Graphs showing per-test results are shown below:
<img width="435" alt="Chart showing speed of HTML minifiers per test" src="https://wilsonl.in/minify-html/bench/0.6.1/core/speeds.png"> <img width="435" alt="Chart showing effectiveness of HTML minifiers per test" src="https://wilsonl.in/minify-html/bench/0.6.1/core/sizes.png"> <img width="435" alt="Chart showing speed of HTML minifiers per test" src="https://wilsonl.in/minify-html/bench/0.6.1/core/speeds.png"><img width="435" alt="Chart showing effectiveness of HTML minifiers per test" src="https://wilsonl.in/minify-html/bench/0.6.1/core/sizes.png">
Since speed depends on the input, speed charts show performance relative to the Node.js minify-html as a percentage. Results depend on the input, so charts show performance relative to minify-html as a percentage.
The settings used for each minifier can be found in [minifiers.js](./minifiers.js). Some settings to note:
- `conservativeCollapse` is enabled for html-minifier as otherwise some whitespace would be unsafely removed with side effects. minify-html can safely remove whitespace with context if configured properly.
## Running ## Running
Make sure to install the dependencies listed in [package.json](./package.json) by running `npm i` or `yarn`. Run [build](./build) to build the minifiers.
Run [build.sh](./build.sh) to build @minify-html/js with the local minify-html. Run [run](./run) to benchmark the performance of each HTML minifier against each test and record the op/s results.
Run [sizes.js](sizes.js) to run each HTML minifier against each test and record the minified size results. This will also output the minified files in `min` if inspection of minified outputs is necessary. [compare.sh](./compare.sh) is a useful script for viewing a character-by-character diff between the minified outputs of minify-html and html-minifier for a specific test. Pass the test's file name as the first argument.
Run [speeds.js](./speeds.js) to benchmark the performance of each HTML minifier against each test and record the op/s results.
Run [graph.js](./graph.js) to render graphs from recorded speed and size results in the `results` folder. Run [graph.js](./graph.js) to render graphs from recorded speed and size results in the `results` folder.
## minify-html-bench
The [minify-html-bench](./minify-html-bench) folder contains a Rust executable subproject that runs the local minify-html on all tests for many iterations to calculate speed as operations per second.
This can be useful for profiling the core code or checking the performance of minify-html in other languages with native bindings.
It takes two arguments:
- `--tests`: path to the folder containing tests to use as inputs.
- `--iterations`: how many iterations to run per test.
The results will be written to stdout as a JSON object, where properties are the test file names and values are the operations per second.
Profiling minify-html can be done on Linux by using [profile.sh](./profile.sh), which uses `perf`. The generated report can be used using `perf report`.

View File

@ -1,7 +1,7 @@
const results = require("./results"); const fs = require("fs/promises");
const https = require("https"); const https = require("https");
const path = require("path"); const path = require("path");
const fs = require("fs/promises"); const results = require("./results");
const GRAPHS_DIR = path.join(__dirname, "graphs"); const GRAPHS_DIR = path.join(__dirname, "graphs");
const SPEEDS_GRAPH = path.join(GRAPHS_DIR, "speeds.png"); const SPEEDS_GRAPH = path.join(GRAPHS_DIR, "speeds.png");
@ -10,7 +10,6 @@ const AVERAGE_SPEEDS_GRAPH = path.join(GRAPHS_DIR, "average-speeds.png");
const AVERAGE_SIZES_GRAPH = path.join(GRAPHS_DIR, "average-sizes.png"); const AVERAGE_SIZES_GRAPH = path.join(GRAPHS_DIR, "average-sizes.png");
const speedColours = { const speedColours = {
"@minify-html/js": "#2e61bd",
"minify-html": "#2e61bd", "minify-html": "#2e61bd",
"minify-html-onepass": "#222", "minify-html-onepass": "#222",
}; };
@ -21,12 +20,57 @@ const sizeColours = {
}; };
const defaultSizeColour = "rgb(188, 188, 188)"; const defaultSizeColour = "rgb(188, 188, 188)";
const averageChartOptions = (label) => ({
options: {
legend: {
display: false,
},
scales: {
xAxes: [
{
barPercentage: 0.5,
gridLines: {
display: false,
},
ticks: {
fontColor: "#555",
fontSize: 20,
},
},
],
yAxes: [
{
type: "linear",
scaleLabel: {
display: true,
fontColor: '#222',
fontSize: 24,
fontStyle: "bold",
labelString: label,
padding: 12,
},
position: "left",
ticks: {
callback: "$$$_____REPLACE_WITH_TICK_CALLBACK_____$$$",
fontColor: "#222",
fontSize: 20,
},
gridLines: {
color: "#eee",
},
},
],
},
},
});
const breakdownChartOptions = (title) => ({ const breakdownChartOptions = (title) => ({
options: { options: {
legend: { legend: {
display: true, display: true,
labels: { labels: {
fontColor: "#000", fontColor: "#000",
fontSize: 20,
}, },
}, },
title: { title: {
@ -64,52 +108,6 @@ const breakdownChartOptions = (title) => ({
}, },
}); });
const axisLabel = (fontColor, labelString) => ({
display: true,
fontColor,
fontSize: 24,
fontStyle: "bold",
labelString,
padding: 12,
});
const averageChartOptions = (label) => ({
options: {
legend: {
display: false,
},
scales: {
xAxes: [
{
barPercentage: 0.5,
gridLines: {
display: false,
},
ticks: {
fontColor: "#555",
fontSize: 16,
},
},
],
yAxes: [
{
type: "linear",
scaleLabel: axisLabel("#222", label),
position: "left",
ticks: {
callback: "$$$_____REPLACE_WITH_TICK_CALLBACK_____$$$",
fontColor: "#222",
fontSize: 16,
},
gridLines: {
color: "#eee",
},
},
],
},
},
});
const renderChart = (cfg, width, height) => const renderChart = (cfg, width, height) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
const req = https.request("https://quickchart.io/chart", { const req = https.request("https://quickchart.io/chart", {
@ -147,9 +145,7 @@ const renderChart = (cfg, width, height) =>
await fs.mkdir(GRAPHS_DIR, { recursive: true }); await fs.mkdir(GRAPHS_DIR, { recursive: true });
const res = results.calculate(); const res = results.calculate();
const speedMinifiers = [...res.minifiers].sort( const speedMinifiers = ["html-minifier", "minimize", "minify-html", "minify-html-onepass"];
(a, b) => res.minifierAvgOps[a] - res.minifierAvgOps[b]
);
const sizeMinifiers = ["minimize", "html-minifier", "minify-html"]; const sizeMinifiers = ["minimize", "html-minifier", "minify-html"];
const inputs = Object.keys(res.inputSizes).sort(); const inputs = Object.keys(res.inputSizes).sort();
@ -159,7 +155,7 @@ const renderChart = (cfg, width, height) =>
{ {
type: "bar", type: "bar",
data: { data: {
labels: speedMinifiers.map((m) => m.replace(" (", "\n(")), labels: speedMinifiers,
datasets: [ datasets: [
{ {
backgroundColor: speedMinifiers.map( backgroundColor: speedMinifiers.map(
@ -184,7 +180,7 @@ const renderChart = (cfg, width, height) =>
{ {
type: "bar", type: "bar",
data: { data: {
labels: sizeMinifiers.map((m) => m.replace(" (", "\n(")), labels: sizeMinifiers,
datasets: [ datasets: [
{ {
backgroundColor: sizeMinifiers.map( backgroundColor: sizeMinifiers.map(
@ -212,14 +208,14 @@ const renderChart = (cfg, width, height) =>
label: minifier, label: minifier,
data: inputs.map( data: inputs.map(
(input) => (input) =>
res.perInputOps[minifier][input] / res.maxInputOps[input] res.perInputOps[minifier][input] / res.perInputOps['minify-html'][input]
), ),
})), })),
}, },
...breakdownChartOptions("Operations per second (higher is better)"), ...breakdownChartOptions("Operations per second, relative to minify-html"),
}, },
900, 800,
1000 1280
) )
); );
@ -232,13 +228,13 @@ const renderChart = (cfg, width, height) =>
labels: inputs, labels: inputs,
datasets: sizeMinifiers.map((minifier) => ({ datasets: sizeMinifiers.map((minifier) => ({
label: minifier, label: minifier,
data: inputs.map((input) => res.perInputReduction[minifier][input]), data: inputs.map((input) => res.perInputReduction[minifier][input] / res.perInputReduction['minify-html'][input]),
})), })),
}, },
...breakdownChartOptions("Size reduction (higher is better)"), ...breakdownChartOptions("Size reduction, relative to minify-html"),
}, },
900, 800,
1000 1280
) )
); );
})(); })();

1
debug/diff/README.md Normal file
View File

@ -0,0 +1 @@
[compare.sh](./compare.sh) is a useful script for viewing a character-by-character diff between the minified outputs of minify-html and html-minifier for a specific test. Pass the test's file name as the first argument.

1
debug/prof/README.md Normal file
View File

@ -0,0 +1 @@
Profiling minify-html can be done on Linux by using [profile.sh](./profile.sh), which uses `perf`. The generated report can be used using `perf report`.

View File

@ -8,7 +8,7 @@ An HTML minifier that provides the functionality of [minify-html](https://github
## Performance ## Performance
<img alt="Chart showing speed of HTML minifiers" src="https://wilsonl.in/minify-html/bench/0.6.1/core/average-speeds.png"> <img width="600" alt="Chart showing speed of HTML minifiers" src="https://wilsonl.in/minify-html/bench/0.6.1/core/average-speeds.png">
## Usage ## Usage