Skip to content

Commit

Permalink
feature: add benchmark (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
sirenkovladd authored and coderaiser committed Jun 19, 2024
1 parent 9f985a2 commit 9402240
Show file tree
Hide file tree
Showing 11 changed files with 319 additions and 10 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ yarn-error.log
coverage
.idea
bundle
.DS_Store
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ rules
rollup.config.js
lib/*.js
*.config.*
benchmark
1 change: 1 addition & 0 deletions .nycrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"**/bundle",
"**/stub",
"rollup.config.js",
"benchmark",
"**/*.config.*"
],
"branches": 100,
Expand Down
12 changes: 2 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,9 @@ minify(source, {
});
```

### How it's compared to [Terser](https://github.com/terser/terser)?
### How it's compared to X(your benchmark)?

For [such code](https://github.com/coderaiser/minify/issues/96#issuecomment-1546605157):

- 🔥 `@putout/minify`: `475B`
-`terser`: `482B`

`react.js`:

- 🔥 `@putout/minify`: `16309B`
-`terser`: `16346B`
[Benchmark](benchmark)

## License

Expand Down
7 changes: 7 additions & 0 deletions benchmark/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
source/*
!source/example*
result
.idea
*.swp
yarn-error.log
coverage
7 changes: 7 additions & 0 deletions benchmark/.putout.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"rules": {
"remove-unused-variables": "off",
"remove-console": "off",
"remove-useless-arguments/arguments": "off"
}
}
55 changes: 55 additions & 0 deletions benchmark/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Benchmark

This is a performance benchmark of the following bundlers:

- Bun
- esbuild
- Terser
- @putoutjs/minify

To run the benchmark:

- Need to install bun globally (https://bun.sh/docs/installation)

```sh
$ npm install
$ node index.js
```

## Results

The `real` results, as run on a 13-inch M1 Macbook air:

Time in ms

```
┌──────────┬─────┬─────────┬────────┬─────────────────┐
│ (index) │ bun │ esbuild │ terser │ putoutjs_minify │
├──────────┼─────┼─────────┼────────┼─────────────────┤
│ react │ 58 │ 50 │ 230 │ 2509 │
│ solidjs │ 21 │ 16 │ 270 │ 12735 │
│ treejs │ 74 │ 100 │ 1787 │ │
│ lodash │ 21 │ 27 │ 603 │ 108901 │
│ vue │ 60 │ 62 │ 1455 │ │
│ angular │ 32 │ 41 │ 1320 │ │
│ jquery │ 18 │ 23 │ 548 │ 80481 │
│ example1 │ 42 │ 35 │ 108 │ 330 │
└──────────┴─────┴─────────┴────────┴─────────────────┘
```

Size in bytes

```
┌──────────┬───────────────────┬───────────────────┬───────────────────┬──────────────────┬──────────────────┐
│ (index) │ original │ bun │ esbuild │ terser │ putoutjs_minify │
├──────────┼───────────────────┼───────────────────┼───────────────────┼──────────────────┼──────────────────┤
│ react │ '10751 (100.0%)' │ '10553 (98.2%)' │ '10644 (99.0%)' │ '10391 (96.7%)' │ '10052 (93.5%)' │
│ solidjs │ '28537 (100.0%)' │ '21888 (76.7%)' │ '20553 (72.0%)' │ '28202 (98.8%)' │ '21886 (76.7%)' │
│ treejs │ '677935 (100.0%)' │ '680368 (100.4%)' │ '657528 (97.0%)' │ '677573 (99.9%)' │ │
│ lodash │ '73015 (100.0%)' │ '72551 (99.4%)' │ '72189 (98.9%)' │ '70751 (96.9%)' │ '73688 (100.9%)' │
│ vue │ '196075 (100.0%)' │ '196443 (100.2%)' │ '193091 (98.5%)' │ '195200 (99.6%)' │ │
│ angular │ '177368 (100.0%)' │ '178187 (100.5%)' │ '177834 (100.3%)' │ '176388 (99.4%)' │ │
│ jquery │ '87533 (100.0%)' │ '87451 (99.9%)' │ '87119 (99.5%)' │ '86958 (99.3%)' │ '85889 (98.1%)' │
│ example1 │ '853 (100.0%)' │ '494 (57.9%)' │ '490 (57.4%)' │ '481 (56.4%)' │ '482 (56.5%)' │
└──────────┴───────────────────┴───────────────────┴───────────────────┴──────────────────┴──────────────────┘
```
6 changes: 6 additions & 0 deletions benchmark/cli/minify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {readFileSync, writeFileSync} from 'node:fs';
import process from 'node:process';
import {minify} from '@putout/minify';

const body = readFileSync(process.argv[2], 'utf8');
writeFileSync(process.argv[3], minify(body));
191 changes: 191 additions & 0 deletions benchmark/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import {get} from 'node:https';
import {execSync} from 'node:child_process';
import {
statSync,
writeFileSync,
createWriteStream,
rmSync,
} from 'node:fs';

const listFiles = {
react: {
url: 'https://cdn.jsdelivr.net/npm/[email protected]/umd/react.production.min.js',
},
solidjs: {
url: 'https://cdn.jsdelivr.net/npm/[email protected]/dist/solid.min.js',
},
treejs: {
url: 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.min.js',
},
lodash: {
url: 'https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js',
},
vue: {
url: 'https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.global.min.js',
},
angular: {
url: 'https://cdn.jsdelivr.net/npm/[email protected]/angular.min.js',
},
jquery: {
url: 'https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js',
},
example1: {
file: './source/example1.js',
},
};

const compressors = {
bun: 'bun build --minify {dist} --outfile {out}',
esbuild: './node_modules/.bin/esbuild {dist} --bundle --minify --outfile={out}',
terser: './node_modules/.bin/terser {dist} --compress --mangle --comments false -o {out}',
putoutjs_minify: 'node ./cli/minify.js {dist} {out}',
};

const debug = () => {};

// debug = console.log;
function fileExists(path) {
try {
const s = statSync(path);
debug(`File ${path} exists ${s.size} bytes`);

return s.size > 0;
} catch(e) {
debug(`File ${path} does not exist`);
return false;
}
}

function downloadFile(url, dest) {
if (fileExists(dest)) {
debug(`File ${dest} already exists`);
return;
}

return new Promise((resolve, reject) => {
debug(`Downloading ${url} to ${dest}`);
get(url, (res) => {
const file = createWriteStream(dest, '');
res.pipe(file);
res.on('end', () => {
debug(`Downloaded ${url} to ${dest}`);
resolve();
});
});
});
}

function fileFromUrl(url) {
return `./source/${url}.js`;
}

async function download() {
for (const [key, value] of Object.entries(listFiles)) {
if (!value.url)
continue;

const {url} = value;
const dest = fileFromUrl(key);

await downloadFile(url, dest);
}
}

function getPathFromFile(file) {
return listFiles[file].file || fileFromUrl(file);
}

function removeFile(filePath) {
rmSync(filePath, {
force: true,
});
}

function compareTask(file) {
const dist = getPathFromFile(file);
const result = {};

for (const [key, value] of Object.entries(compressors)) {
const out = `./result/${file}-${key}.js`;
removeFile(out);

const cmd = value
.replace('{dist}', dist)
.replace('{out}', out);

debug(`Running: ${cmd}`);
const endTime = 0;

try {
const startTime = Date.now();

execSync(cmd, {
stdio: 'pipe',
encoding: 'utf8',
});

const endTime = Date.now() - startTime;
} catch(e) {
console.error(`Error running ${key}: ${e.message} ${e.stderr}`);
continue;
}

const fileSize = statSync(out).size;

result[key] = {
time: endTime,
size: fileSize,
};
console.log(`File: ${file} Compressor: ${key} Time: ${endTime}ms Size: ${fileSize} bytes`);
}

return result;
}

function convertMap(fn, obj) {
return Object.fromEntries(Object
.entries(obj)
.map(([k, v]) => [k, fn(v, k, obj)])
.filter(([k, v]) => v !== undefined));
}

function convertTable(obj, key) {
return convertMap(convertMap.bind(null, (v) => v[key]), obj);
}

function compare() {
const result = {};

for (const key of Object.keys(listFiles)) {
result[key] = compareTask(key);
const originalSize = statSync(getPathFromFile(key)).size;

result[key] = {
original: {
size: originalSize,
},
...result[key],
};
}

return result;
}

async function main() {
await download();
const result = compare();

debug(result);
const resultTime = convertTable(result, 'time');

debug(resultTime);
console.log('Time in ms');
console.table(resultTime);
const resultSize = convertMap(convertMap.bind(null, (v, k, obj) => `${v} (${(100 * v / obj.original).toFixed(1)}%)`), convertTable(result, 'size'));

debug(resultSize);
console.log('Size in bytes');
console.table(resultSize);
}

main();
20 changes: 20 additions & 0 deletions benchmark/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "@putout/minify-benchmark",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"homepage": "https://github.com/putoutjs/minify/tree/master/benchmark#readme",
"license": "MIT",
"repository": {
"type": "git",
"url": "git://github.com/putoutjs/minify.git"
},
"dependencies": {
"@putout/minify": "^4.1.0",
"esbuild": "^0.21.5",
"terser": "^5.31.1"
}
}
28 changes: 28 additions & 0 deletions benchmark/source/example1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* global jQuery */
const isUndefined = (a) => typeof a === 'undefined';

jQuery(($) => {
let dp;

if (!isUndefined($.datepicker)) {
$('#ep_ipo_date').datepicker({
dateFormat: 'yy-mm-dd',
changeYear: true,
changeMonth: true,
});
$('#ep_ipo_date_dp').click(function() {
if ($('#ui-datepicker-div').is(':visible') && dp === $(this).attr('id')) {
$('#ep_ipo_date').datepicker('hide');
const dp = null;
} else {
$('#ep_ipo_date').datepicker('show');
const dp = $(this).attr('id');
}
});

$('#ep_ipo_date').focusout(function() {
if (!/^(?:\d{2}\/){2}\d{4}$/.test($(this).val()) && $(this).val().length)
$(this).val('');
});
}
});

0 comments on commit 9402240

Please sign in to comment.