overtake 0.0.8 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +22 -9
- package/index.js +65 -35
- package/overtakes/async.md +25 -0
- package/package.json +22 -15
package/cli.js
CHANGED
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env -S node --no-warnings
|
|
2
2
|
|
|
3
|
-
import { Command } from 'commander';
|
|
3
|
+
import { Command, Option } from 'commander';
|
|
4
4
|
import Path from 'path';
|
|
5
|
-
import glob from 'glob';
|
|
6
|
-
import { load, createScript, benchmark, setup, teardown, measure, perform, run, defaultReporter } from './index.js';
|
|
7
|
-
import packageJson from './package.json'
|
|
5
|
+
import { glob } from 'glob';
|
|
6
|
+
import { load, createScript, benchmark, setup, teardown, measure, perform, run, defaultReporter, allowedFields } from './index.js';
|
|
7
|
+
import packageJson from './package.json' with { type: 'json' };
|
|
8
8
|
|
|
9
9
|
const commands = new Command();
|
|
10
10
|
|
|
11
11
|
commands.name('overtake').description(packageJson.description).version(packageJson.version, '-v, --version');
|
|
12
12
|
|
|
13
13
|
commands
|
|
14
|
-
.argument('[files...]', '
|
|
15
|
-
.option('-i, --inline [inline]', '
|
|
16
|
-
.option('-c, --count [count]', '
|
|
17
|
-
.
|
|
14
|
+
.argument('[files...]', 'File paths or path patterns to search benchmark scripts')
|
|
15
|
+
.option('-i, --inline [inline]', 'Inline benchmark.', (value, previous) => previous.concat([value]), [])
|
|
16
|
+
.option('-c, --count [count]', 'Perform count for inline benchmark.', (v) => parseInt(v))
|
|
17
|
+
.addOption(
|
|
18
|
+
new Option('-f, --fields [fields]', `Comma separated list of fields to report. Allowed values are: ${allowedFields}.`)
|
|
19
|
+
.default(['med', 'p95', 'p99', 'sum:total', 'count'])
|
|
20
|
+
.argParser((fields) =>
|
|
21
|
+
fields.split(',').filter((field) => {
|
|
22
|
+
if (!allowedFields.includes(field)) {
|
|
23
|
+
console.error(`Invalid field name: ${field}. Allowed values are: ${allowedFields.join(', ')}.`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
}),
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
.action(async (patterns, { count = 1, inline, fields }) => {
|
|
18
31
|
Object.assign(globalThis, { benchmark, setup, teardown, measure, perform });
|
|
19
32
|
|
|
20
33
|
const foundFiles = await glob(patterns);
|
|
@@ -43,7 +56,7 @@ commands
|
|
|
43
56
|
scripts.push(script);
|
|
44
57
|
}
|
|
45
58
|
|
|
46
|
-
await run(scripts, defaultReporter);
|
|
59
|
+
await run(scripts, defaultReporter, fields);
|
|
47
60
|
});
|
|
48
61
|
|
|
49
62
|
commands.on('--help', () => {
|
package/index.js
CHANGED
|
@@ -1,11 +1,38 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import WorkerThreads from 'node:worker_threads';
|
|
2
|
+
import pkg from 'conode';
|
|
3
|
+
const { createContext } = pkg;
|
|
3
4
|
|
|
4
5
|
const overtakeContext = createContext();
|
|
5
6
|
const suiteContext = createContext();
|
|
6
7
|
|
|
7
8
|
export const NOOP = () => {};
|
|
8
9
|
|
|
10
|
+
export const allowedFields = [
|
|
11
|
+
'mode',
|
|
12
|
+
'med',
|
|
13
|
+
'p1',
|
|
14
|
+
'p5',
|
|
15
|
+
'p10',
|
|
16
|
+
'p20',
|
|
17
|
+
'p33',
|
|
18
|
+
'p50',
|
|
19
|
+
'p66',
|
|
20
|
+
'p80',
|
|
21
|
+
'p90',
|
|
22
|
+
'p95',
|
|
23
|
+
'p99',
|
|
24
|
+
'min',
|
|
25
|
+
'max',
|
|
26
|
+
'avg',
|
|
27
|
+
'sum',
|
|
28
|
+
'count',
|
|
29
|
+
'setup',
|
|
30
|
+
'init',
|
|
31
|
+
'cycles',
|
|
32
|
+
'teardown',
|
|
33
|
+
'total',
|
|
34
|
+
];
|
|
35
|
+
|
|
9
36
|
export const setup = (fn) => {
|
|
10
37
|
suiteContext.getContext().setup = fn;
|
|
11
38
|
};
|
|
@@ -57,27 +84,25 @@ const map = {
|
|
|
57
84
|
measure: '✓ Measure',
|
|
58
85
|
};
|
|
59
86
|
|
|
60
|
-
export const defaultReporter = async (type, title, test) => {
|
|
87
|
+
export const defaultReporter = async (type, title, test, fields) => {
|
|
61
88
|
console.group(`${map[type]} ${title}`);
|
|
62
89
|
await test({
|
|
63
|
-
test: defaultReporter,
|
|
90
|
+
test: (...args) => defaultReporter(...args, fields),
|
|
64
91
|
output: (report) =>
|
|
65
92
|
console.table(
|
|
66
93
|
report.success
|
|
67
94
|
? {
|
|
68
|
-
[formatFloat(report.mode)]: {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
count: report.count,
|
|
74
|
-
},
|
|
95
|
+
[formatFloat(report.mode)]: fields.reduce((data, field) => {
|
|
96
|
+
const [key, alias = key] = field.split(':');
|
|
97
|
+
data[alias] = formatFloat(report[key]);
|
|
98
|
+
return data;
|
|
99
|
+
}, {}),
|
|
75
100
|
}
|
|
76
101
|
: {
|
|
77
102
|
error: {
|
|
78
103
|
reason: report.error,
|
|
79
104
|
},
|
|
80
|
-
}
|
|
105
|
+
},
|
|
81
106
|
),
|
|
82
107
|
});
|
|
83
108
|
console.groupEnd();
|
|
@@ -88,31 +113,36 @@ export function formatFloat(value, digits = ACCURACY) {
|
|
|
88
113
|
return parseFloat(value.toFixed(digits));
|
|
89
114
|
}
|
|
90
115
|
|
|
91
|
-
export const run = async (scripts, reporter) => {
|
|
116
|
+
export const run = async (scripts, reporter, fields) => {
|
|
92
117
|
for (const script of scripts) {
|
|
93
|
-
await reporter(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
118
|
+
await reporter(
|
|
119
|
+
'script',
|
|
120
|
+
script.filename,
|
|
121
|
+
async (scriptTest) => {
|
|
122
|
+
for (const suite of script.suites) {
|
|
123
|
+
await scriptTest.test('suite', suite.title, async (suiteTest) => {
|
|
124
|
+
await suiteContext.contextualize(suite, suite.init);
|
|
125
|
+
for (const perform of suite.performs) {
|
|
126
|
+
await suiteTest.test('perform', perform.title, async (performTest) => {
|
|
127
|
+
for (const measure of suite.measures) {
|
|
128
|
+
await performTest.test('measure', perform.count + ' ' + measure.title, async (measureTest) => {
|
|
129
|
+
const result = await runWorker({
|
|
130
|
+
setup: suite.setup,
|
|
131
|
+
teardown: suite.teardown,
|
|
132
|
+
init: measure.init,
|
|
133
|
+
count: perform.count,
|
|
134
|
+
args: perform.args,
|
|
135
|
+
});
|
|
136
|
+
measureTest.output(result);
|
|
107
137
|
});
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
fields,
|
|
145
|
+
);
|
|
116
146
|
}
|
|
117
147
|
};
|
|
118
148
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Async functions call performance
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
⭐ Script __benchmarks__/async.js
|
|
5
|
+
⇶ Suite Async functions performance
|
|
6
|
+
➤ Perform 10000000 calls
|
|
7
|
+
✓ Measure 10000000 calls of function that returns Promise.resolve()
|
|
8
|
+
┌─────────┬──────────┬──────────┬─────────┬─────────────┬──────────┐
|
|
9
|
+
│ (index) │ med │ p95 │ p99 │ total │ count │
|
|
10
|
+
├─────────┼──────────┼──────────┼─────────┼─────────────┼──────────┤
|
|
11
|
+
│ 0.00008 │ 0.000093 │ 0.000138 │ 0.00023 │ 1189.475052 │ 10000000 │
|
|
12
|
+
└─────────┴──────────┴──────────┴─────────┴─────────────┴──────────┘
|
|
13
|
+
✓ Measure 10000000 calls of async function that returns await Promise.resolve()
|
|
14
|
+
┌──────────┬──────────┬──────────┬──────────┬─────────────┬──────────┐
|
|
15
|
+
│ (index) │ med │ p95 │ p99 │ total │ count │
|
|
16
|
+
├──────────┼──────────┼──────────┼──────────┼─────────────┼──────────┤
|
|
17
|
+
│ 0.000108 │ 0.000122 │ 0.000267 │ 0.000389 │ 1735.581918 │ 10000000 │
|
|
18
|
+
└──────────┴──────────┴──────────┴──────────┴─────────────┴──────────┘
|
|
19
|
+
✓ Measure 10000000 calls of async function that returns Promise.resolve()
|
|
20
|
+
┌──────────┬──────────┬──────────┬──────────┬─────────────┬──────────┐
|
|
21
|
+
│ (index) │ med │ p95 │ p99 │ total │ count │
|
|
22
|
+
├──────────┼──────────┼──────────┼──────────┼─────────────┼──────────┤
|
|
23
|
+
│ 0.000105 │ 0.000125 │ 0.000203 │ 0.000333 │ 1557.773513 │ 10000000 │
|
|
24
|
+
└──────────┴──────────┴──────────┴──────────┴─────────────┴──────────┘
|
|
25
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "overtake",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "NodeJS performance benchmark",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"start": "./cli.js",
|
|
13
|
-
"test": "
|
|
14
|
-
"prepare": "husky
|
|
13
|
+
"test": "jest --detectOpenHandles",
|
|
14
|
+
"prepare": "husky"
|
|
15
15
|
},
|
|
16
16
|
"repository": {
|
|
17
17
|
"type": "git",
|
|
@@ -30,18 +30,25 @@
|
|
|
30
30
|
},
|
|
31
31
|
"homepage": "https://github.com/3axap4eHko/overtake#readme",
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@jest/globals": "^29.
|
|
34
|
-
"@
|
|
35
|
-
"
|
|
36
|
-
"jest": "^29.5.
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
33
|
+
"@jest/globals": "^29.7.0",
|
|
34
|
+
"@swc/core": "^1.11.24",
|
|
35
|
+
"@swc/jest": "^0.2.38",
|
|
36
|
+
"@types/jest": "^29.5.14",
|
|
37
|
+
"@types/node": "^22.15.18",
|
|
38
|
+
"ajv": "^8.17.1",
|
|
39
|
+
"ascertain": "1.2.104",
|
|
40
|
+
"husky": "^9.1.7",
|
|
41
|
+
"jest": "^29.7.0",
|
|
42
|
+
"mongodb": "^6.16.0",
|
|
43
|
+
"pg": "^8.16.0",
|
|
44
|
+
"prettier": "^3.5.3",
|
|
45
|
+
"pretty-quick": "^4.1.1",
|
|
46
|
+
"zod": "^3.24.4"
|
|
41
47
|
},
|
|
42
48
|
"dependencies": {
|
|
43
|
-
"commander": "^
|
|
44
|
-
"conode": "^0.
|
|
45
|
-
"glob": "^
|
|
46
|
-
}
|
|
49
|
+
"commander": "^13.1.0",
|
|
50
|
+
"conode": "^0.2.0",
|
|
51
|
+
"glob": "^11.0.2"
|
|
52
|
+
},
|
|
53
|
+
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
|
47
54
|
}
|