Generify bench
This commit is contained in:
parent
4fc9496829
commit
a28e69ddb0
|
@ -1,7 +1 @@
|
|||
/minify-html-bench/Cargo.lock
|
||||
/minify-html-bench/target/
|
||||
/min/
|
||||
/results*/
|
||||
node_modules/
|
||||
package-lock.json
|
||||
perf.data*
|
||||
/results/
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
10
|
|
@ -7,10 +7,14 @@ for i in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
|
|||
echo performance | sudo dd status=none of="$i"
|
||||
done
|
||||
|
||||
node sizes.js
|
||||
# We need sudo to use `nice` but want to keep using current `node`, so use explicit path in case sudo decides to ignore PATH.
|
||||
node_path="$(which node)"
|
||||
echo "Using Node.js at $node_path"
|
||||
sudo --preserve-env=HTML_ONLY nice -n -20 taskset -c 1 "$node_path" speeds.js
|
||||
sudo chown -R "$USER:$USER" results
|
||||
node graph.js
|
||||
results_dir="$PWD/results"
|
||||
input_dir="$PWD/inputs"
|
||||
iterations=100
|
||||
|
||||
pushd runners
|
||||
for r in *; do
|
||||
pushd "$r"
|
||||
./build
|
||||
sudo --preserve-env=MHB_HTML_ONLY,PATH MHB_ITERATIONS=$iterations MHB_INPUT_DIR="$input_dir" nice -n -20 taskset -c 1 ./run | tee "$results_dir/$r.json"
|
||||
popd
|
||||
done
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
pushd "$(dirname "$0")"
|
||||
|
||||
pushd ../nodejs
|
||||
npm run build
|
||||
popd
|
||||
|
||||
pushd minify-html-bench
|
||||
cargo build --release
|
||||
popd
|
||||
|
||||
popd
|
|
@ -1,54 +1,71 @@
|
|||
const {promises: fs} = require('fs');
|
||||
const request = require('request-promise-native');
|
||||
const path = require('path');
|
||||
const { promises: fs } = require("fs");
|
||||
const childProcess = require("child_process");
|
||||
const path = require("path");
|
||||
|
||||
const tests = {
|
||||
"Amazon": "https://www.amazon.com/",
|
||||
"BBC": "https://www.bbc.co.uk/",
|
||||
"Bootstrap": "https://getbootstrap.com/docs/3.4/css/",
|
||||
"Bing": "https://www.bing.com/",
|
||||
Amazon: "https://www.amazon.com/",
|
||||
BBC: "https://www.bbc.co.uk/",
|
||||
Bootstrap: "https://getbootstrap.com/docs/3.4/css/",
|
||||
Bing: "https://www.bing.com/",
|
||||
"Coding Horror": "https://blog.codinghorror.com/",
|
||||
"ECMA-262": "https://www.ecma-international.org/ecma-262/10.0/index.html",
|
||||
"Google": "https://www.google.com/",
|
||||
Google: "https://www.google.com/",
|
||||
"Hacker News": "https://news.ycombinator.com/",
|
||||
"NY Times": "https://www.nytimes.com/",
|
||||
"Reddit": "https://www.reddit.com/",
|
||||
Reddit: "https://www.reddit.com/",
|
||||
"Stack Overflow": "https://www.stackoverflow.com/",
|
||||
"Twitter": "https://twitter.com/",
|
||||
"Wikipedia": "https://en.wikipedia.org/wiki/Soil",
|
||||
Twitter: "https://twitter.com/",
|
||||
Wikipedia: "https://en.wikipedia.org/wiki/Soil",
|
||||
};
|
||||
|
||||
const fetchTest = async (name, url) => {
|
||||
const html = await request({
|
||||
url,
|
||||
gzip: true,
|
||||
headers: {
|
||||
'Accept': '*/*',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; rv:71.0) Gecko/20100101 Firefox/71.0',
|
||||
},
|
||||
const fetchTest = (name, url) =>
|
||||
new Promise((resolve, reject) => {
|
||||
// Use curl to follow redirects without needing a Node.js library.
|
||||
childProcess.execFile(
|
||||
"curl",
|
||||
[
|
||||
"-H",
|
||||
`User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; rv:71.0) Gecko/20100101 Firefox/71.0`,
|
||||
"-H",
|
||||
"Accept: */*",
|
||||
"-fLSs",
|
||||
url,
|
||||
],
|
||||
(error, stdout, stderr) => {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
if (stderr) {
|
||||
return reject(new Error(`stderr: ${stderr}`));
|
||||
}
|
||||
resolve([name, stdout]);
|
||||
}
|
||||
);
|
||||
});
|
||||
console.log(`Fetched ${name}`);
|
||||
return [name, html];
|
||||
};
|
||||
|
||||
(async () => {
|
||||
const existing = await fs.readdir(path.join(__dirname, 'tests'));
|
||||
await Promise.all(existing.map(e => fs.unlink(path.join(__dirname, 'tests', e))));
|
||||
const existing = await fs.readdir(path.join(__dirname, "tests"));
|
||||
await Promise.all(
|
||||
existing.map((e) => fs.unlink(path.join(__dirname, "tests", e)))
|
||||
);
|
||||
|
||||
// Format after fetching as formatting is synchronous and can take so long that connections get dropped by server due to inactivity.
|
||||
for (const [name, html] of await Promise.all(Object.entries(tests).map(([name, url]) => fetchTest(name, url)))) {
|
||||
for (const [name, html] of await Promise.all(
|
||||
Object.entries(tests).map(([name, url]) => fetchTest(name, url))
|
||||
)) {
|
||||
// Apply some fixes to HTML.
|
||||
const fixed = html
|
||||
// Fix early termination of conditional comment in Amazon.
|
||||
.replace('--></style>\n<![endif]-->', '</style>\n<![endif]-->')
|
||||
.replace("--></style>\n<![endif]-->", "</style>\n<![endif]-->")
|
||||
// Fix closing of void tag in Amazon.
|
||||
.replace(/><\/hr>/g, '/>')
|
||||
.replace(/><\/hr>/g, "/>")
|
||||
// Fix extra '</div>' in BBC.
|
||||
.replace('</a></span></small></div></div></div></footer>', '</a></span></small></div></div></footer>')
|
||||
.replace(
|
||||
"</a></span></small></div></div></div></footer>",
|
||||
"</a></span></small></div></div></footer>"
|
||||
)
|
||||
// Fix broken attribute value in Stack Overflow.
|
||||
.replace('height=151"', 'height="151"')
|
||||
;
|
||||
await fs.writeFile(path.join(__dirname, 'tests', name), fixed);
|
||||
.replace('height=151"', 'height="151"');
|
||||
await fs.writeFile(path.join(__dirname, "tests", name), fixed);
|
||||
}
|
||||
})()
|
||||
.catch(console.error);
|
||||
})().catch(console.error);
|
||||
|
|
224
bench/graph.js
224
bench/graph.js
|
@ -1,30 +1,30 @@
|
|||
const results = require('./results');
|
||||
const https = require('https');
|
||||
const results = require("./results");
|
||||
const https = require("https");
|
||||
|
||||
const colours = {
|
||||
'minify-html': '#041f60',
|
||||
'@minify-html/js': '#1f77b4',
|
||||
'minimize': '#ff7f0e',
|
||||
'html-minifier': '#2ca02c',
|
||||
"minify-html": "#041f60",
|
||||
"@minify-html/js": "#1f77b4",
|
||||
minimize: "#ff7f0e",
|
||||
"html-minifier": "#2ca02c",
|
||||
};
|
||||
|
||||
const COLOUR_SPEED_PRIMARY = '#2e61bd';
|
||||
const COLOUR_SPEED_SECONDARY = 'rgb(188, 188, 188)';
|
||||
const COLOUR_SIZE_PRIMARY = '#64acce';
|
||||
const COLOUR_SIZE_SECONDARY = 'rgb(224, 224, 224)';
|
||||
const COLOUR_SPEED_PRIMARY = "#2e61bd";
|
||||
const COLOUR_SPEED_SECONDARY = "rgb(188, 188, 188)";
|
||||
const COLOUR_SIZE_PRIMARY = "#64acce";
|
||||
const COLOUR_SIZE_SECONDARY = "rgb(224, 224, 224)";
|
||||
|
||||
const breakdownChartOptions = (title) => ({
|
||||
options: {
|
||||
legend: {
|
||||
display: true,
|
||||
labels: {
|
||||
fontColor: '#000',
|
||||
fontColor: "#000",
|
||||
},
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: title,
|
||||
fontColor: '#333',
|
||||
fontColor: "#333",
|
||||
fontSize: 24,
|
||||
},
|
||||
scales: {
|
||||
|
@ -32,10 +32,10 @@ const breakdownChartOptions = (title) => ({
|
|||
{
|
||||
barPercentage: 0.25,
|
||||
gridLines: {
|
||||
color: '#e2e2e2',
|
||||
color: "#e2e2e2",
|
||||
},
|
||||
ticks: {
|
||||
fontColor: '#666',
|
||||
fontColor: "#666",
|
||||
fontSize: 20,
|
||||
},
|
||||
},
|
||||
|
@ -43,10 +43,10 @@ const breakdownChartOptions = (title) => ({
|
|||
yAxes: [
|
||||
{
|
||||
gridLines: {
|
||||
color: '#ccc',
|
||||
color: "#ccc",
|
||||
},
|
||||
ticks: {
|
||||
fontColor: '#666',
|
||||
fontColor: "#666",
|
||||
fontSize: 20,
|
||||
},
|
||||
},
|
||||
|
@ -59,7 +59,7 @@ const axisLabel = (fontColor, labelString) => ({
|
|||
display: true,
|
||||
fontColor,
|
||||
fontSize: 24,
|
||||
fontStyle: 'bold',
|
||||
fontStyle: "bold",
|
||||
labelString,
|
||||
padding: 16,
|
||||
});
|
||||
|
@ -76,31 +76,31 @@ const combinedChartOptions = () => ({
|
|||
display: false,
|
||||
},
|
||||
ticks: {
|
||||
fontColor: '#555',
|
||||
fontColor: "#555",
|
||||
fontSize: 24,
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
id: 'y1',
|
||||
type: 'linear',
|
||||
scaleLabel: axisLabel(COLOUR_SPEED_PRIMARY, 'Performance'),
|
||||
position: 'left',
|
||||
id: "y1",
|
||||
type: "linear",
|
||||
scaleLabel: axisLabel(COLOUR_SPEED_PRIMARY, "Performance"),
|
||||
position: "left",
|
||||
ticks: {
|
||||
callback: "$$$_____REPLACE_WITH_TICK_CALLBACK_____$$$",
|
||||
fontColor: COLOUR_SPEED_PRIMARY,
|
||||
fontSize: 24,
|
||||
},
|
||||
gridLines: {
|
||||
color: '#eee',
|
||||
color: "#eee",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'y2',
|
||||
type: 'linear',
|
||||
scaleLabel: axisLabel(COLOUR_SIZE_PRIMARY, 'Average size reduction'),
|
||||
position: 'right',
|
||||
id: "y2",
|
||||
type: "linear",
|
||||
scaleLabel: axisLabel(COLOUR_SIZE_PRIMARY, "Average size reduction"),
|
||||
position: "right",
|
||||
ticks: {
|
||||
callback: "$$$_____REPLACE_WITH_TICK_CALLBACK_____$$$",
|
||||
fontColor: COLOUR_SIZE_PRIMARY,
|
||||
|
@ -108,90 +108,116 @@ const combinedChartOptions = () => ({
|
|||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const renderChart = (cfg) => new Promise((resolve, reject) => {
|
||||
const req = https.request('https://quickchart.io/chart', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
const renderChart = (cfg) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const req = https.request("https://quickchart.io/chart", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
req.on("error", reject);
|
||||
req.on("response", (res) => {
|
||||
const err = res.headers["x-quickchart-error"];
|
||||
if (res.statusCode < 200 || res.statusCode > 299 || err) {
|
||||
return reject(new Error(err || `Status ${res.statusCode}`));
|
||||
}
|
||||
const chunks = [];
|
||||
res.on("error", reject);
|
||||
res.on("data", (c) => chunks.push(c));
|
||||
res.on("end", () => resolve(Buffer.concat(chunks)));
|
||||
});
|
||||
req.end(
|
||||
JSON.stringify({
|
||||
backgroundColor: "white",
|
||||
chart: JSON.stringify(cfg).replaceAll(
|
||||
'"$$$_____REPLACE_WITH_TICK_CALLBACK_____$$$"',
|
||||
"function(value) {return Math.round(value * 10000) / 100 + '%';}"
|
||||
),
|
||||
width: 1333,
|
||||
height: 768,
|
||||
format: "png",
|
||||
})
|
||||
);
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.on('response', res => {
|
||||
const err = res.headers['x-quickchart-error'];
|
||||
if (res.statusCode < 200 || res.statusCode > 299 || err) {
|
||||
return reject(new Error(err || `Status ${res.statusCode}`));
|
||||
}
|
||||
const chunks = [];
|
||||
res.on('error', reject);
|
||||
res.on('data', c => chunks.push(c));
|
||||
res.on('end', () => resolve(Buffer.concat(chunks)));
|
||||
});
|
||||
req.end(JSON.stringify({
|
||||
backgroundColor: 'white',
|
||||
chart: JSON.stringify(cfg).replaceAll('"$$$_____REPLACE_WITH_TICK_CALLBACK_____$$$"', "function(value) {return Math.round(value * 10000) / 100 + '%';}"),
|
||||
width: 1333,
|
||||
height: 768,
|
||||
format: 'png',
|
||||
}));
|
||||
});
|
||||
|
||||
(async () => {
|
||||
const averageSpeeds = results.getSpeedResults().getAverageRelativeSpeedPerMinifier('@minify-html/js');
|
||||
const averageSizes = results.getSizeResults().getAverageRelativeSizePerMinifier();
|
||||
const averageSpeeds = results
|
||||
.getSpeedResults()
|
||||
.getAverageRelativeSpeedPerMinifier("@minify-html/js");
|
||||
const averageSizes = results
|
||||
.getSizeResults()
|
||||
.getAverageRelativeSizePerMinifier();
|
||||
const averageLabels = ["minimize", "html-minifier", "@minify-html/js"];
|
||||
|
||||
results.writeAverageCombinedGraph(await renderChart({
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: averageLabels,
|
||||
datasets: [
|
||||
{
|
||||
yAxisID: 'y1',
|
||||
backgroundColor: averageLabels.map((n) => n === "@minify-html/js" ? COLOUR_SPEED_PRIMARY : COLOUR_SPEED_SECONDARY),
|
||||
data: averageLabels.map((n) => averageSpeeds.get(n)),
|
||||
},
|
||||
{
|
||||
yAxisID: 'y2',
|
||||
backgroundColor: averageLabels.map((n) => n === "@minify-html/js" ? COLOUR_SIZE_PRIMARY : COLOUR_SIZE_SECONDARY),
|
||||
data: averageLabels.map((n) => 1 - averageSizes.get(n)),
|
||||
},
|
||||
],
|
||||
},
|
||||
...combinedChartOptions(),
|
||||
}));
|
||||
results.writeAverageCombinedGraph(
|
||||
await renderChart({
|
||||
type: "bar",
|
||||
data: {
|
||||
labels: averageLabels,
|
||||
datasets: [
|
||||
{
|
||||
yAxisID: "y1",
|
||||
backgroundColor: averageLabels.map((n) =>
|
||||
n === "@minify-html/js"
|
||||
? COLOUR_SPEED_PRIMARY
|
||||
: COLOUR_SPEED_SECONDARY
|
||||
),
|
||||
data: averageLabels.map((n) => averageSpeeds.get(n)),
|
||||
},
|
||||
{
|
||||
yAxisID: "y2",
|
||||
backgroundColor: averageLabels.map((n) =>
|
||||
n === "@minify-html/js"
|
||||
? COLOUR_SIZE_PRIMARY
|
||||
: COLOUR_SIZE_SECONDARY
|
||||
),
|
||||
data: averageLabels.map((n) => 1 - averageSizes.get(n)),
|
||||
},
|
||||
],
|
||||
},
|
||||
...combinedChartOptions(),
|
||||
})
|
||||
);
|
||||
|
||||
const speeds = results.getSpeedResults().getRelativeFileSpeedsPerMinifier('@minify-html/js');
|
||||
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),
|
||||
})),
|
||||
},
|
||||
...breakdownChartOptions('Operations per second (higher is better)'),
|
||||
}));
|
||||
const speeds = results
|
||||
.getSpeedResults()
|
||||
.getRelativeFileSpeedsPerMinifier("@minify-html/js");
|
||||
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),
|
||||
})),
|
||||
},
|
||||
...breakdownChartOptions("Operations per second (higher is better)"),
|
||||
})
|
||||
);
|
||||
|
||||
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),
|
||||
})),
|
||||
},
|
||||
...breakdownChartOptions('Minified size (lower is better)'),
|
||||
}));
|
||||
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),
|
||||
})),
|
||||
},
|
||||
...breakdownChartOptions("Minified size (lower is better)"),
|
||||
})
|
||||
);
|
||||
})();
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
const esbuild = require('esbuild');
|
||||
const htmlMinifier = require('html-minifier');
|
||||
const minifyHtml = require('@minify-html/js');
|
||||
const minimize = require('minimize');
|
||||
|
||||
const testJsAndCssMinification = process.env.HTML_ONLY !== '1';
|
||||
|
||||
const jsMime = new Set([
|
||||
undefined,
|
||||
'application/ecmascript',
|
||||
'application/javascript',
|
||||
'application/x-ecmascript',
|
||||
'application/x-javascript',
|
||||
'text/ecmascript',
|
||||
'text/javascript',
|
||||
'text/javascript1.0',
|
||||
'text/javascript1.1',
|
||||
'text/javascript1.2',
|
||||
'text/javascript1.3',
|
||||
'text/javascript1.4',
|
||||
'text/javascript1.5',
|
||||
'text/jscript',
|
||||
'text/livescript',
|
||||
'text/x-ecmascript',
|
||||
'text/x-javascript',
|
||||
]);
|
||||
|
||||
const minifyHtmlCfg = minifyHtml.createConfiguration({
|
||||
minify_js: testJsAndCssMinification,
|
||||
minify_css: testJsAndCssMinification,
|
||||
});
|
||||
|
||||
const esbuildCss = code => esbuild.transformSync(code, {
|
||||
loader: "css",
|
||||
minify: true,
|
||||
}).code;
|
||||
const esbuildJs = code => esbuild.transformSync(code, {
|
||||
loader: "js",
|
||||
minify: true,
|
||||
}).code.replace(/<\/script/g, "<\\/script");
|
||||
|
||||
const htmlMinifierCfg = {
|
||||
collapseBooleanAttributes: true,
|
||||
collapseInlineTagWhitespace: true,
|
||||
collapseWhitespace: true,
|
||||
// minify-html can do context-aware whitespace removal, which is safe when configured correctly to match how whitespace is used in the document.
|
||||
// html-minifier cannot, so whitespace must be collapsed conservatively.
|
||||
// Alternatively, minify-html can also be made to remove whitespace regardless of context.
|
||||
conservativeCollapse: true,
|
||||
customEventAttributes: [],
|
||||
decodeEntities: true,
|
||||
ignoreCustomComments: [],
|
||||
ignoreCustomFragments: [/<\?[\s\S]*?\?>/],
|
||||
minifyCSS: testJsAndCssMinification && esbuildCss,
|
||||
minifyJS: testJsAndCssMinification && esbuildJs,
|
||||
processConditionalComments: true,
|
||||
removeAttributeQuotes: true,
|
||||
removeComments: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeOptionalTags: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
removeTagWhitespace: true,
|
||||
useShortDoctype: true,
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
'@minify-html/js': (_, buffer) => minifyHtml.minify(buffer, minifyHtmlCfg),
|
||||
'html-minifier': content => htmlMinifier.minify(content, htmlMinifierCfg),
|
||||
'minimize': testJsAndCssMinification
|
||||
? (content) => new minimize({
|
||||
plugins: [
|
||||
{
|
||||
id: 'esbuild',
|
||||
element: (node, next) => {
|
||||
if (node.type === 'text' && node.parent) {
|
||||
if (node.parent.type === 'script' && jsMime.has(node.parent.attribs.type)) {
|
||||
node.data = esbuildJs(node.data);
|
||||
} else if (node.parent.type === 'style') {
|
||||
node.data = esbuildCss(node.data);
|
||||
}
|
||||
}
|
||||
next();
|
||||
},
|
||||
},
|
||||
],
|
||||
}).parse(content)
|
||||
: content => new minimize().parse(content),
|
||||
};
|
|
@ -1,34 +0,0 @@
|
|||
use minify_html::{Cfg, minify};
|
||||
use std::fs;
|
||||
use std::io::{stdout};
|
||||
use std::time::Instant;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(StructOpt)]
|
||||
struct Args {
|
||||
#[structopt(long, parse(from_os_str))]
|
||||
tests: std::path::PathBuf,
|
||||
#[structopt(long)]
|
||||
iterations: usize,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::from_args();
|
||||
let tests = fs::read_dir(args.tests).unwrap().map(|d| d.unwrap());
|
||||
|
||||
let mut results: Vec<(String, f64)> = Vec::new();
|
||||
|
||||
for t in tests {
|
||||
let source = fs::read(t.path()).unwrap();
|
||||
let start = Instant::now();
|
||||
for _ in 0..args.iterations {
|
||||
let data = source.to_vec();
|
||||
minify(&data, &Cfg::new());
|
||||
};
|
||||
let elapsed = start.elapsed().as_secs_f64();
|
||||
let ops = args.iterations as f64 / elapsed;
|
||||
results.push((t.file_name().to_str().unwrap().to_string(), ops));
|
||||
};
|
||||
|
||||
serde_json::to_writer(stdout(), &results).unwrap();
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@minify-html/js": "file:../nodejs",
|
||||
"benchmark": "2.1.4",
|
||||
"esbuild": "^0.12.18",
|
||||
"html-minifier": "4.0.0",
|
||||
"minimize": "2.2.0",
|
||||
"minimist": "^1.2.5",
|
||||
"request": "^2.88.2",
|
||||
"request-promise-native": "^1.0.9"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node bench.js"
|
||||
}
|
||||
}
|
|
@ -1,21 +1,21 @@
|
|||
const minifiers = require('./minifiers');
|
||||
const tests = require('./tests');
|
||||
const {join} = require('path');
|
||||
const {mkdirSync, readFileSync, writeFileSync} = require('fs');
|
||||
const minifiers = require("./minifiers");
|
||||
const tests = require("./tests");
|
||||
const { join } = require("path");
|
||||
const { mkdirSync, readFileSync, writeFileSync } = require("fs");
|
||||
|
||||
const RESULTS_DIR = join(__dirname, 'results');
|
||||
const SPEEDS_JSON = join(RESULTS_DIR, 'speeds.json');
|
||||
const SPEEDS_GRAPH = join(RESULTS_DIR, 'speeds.png');
|
||||
const AVERAGE_COMBINED_GRAPH = join(RESULTS_DIR, 'average-combined.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 RESULTS_DIR = join(__dirname, "results");
|
||||
const SPEEDS_JSON = join(RESULTS_DIR, "speeds.json");
|
||||
const SPEEDS_GRAPH = join(RESULTS_DIR, "speeds.png");
|
||||
const AVERAGE_COMBINED_GRAPH = join(RESULTS_DIR, "average-combined.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);
|
||||
const testNames = tests.map((t) => t.name);
|
||||
|
||||
mkdirSync(RESULTS_DIR, {recursive: true});
|
||||
mkdirSync(RESULTS_DIR, { recursive: true });
|
||||
|
||||
module.exports = {
|
||||
writeSpeedResults(speeds) {
|
||||
|
@ -40,50 +40,58 @@ module.exports = {
|
|||
writeFileSync(SIZES_GRAPH, data);
|
||||
},
|
||||
getSpeedResults() {
|
||||
const data = JSON.parse(readFileSync(SPEEDS_JSON, 'utf8'));
|
||||
const data = JSON.parse(readFileSync(SPEEDS_JSON, "utf8"));
|
||||
|
||||
return {
|
||||
// Get minifier-speed pairs.
|
||||
getAverageRelativeSpeedPerMinifier(baselineMinifier) {
|
||||
return new Map(minifierNames.map(minifier => [
|
||||
minifier,
|
||||
testNames
|
||||
// Get operations per second for each test.
|
||||
.map(test => data[test][minifier] / data[test][baselineMinifier])
|
||||
// Sum all test operations per second.
|
||||
.reduce((sum, c) => sum + c)
|
||||
// Divide by tests count to get average operations per second.
|
||||
/ testNames.length,
|
||||
]));
|
||||
return new Map(
|
||||
minifierNames.map((minifier) => [
|
||||
minifier,
|
||||
testNames
|
||||
// Get operations per second for each test.
|
||||
.map(
|
||||
(test) => data[test][minifier] / data[test][baselineMinifier]
|
||||
)
|
||||
// Sum all test operations per second.
|
||||
.reduce((sum, c) => sum + c) /
|
||||
// Divide by tests count to get average operations per second.
|
||||
testNames.length,
|
||||
])
|
||||
);
|
||||
},
|
||||
// Get minifier-speeds pairs.
|
||||
getRelativeFileSpeedsPerMinifier(baselineMinifier) {
|
||||
return minifierNames.map(minifier => [
|
||||
return minifierNames.map((minifier) => [
|
||||
minifier,
|
||||
testNames.map(test => [test, data[test][minifier] / data[test][baselineMinifier]]),
|
||||
testNames.map((test) => [
|
||||
test,
|
||||
data[test][minifier] / data[test][baselineMinifier],
|
||||
]),
|
||||
]);
|
||||
},
|
||||
};
|
||||
},
|
||||
getSizeResults() {
|
||||
const data = JSON.parse(readFileSync(SIZES_JSON, 'utf8'));
|
||||
const data = JSON.parse(readFileSync(SIZES_JSON, "utf8"));
|
||||
|
||||
return {
|
||||
// Get minifier-size pairs.
|
||||
getAverageRelativeSizePerMinifier() {
|
||||
return new Map(minifierNames.map(minifier => [
|
||||
minifier,
|
||||
testNames
|
||||
.map(test => data[test][minifier].relative)
|
||||
.reduce((sum, c) => sum + c)
|
||||
/ testNames.length,
|
||||
]));
|
||||
return new Map(
|
||||
minifierNames.map((minifier) => [
|
||||
minifier,
|
||||
testNames
|
||||
.map((test) => data[test][minifier].relative)
|
||||
.reduce((sum, c) => sum + c) / testNames.length,
|
||||
])
|
||||
);
|
||||
},
|
||||
// Get minifier-sizes pairs.
|
||||
getRelativeFileSizesPerMinifier() {
|
||||
return minifierNames.map(minifier => [
|
||||
return minifierNames.map((minifier) => [
|
||||
minifier,
|
||||
testNames.map(test => [test, data[test][minifier].relative]),
|
||||
testNames.map((test) => [test, data[test][minifier].relative]),
|
||||
]);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
# Benchmark runners
|
||||
|
||||
- Each directory should have an executable `./build` and `./run`.
|
||||
- The runners should use the following environment variables:
|
||||
- `MHB_ITERATIONS`: times to run each input.
|
||||
- `MHB_INPUT_DIR`: path to directory containing inputs. Files should be read from this directory and used as the inputs.
|
||||
- `MHB_HTML_ONLY`: if set to `1`, `minify_css` and `minify_js` should be disabled.
|
||||
- The output should be a JSON array of pairs, where each pair represents the input name and execution time in seconds (as a floating point value).
|
||||
- The execution time should be measured using high-precision monotonic system clocks where possible.
|
|
@ -0,0 +1,2 @@
|
|||
/package-lock.json
|
||||
node_modules/
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuxo pipefail
|
||||
|
||||
npm i
|
|
@ -0,0 +1,57 @@
|
|||
const esbuild = require("esbuild");
|
||||
const fs = require("fs");
|
||||
const htmlMinifier = require("html-minifier");
|
||||
const path = require("path");
|
||||
|
||||
const iterations = parseInt(process.env.MHB_ITERATIONS, 10);
|
||||
const inputDir = process.env.MHB_INPUT_DIR;
|
||||
const htmlOnly = process.env.MHB_HTML_ONLY === "1";
|
||||
|
||||
const htmlMinifierCfg = {
|
||||
collapseBooleanAttributes: true,
|
||||
collapseInlineTagWhitespace: true,
|
||||
collapseWhitespace: true,
|
||||
// minify-html can do context-aware whitespace removal, which is safe when configured correctly to match how whitespace is used in the document.
|
||||
// html-minifier cannot, so whitespace must be collapsed conservatively.
|
||||
// Alternatively, minify-html can also be made to remove whitespace regardless of context.
|
||||
conservativeCollapse: true,
|
||||
customEventAttributes: [],
|
||||
decodeEntities: true,
|
||||
ignoreCustomComments: [],
|
||||
ignoreCustomFragments: [/<\?[\s\S]*?\?>/],
|
||||
minifyCSS: !htmlOnly && esbuildCss,
|
||||
minifyJS: !htmlOnly && esbuildJs,
|
||||
processConditionalComments: true,
|
||||
removeAttributeQuotes: true,
|
||||
removeComments: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeOptionalTags: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
removeTagWhitespace: true,
|
||||
useShortDoctype: true,
|
||||
};
|
||||
|
||||
const esbuildCss = (code) =>
|
||||
esbuild.transformSync(code, {
|
||||
loader: "css",
|
||||
minify: true,
|
||||
}).code;
|
||||
|
||||
const esbuildJs = (code) =>
|
||||
esbuild.transformSync(code, {
|
||||
loader: "js",
|
||||
minify: true,
|
||||
}).code;
|
||||
|
||||
const results = fs.readdirSync(inputDir).map((name) => {
|
||||
const src = fs.readFileSync(path.join(inputDir, name), "utf8");
|
||||
const start = process.hrtime.bigint();
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
htmlMinifier.minify(src, htmlMinifierCfg);
|
||||
}
|
||||
const elapsed = process.hrtime.bigint() - start;
|
||||
return [name, Number(elapsed) / 1_000_000_000];
|
||||
});
|
||||
console.log(JSON.stringify(results));
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"esbuild": "0.12.19",
|
||||
"html-minifier": "4.0.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuxo pipefail
|
||||
|
||||
node index.js
|
|
@ -0,0 +1,2 @@
|
|||
/package-lock.json
|
||||
node_modules/
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuxo pipefail
|
||||
|
||||
pushd ../../../nodejs
|
||||
npm run build
|
||||
popd
|
||||
|
||||
npm i
|
|
@ -0,0 +1,23 @@
|
|||
const fs = require("fs");
|
||||
const minifyHtml = require("@minify-html/js");
|
||||
const path = require("path");
|
||||
|
||||
const iterations = parseInt(process.env.MHB_ITERATIONS, 10);
|
||||
const inputDir = process.env.MHB_INPUT_DIR;
|
||||
const htmlOnly = process.env.MHB_HTML_ONLY === "1";
|
||||
|
||||
const minifyHtmlCfg = minifyHtml.createConfiguration({
|
||||
minify_css: !htmlOnly,
|
||||
minify_js: !htmlOnly,
|
||||
});
|
||||
|
||||
const results = fs.readdirSync(inputDir).map((name) => {
|
||||
const src = fs.readFileSync(path.join(inputDir, name));
|
||||
const start = process.hrtime.bigint();
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
minifyHtml.minify(src, minifyHtmlCfg);
|
||||
}
|
||||
const elapsed = process.hrtime.bigint() - start;
|
||||
return [name, Number(elapsed) / 1_000_000_000];
|
||||
});
|
||||
console.log(JSON.stringify(results));
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@minify-html/js": "file:../../../nodejs"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuxo pipefail
|
||||
|
||||
node index.js
|
|
@ -0,0 +1,2 @@
|
|||
/Cargo.lock
|
||||
/target/
|
|
@ -6,8 +6,7 @@ authors = ["Wilson Lin <code@wilsonl.in>"]
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
minify-html = { path = "../../rust/main" }
|
||||
structopt = "0.3.5"
|
||||
minify-html = { path = "../../../rust/main" }
|
||||
serde = { version = "1.0.104", features = ["derive"] }
|
||||
serde_json = "1.0.44"
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuxo pipefail
|
||||
|
||||
cargo build --release
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuxo pipefail
|
||||
|
||||
cargo run --release
|
|
@ -0,0 +1,33 @@
|
|||
use std::{env, fs};
|
||||
use std::io::stdout;
|
||||
use std::time::Instant;
|
||||
|
||||
use minify_html::{Cfg, minify};
|
||||
|
||||
fn main() {
|
||||
let iterations = env::var("MHB_ITERATIONS").unwrap().parse::<usize>().unwrap();
|
||||
let input_dir = env::var("MHB_INPUT_DIR").unwrap();
|
||||
let html_only = env::var("MHB_HTML_ONLY").unwrap() == "1";
|
||||
|
||||
let tests = fs::read_dir(input_dir).unwrap().map(|d| d.unwrap());
|
||||
|
||||
let mut results: Vec<(String, f64)> = Vec::new();
|
||||
let mut cfg = Cfg::new();
|
||||
if !html_only {
|
||||
cfg.minify_css = true;
|
||||
cfg.minify_js = true;
|
||||
};
|
||||
|
||||
for t in tests {
|
||||
let source = fs::read(t.path()).unwrap();
|
||||
let start = Instant::now();
|
||||
for _ in 0..iterations {
|
||||
let data = source.to_vec();
|
||||
minify(&source, &cfg);
|
||||
};
|
||||
let elapsed = start.elapsed().as_secs_f64();
|
||||
results.push((t.file_name().into_string().unwrap(), elapsed));
|
||||
};
|
||||
|
||||
serde_json::to_writer(stdout(), &results).unwrap();
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
/Cargo.lock
|
||||
/target/
|
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "minify-html-onepass-bench"
|
||||
publish = false
|
||||
version = "0.0.1"
|
||||
authors = ["Wilson Lin <code@wilsonl.in>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
minify-html-onepass = { path = "../../../rust/onepass" }
|
||||
serde = { version = "1.0.104", features = ["derive"] }
|
||||
serde_json = "1.0.44"
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuxo pipefail
|
||||
|
||||
cargo build --release
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuxo pipefail
|
||||
|
||||
cargo run --release
|
|
@ -0,0 +1,32 @@
|
|||
use std::{env, fs};
|
||||
use std::io::stdout;
|
||||
use std::time::Instant;
|
||||
|
||||
use minify_html_onepass::{Cfg, in_place};
|
||||
|
||||
fn main() {
|
||||
let iterations = env::var("MHB_ITERATIONS").unwrap().parse::<usize>().unwrap();
|
||||
let input_dir = env::var("MHB_INPUT_DIR").unwrap();
|
||||
let html_only = env::var("MHB_HTML_ONLY").unwrap() == "1";
|
||||
|
||||
let tests = fs::read_dir(input_dir).unwrap().map(|d| d.unwrap());
|
||||
|
||||
let mut results: Vec<(String, f64)> = Vec::new();
|
||||
let mut cfg = Cfg {
|
||||
minify_css: !html_only,
|
||||
minify_js: !html_only,
|
||||
};
|
||||
|
||||
for t in tests {
|
||||
let source = fs::read(t.path()).unwrap();
|
||||
let start = Instant::now();
|
||||
for _ in 0..iterations {
|
||||
let mut data = source.to_vec();
|
||||
let _ = in_place(&mut data, &cfg).unwrap();
|
||||
};
|
||||
let elapsed = start.elapsed().as_secs_f64();
|
||||
results.push((t.file_name().into_string().unwrap(), elapsed));
|
||||
};
|
||||
|
||||
serde_json::to_writer(stdout(), &results).unwrap();
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
/package-lock.json
|
||||
node_modules/
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuxo pipefail
|
||||
|
||||
npm i
|
|
@ -0,0 +1,70 @@
|
|||
const esbuild = require("esbuild");
|
||||
const fs = require("fs");
|
||||
const minimize = require("minimize");
|
||||
const path = require("path");
|
||||
|
||||
const iterations = parseInt(process.env.MHB_ITERATIONS, 10);
|
||||
const inputDir = process.env.MHB_INPUT_DIR;
|
||||
const htmlOnly = process.env.MHB_HTML_ONLY === "1";
|
||||
|
||||
const jsMime = new Set([
|
||||
undefined,
|
||||
"application/ecmascript",
|
||||
"application/javascript",
|
||||
"application/x-ecmascript",
|
||||
"application/x-javascript",
|
||||
"text/ecmascript",
|
||||
"text/javascript",
|
||||
"text/javascript1.0",
|
||||
"text/javascript1.1",
|
||||
"text/javascript1.2",
|
||||
"text/javascript1.3",
|
||||
"text/javascript1.4",
|
||||
"text/javascript1.5",
|
||||
"text/jscript",
|
||||
"text/livescript",
|
||||
"text/x-ecmascript",
|
||||
"text/x-javascript",
|
||||
]);
|
||||
|
||||
const esbuildCss = (code) =>
|
||||
esbuild.transformSync(code, {
|
||||
loader: "css",
|
||||
minify: true,
|
||||
}).code;
|
||||
|
||||
const esbuildJs = (code) =>
|
||||
esbuild.transformSync(code, {
|
||||
loader: "js",
|
||||
minify: true,
|
||||
}).code;
|
||||
|
||||
const jsCssPlugin = {
|
||||
id: "esbuild",
|
||||
element: (node, next) => {
|
||||
if (node.type === "text" && node.parent) {
|
||||
if (
|
||||
node.parent.type === "script" &&
|
||||
jsMime.has(node.parent.attribs.type)
|
||||
) {
|
||||
node.data = esbuildJs(node.data);
|
||||
} else if (node.parent.type === "style") {
|
||||
node.data = esbuildCss(node.data);
|
||||
}
|
||||
}
|
||||
next();
|
||||
},
|
||||
};
|
||||
|
||||
const plugins = htmlOnly ? [] : [jsCssPlugin];
|
||||
|
||||
const results = fs.readdirSync(inputDir).map((name) => {
|
||||
const src = fs.readFileSync(path.join(inputDir, name), "utf8");
|
||||
const start = process.hrtime.bigint();
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
new minimize({ plugins }).parse(src);
|
||||
}
|
||||
const elapsed = process.hrtime.bigint() - start;
|
||||
return [name, Number(elapsed) / 1_000_000_000];
|
||||
});
|
||||
console.log(JSON.stringify(results));
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"esbuild": "0.12.19",
|
||||
"minimize": "2.2.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuxo pipefail
|
||||
|
||||
node index.js
|
|
@ -1,15 +1,15 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const minifiers = require('./minifiers');
|
||||
const results = require('./results');
|
||||
const tests = require('./tests');
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const minifiers = require("./minifiers");
|
||||
const results = require("./results");
|
||||
const tests = require("./tests");
|
||||
|
||||
const sizes = {};
|
||||
const setSize = (program, test, result) => {
|
||||
if (!sizes[test]) {
|
||||
sizes[test] = {
|
||||
original: {
|
||||
absolute: tests.find(t => t.name === test).contentAsString.length,
|
||||
absolute: tests.find((t) => t.name === test).contentAsString.length,
|
||||
relative: 1,
|
||||
},
|
||||
};
|
||||
|
@ -28,8 +28,8 @@ const setSize = (program, test, result) => {
|
|||
const min = await minifiers[m](t.contentAsString, t.contentAsBuffer);
|
||||
// If `min` is a Buffer, convert to string (interpret as UTF-8) to get canonical length.
|
||||
setSize(m, t.name, min.toString().length);
|
||||
const minPath = path.join(__dirname, 'min', m, `${t.name}.html`);
|
||||
fs.mkdirSync(path.dirname(minPath), {recursive: true});
|
||||
const minPath = path.join(__dirname, "min", m, `${t.name}.html`);
|
||||
fs.mkdirSync(path.dirname(minPath), { recursive: true });
|
||||
fs.writeFileSync(minPath, min);
|
||||
} catch (err) {
|
||||
console.error(`Failed to run ${m} on test ${t.name}:`);
|
||||
|
|
|
@ -1,23 +1,27 @@
|
|||
const benchmark = require('benchmark');
|
||||
const childProcess = require('child_process');
|
||||
const minimist = require('minimist');
|
||||
const path = require('path');
|
||||
const minifiers = require('./minifiers');
|
||||
const results = require('./results');
|
||||
const tests = require('./tests');
|
||||
const benchmark = require("benchmark");
|
||||
const childProcess = require("child_process");
|
||||
const minimist = require("minimist");
|
||||
const path = require("path");
|
||||
const minifiers = require("./minifiers");
|
||||
const results = require("./results");
|
||||
const tests = require("./tests");
|
||||
|
||||
const args = minimist(process.argv.slice(2));
|
||||
const shouldRunRust = !!args.rust;
|
||||
|
||||
const cmd = (command, ...args) => {
|
||||
const throwErr = msg => {
|
||||
throw new Error(`${msg}\n ${command} ${args.join(' ')}`);
|
||||
const throwErr = (msg) => {
|
||||
throw new Error(`${msg}\n ${command} ${args.join(" ")}`);
|
||||
};
|
||||
|
||||
const {status, signal, error, stdout, stderr} = childProcess.spawnSync(command, args.map(String), {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
encoding: 'utf8',
|
||||
});
|
||||
const { status, signal, error, stdout, stderr } = childProcess.spawnSync(
|
||||
command,
|
||||
args.map(String),
|
||||
{
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
encoding: "utf8",
|
||||
}
|
||||
);
|
||||
if (error) {
|
||||
throwErr(error.message);
|
||||
}
|
||||
|
@ -33,42 +37,57 @@ const cmd = (command, ...args) => {
|
|||
return stdout;
|
||||
};
|
||||
|
||||
const fromEntries = entries => {
|
||||
const fromEntries = (entries) => {
|
||||
if (Object.fromEntries) return Object.fromEntries(entries);
|
||||
const obj = {};
|
||||
for (const [prop, val] of entries) obj[prop] = val;
|
||||
return obj;
|
||||
};
|
||||
|
||||
const runTest = test => new Promise((resolve, reject) => {
|
||||
// Run JS libraries.
|
||||
const suite = new benchmark.Suite();
|
||||
for (const m of Object.keys(minifiers)) {
|
||||
suite.add(m, {
|
||||
defer: true,
|
||||
fn (deferred) {
|
||||
Promise.resolve(minifiers[m](test.contentAsString, test.contentAsBuffer)).then(() => deferred.resolve());
|
||||
},
|
||||
});
|
||||
}
|
||||
suite
|
||||
.on('cycle', event => console.info(test.name, event.target.toString()))
|
||||
.on('complete', () => resolve(fromEntries(suite.map(b => [b.name, b.hz]))))
|
||||
.on('error', reject)
|
||||
.run({'async': true});
|
||||
});
|
||||
const runTest = (test) =>
|
||||
new Promise((resolve, reject) => {
|
||||
// Run JS libraries.
|
||||
const suite = new benchmark.Suite();
|
||||
for (const m of Object.keys(minifiers)) {
|
||||
suite.add(m, {
|
||||
defer: true,
|
||||
fn(deferred) {
|
||||
Promise.resolve(
|
||||
minifiers[m](test.contentAsString, test.contentAsBuffer)
|
||||
).then(() => deferred.resolve());
|
||||
},
|
||||
});
|
||||
}
|
||||
suite
|
||||
.on("cycle", (event) => console.info(test.name, event.target.toString()))
|
||||
.on("complete", () =>
|
||||
resolve(fromEntries(suite.map((b) => [b.name, b.hz])))
|
||||
)
|
||||
.on("error", reject)
|
||||
.run({ async: true });
|
||||
});
|
||||
|
||||
(async () => {
|
||||
const speeds = fromEntries(tests.map(t => [t.name, {}]));
|
||||
const speeds = fromEntries(tests.map((t) => [t.name, {}]));
|
||||
|
||||
// Run Rust library.
|
||||
if (shouldRunRust) {
|
||||
for (const [testName, testOps] of JSON.parse(cmd(
|
||||
path.join(__dirname, 'minify-html-bench', 'target', 'release', 'minify-html-bench'),
|
||||
'--iterations', 512,
|
||||
'--tests', path.join(__dirname, 'tests'),
|
||||
))) {
|
||||
Object.assign(speeds[testName], {['minify-html']: testOps});
|
||||
for (const [testName, testOps] of JSON.parse(
|
||||
cmd(
|
||||
path.join(
|
||||
__dirname,
|
||||
"minify-html-bench",
|
||||
"target",
|
||||
"release",
|
||||
"minify-html-bench"
|
||||
),
|
||||
"--iterations",
|
||||
512,
|
||||
"--tests",
|
||||
path.join(__dirname, "tests")
|
||||
)
|
||||
)) {
|
||||
Object.assign(speeds[testName], { ["minify-html"]: testOps });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const testsDir = path.join(__dirname, 'tests');
|
||||
module.exports = fs.readdirSync(testsDir).filter(f => !/^\./.test(f)).map(name => ({
|
||||
name,
|
||||
contentAsString: fs.readFileSync(path.join(testsDir, name), 'utf8'),
|
||||
contentAsBuffer: fs.readFileSync(path.join(testsDir, name)),
|
||||
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
@ -0,0 +1 @@
|
|||
/perf.data*
|
Loading…
Reference in New Issue