From 38f186f73e9d41f9bf2a1a523b84f81516cbc1d1 Mon Sep 17 00:00:00 2001 From: Wilson Lin Date: Sun, 8 Aug 2021 23:11:05 +1000 Subject: [PATCH] Complete new bench --- .github/workflows/bench.yaml | 12 +- README.md | 3 +- bench/.gitignore | 1 + bench/graph.js | 218 ++++++++++-------- bench/results.js | 150 +++++------- .../.gitignore | 0 .../build | 0 .../index.js | 5 +- .../package.json | 0 .../run | 0 bench/runners/README.md | 2 +- bench/runners/html-minifier/index.js | 5 +- .../.gitignore | 0 .../Cargo.toml | 0 .../build | 0 .../run | 0 .../src/main.rs | 7 +- .../.gitignore | 0 .../Cargo.toml | 0 .../build | 0 .../{minify-html (Rust) => minify-html}/run | 0 .../src/main.rs | 7 +- bench/runners/minimize/index.js | 5 +- bench/sizes.js | 42 ---- bench/speeds.js | 98 -------- 25 files changed, 204 insertions(+), 351 deletions(-) rename bench/runners/{minify-html (Node.js) => @minify-html%2Fjs}/.gitignore (100%) rename bench/runners/{minify-html (Node.js) => @minify-html%2Fjs}/build (100%) rename bench/runners/{minify-html (Node.js) => @minify-html%2Fjs}/index.js (82%) rename bench/runners/{minify-html (Node.js) => @minify-html%2Fjs}/package.json (100%) rename bench/runners/{minify-html (Node.js) => @minify-html%2Fjs}/run (100%) rename bench/runners/{minify-html (Rust) => minify-html-onepass}/.gitignore (100%) rename bench/runners/{minify-html-onepass (Rust) => minify-html-onepass}/Cargo.toml (100%) rename bench/runners/{minify-html (Rust) => minify-html-onepass}/build (100%) rename bench/runners/{minify-html-onepass (Rust) => minify-html-onepass}/run (100%) rename bench/runners/{minify-html-onepass (Rust) => minify-html-onepass}/src/main.rs (75%) rename bench/runners/{minify-html-onepass (Rust) => minify-html}/.gitignore (100%) rename bench/runners/{minify-html (Rust) => minify-html}/Cargo.toml (100%) rename bench/runners/{minify-html-onepass (Rust) => minify-html}/build (100%) rename bench/runners/{minify-html (Rust) => minify-html}/run (100%) rename bench/runners/{minify-html (Rust) => minify-html}/src/main.rs (77%) delete mode 100644 bench/sizes.js delete mode 100644 bench/speeds.js diff --git a/.github/workflows/bench.yaml b/.github/workflows/bench.yaml index cd0805d..cb85a02 100644 --- a/.github/workflows/bench.yaml +++ b/.github/workflows/bench.yaml @@ -47,9 +47,7 @@ jobs: - name: Build bench working-directory: ./bench run: | - sudo apt install -y build-essential - npm install - ./build.sh + ./build - name: Set up Backblaze B2 CLI uses: wilsonzlin/setup-b2@v3 @@ -58,7 +56,7 @@ jobs: working-directory: ./bench run: | b2 authorize-account ${{ secrets.CICD_CLI_B2_KEY_ID }} ${{ secrets.CICD_CLI_B2_APPLICATION_KEY }} - ./bench.sh - b2 sync ./results/ b2://${{ secrets.CICD_CLI_B2_BUCKET_NAME }}/minify-html/bench/${{ steps.version.outputs.VERSION }}/js/ - HTML_ONLY=1 ./bench.sh - b2 sync ./results/ b2://${{ secrets.CICD_CLI_B2_BUCKET_NAME }}/minify-html/bench/${{ steps.version.outputs.VERSION }}/core/ + ./run + b2 sync ./graphs/ b2://${{ secrets.CICD_CLI_B2_BUCKET_NAME }}/minify-html/bench/${{ steps.version.outputs.VERSION }}/js/ + MHB_HTML_ONLY=1 ./run + b2 sync ./graphs/ b2://${{ secrets.CICD_CLI_B2_BUCKET_NAME }}/minify-html/bench/${{ steps.version.outputs.VERSION }}/core/ diff --git a/README.md b/README.md index 42aa54f..d954878 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,8 @@ 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) -Chart showing speed and compression of HTML minifiers +Chart showing speed of HTML minifiers +Chart showing compression of HTML minifiers Need even faster performance? Check the [one](https://github.com/wilsonzlin/minify-html/tree/one) branch. diff --git a/bench/.gitignore b/bench/.gitignore index 6628455..6bbeaa9 100644 --- a/bench/.gitignore +++ b/bench/.gitignore @@ -1 +1,2 @@ +/graphs/ /results/ diff --git a/bench/graph.js b/bench/graph.js index 84a7f1b..5d73a33 100644 --- a/bench/graph.js +++ b/bench/graph.js @@ -1,17 +1,25 @@ const results = require("./results"); const https = require("https"); +const path = require("path"); +const fs = require("fs/promises"); -const colours = { - "minify-html": "#041f60", - "@minify-html/js": "#1f77b4", - minimize: "#ff7f0e", - "html-minifier": "#2ca02c", +const GRAPHS_DIR = path.join(__dirname, "graphs"); +const SPEEDS_GRAPH = path.join(GRAPHS_DIR, "speeds.png"); +const SIZES_GRAPH = path.join(GRAPHS_DIR, "sizes.png"); +const AVERAGE_SPEEDS_GRAPH = path.join(GRAPHS_DIR, "average-speeds.png"); +const AVERAGE_SIZES_GRAPH = path.join(GRAPHS_DIR, "average-sizes.png"); + +const speedColours = { + "@minify-html/js": "#2e61bd", + "minify-html": "#2e61bd", + "minify-html-onepass": "#222", }; +const defaultSpeedColour = "rgb(188, 188, 188)"; -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 sizeColours = { + "minify-html": "#2e61bd", +}; +const defaultSizeColour = "rgb(188, 188, 188)"; const breakdownChartOptions = (title) => ({ options: { @@ -30,20 +38,21 @@ const breakdownChartOptions = (title) => ({ scales: { xAxes: [ { - barPercentage: 0.25, gridLines: { - color: "#e2e2e2", + color: "#f2f2f2", }, ticks: { - fontColor: "#666", + callback: "$$$_____REPLACE_WITH_TICK_CALLBACK_____$$$", + fontColor: "#999", fontSize: 20, }, }, ], yAxes: [ { + barPercentage: 0.5, gridLines: { - color: "#ccc", + color: "#aaa", }, ticks: { fontColor: "#666", @@ -61,10 +70,10 @@ const axisLabel = (fontColor, labelString) => ({ fontSize: 24, fontStyle: "bold", labelString, - padding: 16, + padding: 12, }); -const combinedChartOptions = () => ({ +const averageChartOptions = (label) => ({ options: { legend: { display: false, @@ -77,45 +86,30 @@ const combinedChartOptions = () => ({ }, ticks: { fontColor: "#555", - fontSize: 24, + fontSize: 16, }, }, ], yAxes: [ { - id: "y1", type: "linear", - scaleLabel: axisLabel(COLOUR_SPEED_PRIMARY, "Performance"), + scaleLabel: axisLabel("#222", label), position: "left", ticks: { callback: "$$$_____REPLACE_WITH_TICK_CALLBACK_____$$$", - fontColor: COLOUR_SPEED_PRIMARY, - fontSize: 24, + fontColor: "#222", + fontSize: 16, }, gridLines: { color: "#eee", }, }, - { - id: "y2", - type: "linear", - scaleLabel: axisLabel(COLOUR_SIZE_PRIMARY, "Average size reduction"), - position: "right", - ticks: { - callback: "$$$_____REPLACE_WITH_TICK_CALLBACK_____$$$", - fontColor: COLOUR_SIZE_PRIMARY, - fontSize: 24, - }, - gridLines: { - display: false, - }, - }, ], }, }, }); -const renderChart = (cfg) => +const renderChart = (cfg, width, height) => new Promise((resolve, reject) => { const req = https.request("https://quickchart.io/chart", { method: "POST", @@ -141,83 +135,109 @@ const renderChart = (cfg) => '"$$$_____REPLACE_WITH_TICK_CALLBACK_____$$$"', "function(value) {return Math.round(value * 10000) / 100 + '%';}" ), - width: 1333, - height: 768, + width, + height, format: "png", }) ); }); (async () => { - const averageSpeeds = results - .getSpeedResults() - .getAverageRelativeSpeedPerMinifier("@minify-html/js"); - const averageSizes = results - .getSizeResults() - .getAverageRelativeSizePerMinifier(); - const averageLabels = ["minimize", "html-minifier", "@minify-html/js"]; + await fs.mkdir(GRAPHS_DIR, { recursive: true }); - 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)), - }, - ], + const res = results.calculate(); + const speedMinifiers = [...res.minifiers].sort( + (a, b) => res.minifierAvgOps[a] - res.minifierAvgOps[b] + ); + const sizeMinifiers = ["minimize", "html-minifier", "minify-html"]; + const inputs = Object.keys(res.inputSizes).sort(); + + await fs.writeFile( + AVERAGE_SPEEDS_GRAPH, + await renderChart( + { + type: "bar", + data: { + labels: speedMinifiers.map(m => m.replace(" (", "\n(")), + datasets: [ + { + backgroundColor: speedMinifiers.map( + (n) => speedColours[n] ?? defaultSpeedColour + ), + data: speedMinifiers.map( + (m) => res.minifierAvgOps[m] / res.maxMinifierAvgOps + ), + }, + ], + }, + ...averageChartOptions("Performance"), }, - ...combinedChartOptions(), - }) + 1024, + 768 + ) ); - 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), - })), + await fs.writeFile( + AVERAGE_SIZES_GRAPH, + await renderChart( + { + type: "bar", + data: { + labels: sizeMinifiers.map(m => m.replace(" (", "\n(")), + datasets: [ + { + backgroundColor: sizeMinifiers.map( + (n) => sizeColours[n] ?? defaultSizeColour + ), + data: sizeMinifiers.map((m) => res.minifierAvgReduction[m]), + }, + ], + }, + ...averageChartOptions("Reduction"), }, - ...breakdownChartOptions("Operations per second (higher is better)"), - }) + 1024, + 768 + ) ); - 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), - })), + await fs.writeFile( + SPEEDS_GRAPH, + await renderChart( + { + type: "horizontalBar", + data: { + labels: inputs, + datasets: speedMinifiers.map((minifier) => ({ + label: minifier, + data: inputs.map( + (input) => + res.perInputOps[minifier][input] / res.maxInputOps[input] + ), + })), + }, + ...breakdownChartOptions("Operations per second (higher is better)"), }, - ...breakdownChartOptions("Minified size (lower is better)"), - }) + 900, + 1000 + ) + ); + + await fs.writeFile( + SIZES_GRAPH, + await renderChart( + { + type: "horizontalBar", + data: { + labels: inputs, + datasets: sizeMinifiers.map((minifier) => ({ + label: minifier, + data: inputs.map((input) => res.perInputReduction[minifier][input]), + })), + }, + ...breakdownChartOptions("Size reduction (higher is better)"), + }, + 900, + 1000 + ) ); })(); diff --git a/bench/results.js b/bench/results.js index 7680812..53c5ce8 100644 --- a/bench/results.js +++ b/bench/results.js @@ -1,99 +1,67 @@ -const minifiers = require("./minifiers"); -const tests = require("./tests"); -const { join } = require("path"); -const { mkdirSync, readFileSync, writeFileSync } = require("fs"); +const fs = require("fs"); +const path = require("path"); -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); - -mkdirSync(RESULTS_DIR, { recursive: true }); +const RESULTS_DIR = path.join(__dirname, "results"); +const INPUTS_DIR = path.join(__dirname, "inputs"); module.exports = { - writeSpeedResults(speeds) { - writeFileSync(SPEEDS_JSON, JSON.stringify(speeds, null, 2)); - }, - writeSizeResults(sizes) { - writeFileSync(SIZES_JSON, JSON.stringify(sizes, null, 2)); - }, - writeAverageCombinedGraph(data) { - writeFileSync(AVERAGE_COMBINED_GRAPH, data); - }, - 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")); + calculate: () => { + // minifier => avg(ops). + const minifierAvgOps = {}; + // minifier => avg(1 - output / original). + const minifierAvgReduction = {}; + let maxMinifierAvgOps = 0; + // minifier => input => ops. + const perInputOps = {}; + // minifier => input => (1 - output / original). + const perInputReduction = {}; + // input => max(ops). + const maxInputOps = {}; + const inputSizes = Object.fromEntries( + fs.readdirSync(INPUTS_DIR).map((f) => { + const name = path.basename(f, ".json"); + const stats = fs.statSync(path.join(INPUTS_DIR, f)); + return [name, stats.size]; + }) + ); + + for (const f of fs.readdirSync(RESULTS_DIR)) { + const minifier = decodeURIComponent(path.basename(f, ".json")); + const data = JSON.parse( + fs.readFileSync(path.join(RESULTS_DIR, f), "utf8") + ); + for (const [input, size, iterations, seconds] of data) { + const originalSize = inputSizes[input]; + const ops = 1 / (seconds / iterations); + const reduction = 1 - size / originalSize; + (minifierAvgOps[minifier] ??= []).push(ops); + (minifierAvgReduction[minifier] ??= []).push(reduction); + (perInputOps[minifier] ??= {})[input] = ops; + (perInputReduction[minifier] ??= {})[input] = reduction; + maxInputOps[input] = Math.max(maxInputOps[input] ?? 0, ops); + } + } + + const minifiers = Object.keys(minifierAvgOps); + for (const m of minifiers) { + minifierAvgOps[m] = + minifierAvgOps[m].reduce((sum, ops) => sum + ops, 0) / + minifierAvgOps[m].length; + maxMinifierAvgOps = Math.max(maxMinifierAvgOps, minifierAvgOps[m]); + minifierAvgReduction[m] = + minifierAvgReduction[m].reduce((sum, ops) => sum + ops, 0) / + minifierAvgReduction[m].length; + } 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, - ]) - ); - }, - // 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. - getAverageRelativeSizePerMinifier() { - 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) => [ - minifier, - testNames.map((test) => [test, data[test][minifier].relative]), - ]); - }, + minifierAvgReduction, + minifierAvgOps, + maxMinifierAvgOps, + perInputOps, + perInputReduction, + maxInputOps, + inputSizes, + minifiers, }; }, }; diff --git a/bench/runners/minify-html (Node.js)/.gitignore b/bench/runners/@minify-html%2Fjs/.gitignore similarity index 100% rename from bench/runners/minify-html (Node.js)/.gitignore rename to bench/runners/@minify-html%2Fjs/.gitignore diff --git a/bench/runners/minify-html (Node.js)/build b/bench/runners/@minify-html%2Fjs/build similarity index 100% rename from bench/runners/minify-html (Node.js)/build rename to bench/runners/@minify-html%2Fjs/build diff --git a/bench/runners/minify-html (Node.js)/index.js b/bench/runners/@minify-html%2Fjs/index.js similarity index 82% rename from bench/runners/minify-html (Node.js)/index.js rename to bench/runners/@minify-html%2Fjs/index.js index b3cee82..c81d99f 100644 --- a/bench/runners/minify-html (Node.js)/index.js +++ b/bench/runners/@minify-html%2Fjs/index.js @@ -14,10 +14,11 @@ const minifyHtmlCfg = minifyHtml.createConfiguration({ const results = fs.readdirSync(inputDir).map((name) => { const src = fs.readFileSync(path.join(inputDir, name)); const start = process.hrtime.bigint(); + let len; for (let i = 0; i < iterations; i++) { - minifyHtml.minify(src, minifyHtmlCfg); + len = minifyHtml.minify(src, minifyHtmlCfg).byteLength; } const elapsed = process.hrtime.bigint() - start; - return [name, Number(elapsed) / 1_000_000_000]; + return [name, len, iterations, Number(elapsed) / 1_000_000_000]; }); console.log(JSON.stringify(results)); diff --git a/bench/runners/minify-html (Node.js)/package.json b/bench/runners/@minify-html%2Fjs/package.json similarity index 100% rename from bench/runners/minify-html (Node.js)/package.json rename to bench/runners/@minify-html%2Fjs/package.json diff --git a/bench/runners/minify-html (Node.js)/run b/bench/runners/@minify-html%2Fjs/run similarity index 100% rename from bench/runners/minify-html (Node.js)/run rename to bench/runners/@minify-html%2Fjs/run diff --git a/bench/runners/README.md b/bench/runners/README.md index 36eb196..75eeb4f 100644 --- a/bench/runners/README.md +++ b/bench/runners/README.md @@ -5,5 +5,5 @@ - `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 output should be a JSON array of tuples, where each tuples contains the input name, output size, iterations, and execution time in seconds (as a floating point value). - The execution time should be measured using high-precision monotonic system clocks where possible. diff --git a/bench/runners/html-minifier/index.js b/bench/runners/html-minifier/index.js index 8fa2be3..9e14a9a 100644 --- a/bench/runners/html-minifier/index.js +++ b/bench/runners/html-minifier/index.js @@ -48,10 +48,11 @@ const htmlMinifierCfg = { const results = fs.readdirSync(inputDir).map((name) => { const src = fs.readFileSync(path.join(inputDir, name), "utf8"); const start = process.hrtime.bigint(); + let len; for (let i = 0; i < iterations; i++) { - htmlMinifier.minify(src, htmlMinifierCfg); + len = htmlMinifier.minify(src, htmlMinifierCfg).length; } const elapsed = process.hrtime.bigint() - start; - return [name, Number(elapsed) / 1_000_000_000]; + return [name, len, iterations, Number(elapsed) / 1_000_000_000]; }); console.log(JSON.stringify(results)); diff --git a/bench/runners/minify-html (Rust)/.gitignore b/bench/runners/minify-html-onepass/.gitignore similarity index 100% rename from bench/runners/minify-html (Rust)/.gitignore rename to bench/runners/minify-html-onepass/.gitignore diff --git a/bench/runners/minify-html-onepass (Rust)/Cargo.toml b/bench/runners/minify-html-onepass/Cargo.toml similarity index 100% rename from bench/runners/minify-html-onepass (Rust)/Cargo.toml rename to bench/runners/minify-html-onepass/Cargo.toml diff --git a/bench/runners/minify-html (Rust)/build b/bench/runners/minify-html-onepass/build similarity index 100% rename from bench/runners/minify-html (Rust)/build rename to bench/runners/minify-html-onepass/build diff --git a/bench/runners/minify-html-onepass (Rust)/run b/bench/runners/minify-html-onepass/run similarity index 100% rename from bench/runners/minify-html-onepass (Rust)/run rename to bench/runners/minify-html-onepass/run diff --git a/bench/runners/minify-html-onepass (Rust)/src/main.rs b/bench/runners/minify-html-onepass/src/main.rs similarity index 75% rename from bench/runners/minify-html-onepass (Rust)/src/main.rs rename to bench/runners/minify-html-onepass/src/main.rs index 868db67..5606ad1 100644 --- a/bench/runners/minify-html-onepass (Rust)/src/main.rs +++ b/bench/runners/minify-html-onepass/src/main.rs @@ -11,7 +11,7 @@ fn main() { let tests = fs::read_dir(input_dir).unwrap().map(|d| d.unwrap()); - let mut results: Vec<(String, f64)> = Vec::new(); + let mut results: Vec<(String, usize, usize, f64)> = Vec::new(); let cfg = Cfg { minify_css: !html_only, minify_js: !html_only, @@ -20,12 +20,13 @@ fn main() { for t in tests { let source = fs::read(t.path()).unwrap(); let start = Instant::now(); + let mut len = 0; for _ in 0..iterations { let mut data = source.to_vec(); - let _ = in_place(&mut data, &cfg).expect("failed to minify"); + len = in_place(&mut data, &cfg).expect("failed to minify"); }; let elapsed = start.elapsed().as_secs_f64(); - results.push((t.file_name().into_string().unwrap(), elapsed)); + results.push((t.file_name().into_string().unwrap(), len, iterations, elapsed)); }; serde_json::to_writer(stdout(), &results).unwrap(); diff --git a/bench/runners/minify-html-onepass (Rust)/.gitignore b/bench/runners/minify-html/.gitignore similarity index 100% rename from bench/runners/minify-html-onepass (Rust)/.gitignore rename to bench/runners/minify-html/.gitignore diff --git a/bench/runners/minify-html (Rust)/Cargo.toml b/bench/runners/minify-html/Cargo.toml similarity index 100% rename from bench/runners/minify-html (Rust)/Cargo.toml rename to bench/runners/minify-html/Cargo.toml diff --git a/bench/runners/minify-html-onepass (Rust)/build b/bench/runners/minify-html/build similarity index 100% rename from bench/runners/minify-html-onepass (Rust)/build rename to bench/runners/minify-html/build diff --git a/bench/runners/minify-html (Rust)/run b/bench/runners/minify-html/run similarity index 100% rename from bench/runners/minify-html (Rust)/run rename to bench/runners/minify-html/run diff --git a/bench/runners/minify-html (Rust)/src/main.rs b/bench/runners/minify-html/src/main.rs similarity index 77% rename from bench/runners/minify-html (Rust)/src/main.rs rename to bench/runners/minify-html/src/main.rs index 565b993..ae1e67f 100644 --- a/bench/runners/minify-html (Rust)/src/main.rs +++ b/bench/runners/minify-html/src/main.rs @@ -11,7 +11,7 @@ fn main() { let tests = fs::read_dir(input_dir).unwrap().map(|d| d.unwrap()); - let mut results: Vec<(String, f64)> = Vec::new(); + let mut results: Vec<(String, usize, usize, f64)> = Vec::new(); let mut cfg = Cfg::new(); if !html_only { cfg.minify_css = true; @@ -21,11 +21,12 @@ fn main() { for t in tests { let source = fs::read(t.path()).unwrap(); let start = Instant::now(); + let mut len = 0; for _ in 0..iterations { - let _ = minify(&source, &cfg); + len = minify(&source, &cfg).len(); }; let elapsed = start.elapsed().as_secs_f64(); - results.push((t.file_name().into_string().unwrap(), elapsed)); + results.push((t.file_name().into_string().unwrap(), len, iterations, elapsed)); }; serde_json::to_writer(stdout(), &results).unwrap(); diff --git a/bench/runners/minimize/index.js b/bench/runners/minimize/index.js index b705a42..f2d7069 100644 --- a/bench/runners/minimize/index.js +++ b/bench/runners/minimize/index.js @@ -61,10 +61,11 @@ 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(); + let len; for (let i = 0; i < iterations; i++) { - new minimize({ plugins }).parse(src); + len = new minimize({ plugins }).parse(src).length; } const elapsed = process.hrtime.bigint() - start; - return [name, Number(elapsed) / 1_000_000_000]; + return [name, len, iterations, Number(elapsed) / 1_000_000_000]; }); console.log(JSON.stringify(results)); diff --git a/bench/sizes.js b/bench/sizes.js deleted file mode 100644 index 74588b9..0000000 --- a/bench/sizes.js +++ /dev/null @@ -1,42 +0,0 @@ -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, - relative: 1, - }, - }; - } - const original = sizes[test].original.absolute; - sizes[test][program] = { - absolute: result, - relative: result / original, - }; -}; - -(async () => { - for (const t of tests) { - for (const m of Object.keys(minifiers)) { - try { - 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 }); - fs.writeFileSync(minPath, min); - } catch (err) { - console.error(`Failed to run ${m} on test ${t.name}:`); - console.error(err); - process.exit(1); - } - } - } - results.writeSizeResults(sizes); -})(); diff --git a/bench/speeds.js b/bench/speeds.js deleted file mode 100644 index e571eb8..0000000 --- a/bench/speeds.js +++ /dev/null @@ -1,98 +0,0 @@ -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 { status, signal, error, stdout, stderr } = childProcess.spawnSync( - command, - args.map(String), - { - stdio: ["ignore", "pipe", "pipe"], - encoding: "utf8", - } - ); - if (error) { - throwErr(error.message); - } - if (signal) { - throwErr(`Command exited with signal ${signal}`); - } - if (status !== 0) { - throwErr(`Command exited with status ${status}`); - } - if (stderr) { - throwErr(`stderr: ${stderr}`); - } - return stdout; -}; - -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 }); - }); - -(async () => { - 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 t of tests) { - Object.assign(speeds[t.name], await runTest(t)); - } - results.writeSpeedResults(speeds); -})();