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 Jun 19, 2024
1 parent 9f985a2 commit 9645d90
Show file tree
Hide file tree
Showing 11 changed files with 787 additions and 11 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 .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
2 changes: 1 addition & 1 deletion .putout.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
"eslint/convert-rc-to-flat": "on"
}
},
"ignore": ["bundle"]
"ignore": ["bundle", "benchmark"]
}
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
3 changes: 3 additions & 0 deletions benchmark/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
source/*
!source/example*
result
53 changes: 53 additions & 0 deletions benchmark/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# 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%)' │
└──────────┴───────────────────┴───────────────────┴───────────────────┴──────────────────┴──────────────────┘
```
5 changes: 5 additions & 0 deletions benchmark/cli/minify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {minify} from '@putout/minify';
import { readFileSync, writeFileSync } from 'node:fs'

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

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}'
};

let 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;
}
}

async 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 });
}

async 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}`);
let endTime = 0;
try {
const startTime = Date.now();
execSync(cmd, { stdio: 'pipe', encoding: 'utf8' });
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);
}

async function compare() {
const result = {};
for (const key of Object.keys(listFiles)) {
result[key] = await compareTask(key);
const originalSize = statSync(getPathFromFile(key)).size;
result[key] = {
original: { size: originalSize },
...result[key]
}
}
return result;
}

async function main() {
await download();
const result = await 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();
Loading

0 comments on commit 9645d90

Please sign in to comment.