overtake 1.0.4 → 1.1.0
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/README.md +68 -25
- package/bin/overtake.js +1 -1
- package/build/cli.cjs +44 -33
- package/build/cli.cjs.map +1 -1
- package/build/cli.js +43 -32
- package/build/cli.js.map +1 -1
- package/build/executor.cjs +6 -3
- package/build/executor.cjs.map +1 -1
- package/build/executor.d.ts +3 -2
- package/build/executor.js +6 -3
- package/build/executor.js.map +1 -1
- package/build/gc-watcher.cjs +31 -0
- package/build/gc-watcher.cjs.map +1 -0
- package/build/gc-watcher.d.ts +9 -0
- package/build/gc-watcher.js +21 -0
- package/build/gc-watcher.js.map +1 -0
- package/build/index.cjs +9 -1
- package/build/index.cjs.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.js +9 -1
- package/build/index.js.map +1 -1
- package/build/runner.cjs +226 -18
- package/build/runner.cjs.map +1 -1
- package/build/runner.d.ts +1 -1
- package/build/runner.js +226 -18
- package/build/runner.js.map +1 -1
- package/build/types.cjs.map +1 -1
- package/build/types.d.ts +4 -0
- package/build/types.js.map +1 -1
- package/build/utils.cjs +21 -0
- package/build/utils.cjs.map +1 -1
- package/build/utils.d.ts +1 -0
- package/build/utils.js +18 -0
- package/build/utils.js.map +1 -1
- package/build/worker.cjs +95 -8
- package/build/worker.cjs.map +1 -1
- package/build/worker.js +54 -8
- package/build/worker.js.map +1 -1
- package/examples/accuracy.ts +54 -0
- package/examples/complete.ts +3 -3
- package/examples/custom-reports.ts +21 -0
- package/examples/imports.ts +47 -0
- package/examples/quick-start.ts +10 -9
- package/package.json +10 -9
- package/src/cli.ts +46 -30
- package/src/executor.ts +8 -2
- package/src/gc-watcher.ts +23 -0
- package/src/index.ts +11 -0
- package/src/runner.ts +266 -17
- package/src/types.ts +4 -0
- package/src/utils.ts +20 -0
- package/src/worker.ts +59 -9
- package/CLAUDE.md +0 -145
- package/examples/array-copy.ts +0 -17
- package/examples/object-merge.ts +0 -41
- package/examples/serialization.ts +0 -22
package/README.md
CHANGED
|
@@ -19,10 +19,11 @@ const suite = benchmark('1M numbers', () => Array.from({ length: 1e6 }, (_, i) =
|
|
|
19
19
|
suite.target('for loop').measure('sum', (_, arr) => {
|
|
20
20
|
let sum = 0;
|
|
21
21
|
for (let i = 0; i < arr.length; i++) sum += arr[i];
|
|
22
|
-
return sum;
|
|
23
22
|
});
|
|
24
23
|
|
|
25
|
-
suite.target('reduce').measure('sum', (_, arr) =>
|
|
24
|
+
suite.target('reduce').measure('sum', (_, arr) => {
|
|
25
|
+
arr.reduce((a, b) => a + b);
|
|
26
|
+
});
|
|
26
27
|
```
|
|
27
28
|
|
|
28
29
|
```bash
|
|
@@ -70,16 +71,16 @@ pnpm add -D overtake
|
|
|
70
71
|
yarn add -D overtake
|
|
71
72
|
```
|
|
72
73
|
|
|
73
|
-
## ⚠️ Critical:
|
|
74
|
+
## ⚠️ Critical: Capture-Free Functions Required
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
Functions you pass to `target/measure/setup/pre/post` are stringified and re-evaluated in a worker. Anything they close over (including statically imported bindings) is **not** available. Pull dependencies inside the function body—typically with `await import(...)`.
|
|
76
77
|
|
|
77
78
|
```typescript
|
|
78
|
-
// ❌ WRONG
|
|
79
|
+
// ❌ WRONG: closes over serialize; it is undefined in the worker
|
|
79
80
|
import { serialize } from 'node:v8';
|
|
80
|
-
benchmark('data', getData).target('v8', () => ({ serialize }));
|
|
81
|
+
benchmark('data', getData).target('v8', () => ({ serialize }));
|
|
81
82
|
|
|
82
|
-
// ✅ CORRECT
|
|
83
|
+
// ✅ CORRECT: import inside the worker-run function
|
|
83
84
|
benchmark('data', getData)
|
|
84
85
|
.target('v8', async () => {
|
|
85
86
|
const { serialize } = await import('node:v8');
|
|
@@ -88,6 +89,29 @@ benchmark('data', getData)
|
|
|
88
89
|
.measure('serialize', ({ serialize }, input) => serialize(input));
|
|
89
90
|
```
|
|
90
91
|
|
|
92
|
+
### Importing Local Files
|
|
93
|
+
|
|
94
|
+
- **CLI mode (`npx overtake`)**: `baseUrl` is set to the benchmark file, so `await import('./helper.js')` works.
|
|
95
|
+
- **Programmatic mode (`suite.execute`)**: pass `baseUrl: import.meta.url` (the benchmark’s file URL) so relative imports resolve correctly. If you omit it, Overtake falls back to `process.cwd()` and relative imports may fail.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// CLI usage – relative path is fine
|
|
99
|
+
benchmark('local', () => 1)
|
|
100
|
+
.target('helper', async () => {
|
|
101
|
+
const { helper } = await import('./helpers.js');
|
|
102
|
+
return { helper };
|
|
103
|
+
})
|
|
104
|
+
.measure('use helper', ({ helper }) => helper());
|
|
105
|
+
|
|
106
|
+
// Programmatic usage – provide baseUrl
|
|
107
|
+
const suite = new Benchmark('local');
|
|
108
|
+
suite.target('helper', async () => {
|
|
109
|
+
const { helper } = await import('./helpers.js');
|
|
110
|
+
return { helper };
|
|
111
|
+
});
|
|
112
|
+
await suite.execute({ baseUrl: import.meta.url });
|
|
113
|
+
```
|
|
114
|
+
|
|
91
115
|
## Usage
|
|
92
116
|
|
|
93
117
|
### CLI Mode (Recommended)
|
|
@@ -99,11 +123,13 @@ When using `npx overtake`, a global `benchmark` function is provided:
|
|
|
99
123
|
benchmark('small', () => generateSmallData())
|
|
100
124
|
.feed('large', () => generateLargeData())
|
|
101
125
|
.target('algorithm A')
|
|
102
|
-
.measure('process', (_, input) =>
|
|
126
|
+
.measure('process', (_, input) => {
|
|
127
|
+
processA(input);
|
|
128
|
+
})
|
|
103
129
|
.target('algorithm B')
|
|
104
|
-
.measure('process', (_, input) =>
|
|
105
|
-
|
|
106
|
-
|
|
130
|
+
.measure('process', (_, input) => {
|
|
131
|
+
processB(input);
|
|
132
|
+
});
|
|
107
133
|
```
|
|
108
134
|
|
|
109
135
|
```bash
|
|
@@ -119,7 +145,9 @@ import { Benchmark, printTableReports } from 'overtake';
|
|
|
119
145
|
|
|
120
146
|
const suite = new Benchmark('dataset', () => getData());
|
|
121
147
|
|
|
122
|
-
suite.target('impl').measure('op', (_, input) =>
|
|
148
|
+
suite.target('impl').measure('op', (_, input) => {
|
|
149
|
+
process(input);
|
|
150
|
+
});
|
|
123
151
|
|
|
124
152
|
// Must explicitly execute
|
|
125
153
|
const reports = await suite.execute({
|
|
@@ -167,7 +195,6 @@ suite
|
|
|
167
195
|
// ctx contains setup return value
|
|
168
196
|
const hash = createHash('sha256').update(input).digest();
|
|
169
197
|
cache.set(input, hash);
|
|
170
|
-
return hash;
|
|
171
198
|
});
|
|
172
199
|
```
|
|
173
200
|
|
|
@@ -184,29 +211,45 @@ suite
|
|
|
184
211
|
.measure('process', ({ gcBlock }, input) => {
|
|
185
212
|
const result = input.map((x) => x * x);
|
|
186
213
|
gcBlock.add(result); // Prevent GC during measurement
|
|
187
|
-
return result;
|
|
188
214
|
});
|
|
189
215
|
```
|
|
190
216
|
|
|
191
217
|
## Examples
|
|
192
218
|
|
|
193
|
-
###
|
|
219
|
+
### Compare Algorithms
|
|
194
220
|
|
|
195
221
|
```typescript
|
|
196
|
-
//
|
|
197
|
-
const
|
|
222
|
+
// examples/quick-start.ts
|
|
223
|
+
const sumBenchmark = benchmark('1M numbers', () => Array.from({ length: 1_000_000 }, (_, i) => i));
|
|
198
224
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
})
|
|
204
|
-
.measure('serialize', ({ serialize }, input) => serialize(input));
|
|
225
|
+
sumBenchmark.target('for loop').measure('sum', (_, numbers) => {
|
|
226
|
+
let sum = 0;
|
|
227
|
+
for (let i = 0; i < numbers.length; i++) sum += numbers[i];
|
|
228
|
+
});
|
|
205
229
|
|
|
206
|
-
|
|
230
|
+
sumBenchmark.target('reduce').measure('sum', (_, numbers) => {
|
|
231
|
+
numbers.reduce((a, b) => a + b, 0);
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Import Local Modules
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
// examples/imports.ts - Correct way to import local files
|
|
239
|
+
.target('local files', async () => {
|
|
240
|
+
const { join } = await import('node:path');
|
|
241
|
+
const modulePath = join(process.cwd(), './build/myModule.js');
|
|
242
|
+
const { myFunction } = await import(modulePath);
|
|
243
|
+
return { myFunction };
|
|
244
|
+
})
|
|
207
245
|
```
|
|
208
246
|
|
|
209
|
-
**[📁
|
|
247
|
+
**[📁 See all examples](./examples/):**
|
|
248
|
+
|
|
249
|
+
- `quick-start.ts` - Minimal benchmark example
|
|
250
|
+
- `complete.ts` - All features (setup/teardown, pre/post hooks, multiple feeds)
|
|
251
|
+
- `imports.ts` - Import patterns and memory management
|
|
252
|
+
- `custom-reports.ts` - Statistics and custom report types
|
|
210
253
|
|
|
211
254
|
## CLI Options
|
|
212
255
|
|
package/bin/overtake.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
#!/usr/bin/env -S node --experimental-vm-modules --no-warnings
|
|
1
|
+
#!/usr/bin/env -S node --experimental-vm-modules --no-warnings --expose-gc
|
|
2
2
|
import '../build/cli.js';
|
package/build/cli.cjs
CHANGED
|
@@ -3,12 +3,13 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
5
|
const _nodemodule = require("node:module");
|
|
6
|
+
const _nodeurl = require("node:url");
|
|
6
7
|
const _nodevm = require("node:vm");
|
|
7
8
|
const _promises = require("node:fs/promises");
|
|
8
|
-
const _core = require("@swc/core");
|
|
9
9
|
const _commander = require("commander");
|
|
10
10
|
const _glob = require("glob");
|
|
11
11
|
const _indexcjs = require("./index.cjs");
|
|
12
|
+
const _utilscjs = require("./utils.cjs");
|
|
12
13
|
const _typescjs = require("./types.cjs");
|
|
13
14
|
function _getRequireWildcardCache(nodeInterop) {
|
|
14
15
|
if (typeof WeakMap !== "function") return null;
|
|
@@ -54,38 +55,26 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
54
55
|
const require1 = (0, _nodemodule.createRequire)(require("url").pathToFileURL(__filename).toString());
|
|
55
56
|
const { name, description, version } = require1('../package.json');
|
|
56
57
|
const commander = new _commander.Command();
|
|
57
|
-
|
|
58
|
-
const output = await (0, _core.transform)(code, {
|
|
59
|
-
filename: 'benchmark.ts',
|
|
60
|
-
jsc: {
|
|
61
|
-
parser: {
|
|
62
|
-
syntax: 'typescript',
|
|
63
|
-
tsx: false,
|
|
64
|
-
dynamicImport: true
|
|
65
|
-
},
|
|
66
|
-
target: 'esnext'
|
|
67
|
-
},
|
|
68
|
-
module: {
|
|
69
|
-
type: 'es6'
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
return output.code;
|
|
73
|
-
};
|
|
74
|
-
commander.name(name).description(description).version(version).argument('<path>', 'glob pattern to find benchmarks').addOption(new _commander.Option('-r, --report-types [reportTypes...]', 'statistic types to include in the report').choices(_typescjs.REPORT_TYPES).default(_indexcjs.DEFAULT_REPORT_TYPES)).addOption(new _commander.Option('-w, --workers [workers]', 'number of concurent workers').default(_indexcjs.DEFAULT_WORKERS).argParser(parseInt)).addOption(new _commander.Option('-f, --format [format]', 'output format').default('simple').choices([
|
|
58
|
+
commander.name(name).description(description).version(version).argument('<paths...>', 'glob pattern to find benchmarks').addOption(new _commander.Option('-r, --report-types [reportTypes...]', 'statistic types to include in the report').choices(_typescjs.REPORT_TYPES).default(_indexcjs.DEFAULT_REPORT_TYPES)).addOption(new _commander.Option('-w, --workers [workers]', 'number of concurent workers').default(_indexcjs.DEFAULT_WORKERS).argParser(parseInt)).addOption(new _commander.Option('-f, --format [format]', 'output format').default('simple').choices([
|
|
75
59
|
'simple',
|
|
76
60
|
'json',
|
|
77
61
|
'pjson',
|
|
78
62
|
'table'
|
|
79
|
-
])).addOption(new _commander.Option('--abs-threshold [absThreshold]', 'absolute error threshold in nanoseconds').argParser(parseInt)).addOption(new _commander.Option('--rel-threshold [relThreshold]', 'relative error threshold (fraction between 0 and 1)').argParser(parseInt)).addOption(new _commander.Option('--warmup-cycles [warmupCycles]', 'number of warmup cycles before measuring').argParser(parseInt)).addOption(new _commander.Option('--max-cycles [maxCycles]', 'maximum measurement cycles per feed').argParser(parseInt)).addOption(new _commander.Option('--min-cycles [minCycles]', 'minimum measurement cycles per feed').argParser(parseInt)).action(async (
|
|
80
|
-
const files =
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
63
|
+
])).addOption(new _commander.Option('--abs-threshold [absThreshold]', 'absolute error threshold in nanoseconds').argParser(parseInt)).addOption(new _commander.Option('--rel-threshold [relThreshold]', 'relative error threshold (fraction between 0 and 1)').argParser(parseInt)).addOption(new _commander.Option('--warmup-cycles [warmupCycles]', 'number of warmup cycles before measuring').argParser(parseInt)).addOption(new _commander.Option('--max-cycles [maxCycles]', 'maximum measurement cycles per feed').argParser(parseInt)).addOption(new _commander.Option('--min-cycles [minCycles]', 'minimum measurement cycles per feed').argParser(parseInt)).addOption(new _commander.Option('--no-gc-observer', 'disable GC overlap detection')).action(async (patterns, executeOptions)=>{
|
|
64
|
+
const files = new Set();
|
|
65
|
+
await Promise.all(patterns.map(async (pattern)=>{
|
|
66
|
+
const matches = await (0, _glob.glob)(pattern, {
|
|
67
|
+
absolute: true,
|
|
68
|
+
cwd: process.cwd()
|
|
69
|
+
}).catch(()=>[]);
|
|
70
|
+
matches.forEach((file)=>files.add(file));
|
|
71
|
+
}));
|
|
84
72
|
for (const file of files){
|
|
85
73
|
const stats = await (0, _promises.stat)(file).catch(()=>false);
|
|
86
74
|
if (stats && stats.isFile()) {
|
|
87
75
|
const content = await (0, _promises.readFile)(file, 'utf8');
|
|
88
|
-
const
|
|
76
|
+
const identifier = (0, _nodeurl.pathToFileURL)(file).href;
|
|
77
|
+
const code = await (0, _utilscjs.transpile)(content);
|
|
89
78
|
let instance;
|
|
90
79
|
const benchmark = (...args)=>{
|
|
91
80
|
if (instance) {
|
|
@@ -95,29 +84,51 @@ commander.name(name).description(description).version(version).argument('<path>'
|
|
|
95
84
|
return instance;
|
|
96
85
|
};
|
|
97
86
|
const script = new _nodevm.SourceTextModule(code, {
|
|
87
|
+
identifier,
|
|
98
88
|
context: (0, _nodevm.createContext)({
|
|
99
|
-
benchmark
|
|
100
|
-
|
|
89
|
+
benchmark,
|
|
90
|
+
Buffer,
|
|
91
|
+
console
|
|
92
|
+
}),
|
|
93
|
+
initializeImportMeta (meta) {
|
|
94
|
+
meta.url = identifier;
|
|
95
|
+
},
|
|
96
|
+
async importModuleDynamically (specifier, referencingModule) {
|
|
97
|
+
if (_nodemodule.Module.isBuiltin(specifier)) {
|
|
98
|
+
return Promise.resolve(specifier).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
|
|
99
|
+
}
|
|
100
|
+
const baseIdentifier = referencingModule.identifier ?? identifier;
|
|
101
|
+
const resolveFrom = (0, _nodemodule.createRequire)((0, _nodeurl.fileURLToPath)(baseIdentifier));
|
|
102
|
+
const resolved = resolveFrom.resolve(specifier);
|
|
103
|
+
return Promise.resolve(resolved).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
|
|
104
|
+
}
|
|
101
105
|
});
|
|
102
106
|
const imports = new Map();
|
|
103
107
|
await script.link(async (specifier, referencingModule)=>{
|
|
104
|
-
|
|
105
|
-
|
|
108
|
+
const baseIdentifier = referencingModule.identifier ?? identifier;
|
|
109
|
+
const resolveFrom = (0, _nodemodule.createRequire)((0, _nodeurl.fileURLToPath)(baseIdentifier));
|
|
110
|
+
const target = _nodemodule.Module.isBuiltin(specifier) ? specifier : resolveFrom.resolve(specifier);
|
|
111
|
+
const cached = imports.get(target);
|
|
112
|
+
if (cached) {
|
|
113
|
+
return cached;
|
|
106
114
|
}
|
|
107
|
-
const mod = await Promise.resolve(
|
|
115
|
+
const mod = await Promise.resolve(target).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
|
|
108
116
|
const exportNames = Object.keys(mod);
|
|
109
117
|
const imported = new _nodevm.SyntheticModule(exportNames, ()=>{
|
|
110
118
|
exportNames.forEach((key)=>imported.setExport(key, mod[key]));
|
|
111
119
|
}, {
|
|
112
|
-
identifier:
|
|
120
|
+
identifier: target,
|
|
113
121
|
context: referencingModule.context
|
|
114
122
|
});
|
|
115
|
-
imports.set(
|
|
123
|
+
imports.set(target, imported);
|
|
116
124
|
return imported;
|
|
117
125
|
});
|
|
118
126
|
await script.evaluate();
|
|
119
127
|
if (instance) {
|
|
120
|
-
const reports = await instance.execute(
|
|
128
|
+
const reports = await instance.execute({
|
|
129
|
+
...executeOptions,
|
|
130
|
+
baseUrl: identifier
|
|
131
|
+
});
|
|
121
132
|
switch(executeOptions.format){
|
|
122
133
|
case 'json':
|
|
123
134
|
{
|
package/build/cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { createRequire, Module } from 'node:module';\nimport {
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { createRequire, Module } from 'node:module';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { SyntheticModule, createContext, SourceTextModule } from 'node:vm';\nimport { stat, readFile } from 'node:fs/promises';\nimport { Command, Option } from 'commander';\nimport { glob } from 'glob';\nimport { Benchmark, printTableReports, printJSONReports, printSimpleReports, DEFAULT_REPORT_TYPES, DEFAULT_WORKERS } from './index.js';\nimport { transpile } from './utils.js';\nimport { REPORT_TYPES } from './types.js';\n\nconst require = createRequire(import.meta.url);\nconst { name, description, version } = require('../package.json');\n\nconst commander = new Command();\n\ncommander\n .name(name)\n .description(description)\n .version(version)\n .argument('<paths...>', 'glob pattern to find benchmarks')\n .addOption(new Option('-r, --report-types [reportTypes...]', 'statistic types to include in the report').choices(REPORT_TYPES).default(DEFAULT_REPORT_TYPES))\n .addOption(new Option('-w, --workers [workers]', 'number of concurent workers').default(DEFAULT_WORKERS).argParser(parseInt))\n .addOption(new Option('-f, --format [format]', 'output format').default('simple').choices(['simple', 'json', 'pjson', 'table']))\n .addOption(new Option('--abs-threshold [absThreshold]', 'absolute error threshold in nanoseconds').argParser(parseInt))\n .addOption(new Option('--rel-threshold [relThreshold]', 'relative error threshold (fraction between 0 and 1)').argParser(parseInt))\n .addOption(new Option('--warmup-cycles [warmupCycles]', 'number of warmup cycles before measuring').argParser(parseInt))\n .addOption(new Option('--max-cycles [maxCycles]', 'maximum measurement cycles per feed').argParser(parseInt))\n .addOption(new Option('--min-cycles [minCycles]', 'minimum measurement cycles per feed').argParser(parseInt))\n .addOption(new Option('--no-gc-observer', 'disable GC overlap detection'))\n .action(async (patterns: string[], executeOptions) => {\n const files = new Set<string>();\n await Promise.all(\n patterns.map(async (pattern) => {\n const matches = await glob(pattern, { absolute: true, cwd: process.cwd() }).catch(() => []);\n matches.forEach((file) => files.add(file));\n }),\n );\n\n for (const file of files) {\n const stats = await stat(file).catch(() => false as const);\n if (stats && stats.isFile()) {\n const content = await readFile(file, 'utf8');\n const identifier = pathToFileURL(file).href;\n const code = await transpile(content);\n let instance: Benchmark<unknown> | undefined;\n const benchmark = (...args: Parameters<(typeof Benchmark)['create']>) => {\n if (instance) {\n throw new Error('Only one benchmark per file is supported');\n }\n instance = Benchmark.create(...args);\n return instance;\n };\n const script = new SourceTextModule(code, {\n identifier,\n context: createContext({\n benchmark,\n Buffer,\n console,\n }),\n initializeImportMeta(meta) {\n meta.url = identifier;\n },\n async importModuleDynamically(specifier, referencingModule) {\n if (Module.isBuiltin(specifier)) {\n return import(specifier);\n }\n const baseIdentifier = referencingModule.identifier ?? identifier;\n const resolveFrom = createRequire(fileURLToPath(baseIdentifier));\n const resolved = resolveFrom.resolve(specifier);\n return import(resolved);\n },\n });\n const imports = new Map<string, SyntheticModule>();\n await script.link(async (specifier: string, referencingModule) => {\n const baseIdentifier = referencingModule.identifier ?? identifier;\n const resolveFrom = createRequire(fileURLToPath(baseIdentifier));\n const target = Module.isBuiltin(specifier) ? specifier : resolveFrom.resolve(specifier);\n const cached = imports.get(target);\n if (cached) {\n return cached;\n }\n const mod = await import(target);\n const exportNames = Object.keys(mod);\n const imported = new SyntheticModule(\n exportNames,\n () => {\n exportNames.forEach((key) => imported.setExport(key, mod[key]));\n },\n { identifier: target, context: referencingModule.context },\n );\n\n imports.set(target, imported);\n return imported;\n });\n await script.evaluate();\n\n if (instance) {\n const reports = await instance.execute({\n ...executeOptions,\n baseUrl: identifier,\n });\n switch (executeOptions.format) {\n case 'json':\n {\n printJSONReports(reports);\n }\n break;\n case 'pjson':\n {\n printJSONReports(reports, 2);\n }\n break;\n case 'table':\n {\n printTableReports(reports);\n }\n break;\n default:\n printSimpleReports(reports);\n }\n }\n }\n }\n });\n\ncommander.parse(process.argv);\n"],"names":["require","createRequire","name","description","version","commander","Command","argument","addOption","Option","choices","REPORT_TYPES","default","DEFAULT_REPORT_TYPES","DEFAULT_WORKERS","argParser","parseInt","action","patterns","executeOptions","files","Set","Promise","all","map","pattern","matches","glob","absolute","cwd","process","catch","forEach","file","add","stats","stat","isFile","content","readFile","identifier","pathToFileURL","href","code","transpile","instance","benchmark","args","Error","Benchmark","create","script","SourceTextModule","context","createContext","Buffer","console","initializeImportMeta","meta","url","importModuleDynamically","specifier","referencingModule","Module","isBuiltin","baseIdentifier","resolveFrom","fileURLToPath","resolved","resolve","imports","Map","link","target","cached","get","mod","exportNames","Object","keys","imported","SyntheticModule","key","setExport","set","evaluate","reports","execute","baseUrl","format","printJSONReports","printTableReports","printSimpleReports","parse","argv"],"mappings":";;;;4BAAsC;yBACO;wBACoB;0BAClC;2BACC;sBACX;0BACqG;0BAChG;0BACG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE7B,MAAMA,WAAUC,IAAAA,yBAAa,EAAC;AAC9B,MAAM,EAAEC,IAAI,EAAEC,WAAW,EAAEC,OAAO,EAAE,GAAGJ,SAAQ;AAE/C,MAAMK,YAAY,IAAIC,kBAAO;AAE7BD,UACGH,IAAI,CAACA,MACLC,WAAW,CAACA,aACZC,OAAO,CAACA,SACRG,QAAQ,CAAC,cAAc,mCACvBC,SAAS,CAAC,IAAIC,iBAAM,CAAC,uCAAuC,4CAA4CC,OAAO,CAACC,sBAAY,EAAEC,OAAO,CAACC,8BAAoB,GAC1JL,SAAS,CAAC,IAAIC,iBAAM,CAAC,2BAA2B,+BAA+BG,OAAO,CAACE,yBAAe,EAAEC,SAAS,CAACC,WAClHR,SAAS,CAAC,IAAIC,iBAAM,CAAC,yBAAyB,iBAAiBG,OAAO,CAAC,UAAUF,OAAO,CAAC;IAAC;IAAU;IAAQ;IAAS;CAAQ,GAC7HF,SAAS,CAAC,IAAIC,iBAAM,CAAC,kCAAkC,2CAA2CM,SAAS,CAACC,WAC5GR,SAAS,CAAC,IAAIC,iBAAM,CAAC,kCAAkC,uDAAuDM,SAAS,CAACC,WACxHR,SAAS,CAAC,IAAIC,iBAAM,CAAC,kCAAkC,4CAA4CM,SAAS,CAACC,WAC7GR,SAAS,CAAC,IAAIC,iBAAM,CAAC,4BAA4B,uCAAuCM,SAAS,CAACC,WAClGR,SAAS,CAAC,IAAIC,iBAAM,CAAC,4BAA4B,uCAAuCM,SAAS,CAACC,WAClGR,SAAS,CAAC,IAAIC,iBAAM,CAAC,oBAAoB,iCACzCQ,MAAM,CAAC,OAAOC,UAAoBC;IACjC,MAAMC,QAAQ,IAAIC;IAClB,MAAMC,QAAQC,GAAG,CACfL,SAASM,GAAG,CAAC,OAAOC;QAClB,MAAMC,UAAU,MAAMC,IAAAA,UAAI,EAACF,SAAS;YAAEG,UAAU;YAAMC,KAAKC,QAAQD,GAAG;QAAG,GAAGE,KAAK,CAAC,IAAM,EAAE;QAC1FL,QAAQM,OAAO,CAAC,CAACC,OAASb,MAAMc,GAAG,CAACD;IACtC;IAGF,KAAK,MAAMA,QAAQb,MAAO;QACxB,MAAMe,QAAQ,MAAMC,IAAAA,cAAI,EAACH,MAAMF,KAAK,CAAC,IAAM;QAC3C,IAAII,SAASA,MAAME,MAAM,IAAI;YAC3B,MAAMC,UAAU,MAAMC,IAAAA,kBAAQ,EAACN,MAAM;YACrC,MAAMO,aAAaC,IAAAA,sBAAa,EAACR,MAAMS,IAAI;YAC3C,MAAMC,OAAO,MAAMC,IAAAA,mBAAS,EAACN;YAC7B,IAAIO;YACJ,MAAMC,YAAY,CAAC,GAAGC;gBACpB,IAAIF,UAAU;oBACZ,MAAM,IAAIG,MAAM;gBAClB;gBACAH,WAAWI,mBAAS,CAACC,MAAM,IAAIH;gBAC/B,OAAOF;YACT;YACA,MAAMM,SAAS,IAAIC,wBAAgB,CAACT,MAAM;gBACxCH;gBACAa,SAASC,IAAAA,qBAAa,EAAC;oBACrBR;oBACAS;oBACAC;gBACF;gBACAC,sBAAqBC,IAAI;oBACvBA,KAAKC,GAAG,GAAGnB;gBACb;gBACA,MAAMoB,yBAAwBC,SAAS,EAAEC,iBAAiB;oBACxD,IAAIC,kBAAM,CAACC,SAAS,CAACH,YAAY;wBAC/B,OAAO,gBAAOA,6DAAP;oBACT;oBACA,MAAMI,iBAAiBH,kBAAkBtB,UAAU,IAAIA;oBACvD,MAAM0B,cAAcjE,IAAAA,yBAAa,EAACkE,IAAAA,sBAAa,EAACF;oBAChD,MAAMG,WAAWF,YAAYG,OAAO,CAACR;oBACrC,OAAO,gBAAOO,4DAAP;gBACT;YACF;YACA,MAAME,UAAU,IAAIC;YACpB,MAAMpB,OAAOqB,IAAI,CAAC,OAAOX,WAAmBC;gBAC1C,MAAMG,iBAAiBH,kBAAkBtB,UAAU,IAAIA;gBACvD,MAAM0B,cAAcjE,IAAAA,yBAAa,EAACkE,IAAAA,sBAAa,EAACF;gBAChD,MAAMQ,SAASV,kBAAM,CAACC,SAAS,CAACH,aAAaA,YAAYK,YAAYG,OAAO,CAACR;gBAC7E,MAAMa,SAASJ,QAAQK,GAAG,CAACF;gBAC3B,IAAIC,QAAQ;oBACV,OAAOA;gBACT;gBACA,MAAME,MAAM,MAAM,gBAAOH,0DAAP;gBAClB,MAAMI,cAAcC,OAAOC,IAAI,CAACH;gBAChC,MAAMI,WAAW,IAAIC,uBAAe,CAClCJ,aACA;oBACEA,YAAY7C,OAAO,CAAC,CAACkD,MAAQF,SAASG,SAAS,CAACD,KAAKN,GAAG,CAACM,IAAI;gBAC/D,GACA;oBAAE1C,YAAYiC;oBAAQpB,SAASS,kBAAkBT,OAAO;gBAAC;gBAG3DiB,QAAQc,GAAG,CAACX,QAAQO;gBACpB,OAAOA;YACT;YACA,MAAM7B,OAAOkC,QAAQ;YAErB,IAAIxC,UAAU;gBACZ,MAAMyC,UAAU,MAAMzC,SAAS0C,OAAO,CAAC;oBACrC,GAAGpE,cAAc;oBACjBqE,SAAShD;gBACX;gBACA,OAAQrB,eAAesE,MAAM;oBAC3B,KAAK;wBACH;4BACEC,IAAAA,0BAAgB,EAACJ;wBACnB;wBACA;oBACF,KAAK;wBACH;4BACEI,IAAAA,0BAAgB,EAACJ,SAAS;wBAC5B;wBACA;oBACF,KAAK;wBACH;4BACEK,IAAAA,2BAAiB,EAACL;wBACpB;wBACA;oBACF;wBACEM,IAAAA,4BAAkB,EAACN;gBACvB;YACF;QACF;IACF;AACF;AAEFjF,UAAUwF,KAAK,CAAC/D,QAAQgE,IAAI"}
|
package/build/cli.js
CHANGED
|
@@ -1,45 +1,34 @@
|
|
|
1
1
|
import { createRequire, Module } from 'node:module';
|
|
2
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
2
3
|
import { SyntheticModule, createContext, SourceTextModule } from 'node:vm';
|
|
3
4
|
import { stat, readFile } from 'node:fs/promises';
|
|
4
|
-
import { transform } from '@swc/core';
|
|
5
5
|
import { Command, Option } from 'commander';
|
|
6
6
|
import { glob } from 'glob';
|
|
7
7
|
import { Benchmark, printTableReports, printJSONReports, printSimpleReports, DEFAULT_REPORT_TYPES, DEFAULT_WORKERS } from "./index.js";
|
|
8
|
+
import { transpile } from "./utils.js";
|
|
8
9
|
import { REPORT_TYPES } from "./types.js";
|
|
9
10
|
const require = createRequire(import.meta.url);
|
|
10
11
|
const { name, description, version } = require('../package.json');
|
|
11
12
|
const commander = new Command();
|
|
12
|
-
|
|
13
|
-
const output = await transform(code, {
|
|
14
|
-
filename: 'benchmark.ts',
|
|
15
|
-
jsc: {
|
|
16
|
-
parser: {
|
|
17
|
-
syntax: 'typescript',
|
|
18
|
-
tsx: false,
|
|
19
|
-
dynamicImport: true
|
|
20
|
-
},
|
|
21
|
-
target: 'esnext'
|
|
22
|
-
},
|
|
23
|
-
module: {
|
|
24
|
-
type: 'es6'
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
return output.code;
|
|
28
|
-
};
|
|
29
|
-
commander.name(name).description(description).version(version).argument('<path>', 'glob pattern to find benchmarks').addOption(new Option('-r, --report-types [reportTypes...]', 'statistic types to include in the report').choices(REPORT_TYPES).default(DEFAULT_REPORT_TYPES)).addOption(new Option('-w, --workers [workers]', 'number of concurent workers').default(DEFAULT_WORKERS).argParser(parseInt)).addOption(new Option('-f, --format [format]', 'output format').default('simple').choices([
|
|
13
|
+
commander.name(name).description(description).version(version).argument('<paths...>', 'glob pattern to find benchmarks').addOption(new Option('-r, --report-types [reportTypes...]', 'statistic types to include in the report').choices(REPORT_TYPES).default(DEFAULT_REPORT_TYPES)).addOption(new Option('-w, --workers [workers]', 'number of concurent workers').default(DEFAULT_WORKERS).argParser(parseInt)).addOption(new Option('-f, --format [format]', 'output format').default('simple').choices([
|
|
30
14
|
'simple',
|
|
31
15
|
'json',
|
|
32
16
|
'pjson',
|
|
33
17
|
'table'
|
|
34
|
-
])).addOption(new Option('--abs-threshold [absThreshold]', 'absolute error threshold in nanoseconds').argParser(parseInt)).addOption(new Option('--rel-threshold [relThreshold]', 'relative error threshold (fraction between 0 and 1)').argParser(parseInt)).addOption(new Option('--warmup-cycles [warmupCycles]', 'number of warmup cycles before measuring').argParser(parseInt)).addOption(new Option('--max-cycles [maxCycles]', 'maximum measurement cycles per feed').argParser(parseInt)).addOption(new Option('--min-cycles [minCycles]', 'minimum measurement cycles per feed').argParser(parseInt)).action(async (
|
|
35
|
-
const files =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
18
|
+
])).addOption(new Option('--abs-threshold [absThreshold]', 'absolute error threshold in nanoseconds').argParser(parseInt)).addOption(new Option('--rel-threshold [relThreshold]', 'relative error threshold (fraction between 0 and 1)').argParser(parseInt)).addOption(new Option('--warmup-cycles [warmupCycles]', 'number of warmup cycles before measuring').argParser(parseInt)).addOption(new Option('--max-cycles [maxCycles]', 'maximum measurement cycles per feed').argParser(parseInt)).addOption(new Option('--min-cycles [minCycles]', 'minimum measurement cycles per feed').argParser(parseInt)).addOption(new Option('--no-gc-observer', 'disable GC overlap detection')).action(async (patterns, executeOptions)=>{
|
|
19
|
+
const files = new Set();
|
|
20
|
+
await Promise.all(patterns.map(async (pattern)=>{
|
|
21
|
+
const matches = await glob(pattern, {
|
|
22
|
+
absolute: true,
|
|
23
|
+
cwd: process.cwd()
|
|
24
|
+
}).catch(()=>[]);
|
|
25
|
+
matches.forEach((file)=>files.add(file));
|
|
26
|
+
}));
|
|
39
27
|
for (const file of files){
|
|
40
28
|
const stats = await stat(file).catch(()=>false);
|
|
41
29
|
if (stats && stats.isFile()) {
|
|
42
30
|
const content = await readFile(file, 'utf8');
|
|
31
|
+
const identifier = pathToFileURL(file).href;
|
|
43
32
|
const code = await transpile(content);
|
|
44
33
|
let instance;
|
|
45
34
|
const benchmark = (...args)=>{
|
|
@@ -50,29 +39,51 @@ commander.name(name).description(description).version(version).argument('<path>'
|
|
|
50
39
|
return instance;
|
|
51
40
|
};
|
|
52
41
|
const script = new SourceTextModule(code, {
|
|
42
|
+
identifier,
|
|
53
43
|
context: createContext({
|
|
54
|
-
benchmark
|
|
55
|
-
|
|
44
|
+
benchmark,
|
|
45
|
+
Buffer,
|
|
46
|
+
console
|
|
47
|
+
}),
|
|
48
|
+
initializeImportMeta (meta) {
|
|
49
|
+
meta.url = identifier;
|
|
50
|
+
},
|
|
51
|
+
async importModuleDynamically (specifier, referencingModule) {
|
|
52
|
+
if (Module.isBuiltin(specifier)) {
|
|
53
|
+
return import(specifier);
|
|
54
|
+
}
|
|
55
|
+
const baseIdentifier = referencingModule.identifier ?? identifier;
|
|
56
|
+
const resolveFrom = createRequire(fileURLToPath(baseIdentifier));
|
|
57
|
+
const resolved = resolveFrom.resolve(specifier);
|
|
58
|
+
return import(resolved);
|
|
59
|
+
}
|
|
56
60
|
});
|
|
57
61
|
const imports = new Map();
|
|
58
62
|
await script.link(async (specifier, referencingModule)=>{
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
const baseIdentifier = referencingModule.identifier ?? identifier;
|
|
64
|
+
const resolveFrom = createRequire(fileURLToPath(baseIdentifier));
|
|
65
|
+
const target = Module.isBuiltin(specifier) ? specifier : resolveFrom.resolve(specifier);
|
|
66
|
+
const cached = imports.get(target);
|
|
67
|
+
if (cached) {
|
|
68
|
+
return cached;
|
|
61
69
|
}
|
|
62
|
-
const mod = await import(
|
|
70
|
+
const mod = await import(target);
|
|
63
71
|
const exportNames = Object.keys(mod);
|
|
64
72
|
const imported = new SyntheticModule(exportNames, ()=>{
|
|
65
73
|
exportNames.forEach((key)=>imported.setExport(key, mod[key]));
|
|
66
74
|
}, {
|
|
67
|
-
identifier:
|
|
75
|
+
identifier: target,
|
|
68
76
|
context: referencingModule.context
|
|
69
77
|
});
|
|
70
|
-
imports.set(
|
|
78
|
+
imports.set(target, imported);
|
|
71
79
|
return imported;
|
|
72
80
|
});
|
|
73
81
|
await script.evaluate();
|
|
74
82
|
if (instance) {
|
|
75
|
-
const reports = await instance.execute(
|
|
83
|
+
const reports = await instance.execute({
|
|
84
|
+
...executeOptions,
|
|
85
|
+
baseUrl: identifier
|
|
86
|
+
});
|
|
76
87
|
switch(executeOptions.format){
|
|
77
88
|
case 'json':
|
|
78
89
|
{
|
package/build/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { createRequire, Module } from 'node:module';\nimport {
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { createRequire, Module } from 'node:module';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { SyntheticModule, createContext, SourceTextModule } from 'node:vm';\nimport { stat, readFile } from 'node:fs/promises';\nimport { Command, Option } from 'commander';\nimport { glob } from 'glob';\nimport { Benchmark, printTableReports, printJSONReports, printSimpleReports, DEFAULT_REPORT_TYPES, DEFAULT_WORKERS } from './index.js';\nimport { transpile } from './utils.js';\nimport { REPORT_TYPES } from './types.js';\n\nconst require = createRequire(import.meta.url);\nconst { name, description, version } = require('../package.json');\n\nconst commander = new Command();\n\ncommander\n .name(name)\n .description(description)\n .version(version)\n .argument('<paths...>', 'glob pattern to find benchmarks')\n .addOption(new Option('-r, --report-types [reportTypes...]', 'statistic types to include in the report').choices(REPORT_TYPES).default(DEFAULT_REPORT_TYPES))\n .addOption(new Option('-w, --workers [workers]', 'number of concurent workers').default(DEFAULT_WORKERS).argParser(parseInt))\n .addOption(new Option('-f, --format [format]', 'output format').default('simple').choices(['simple', 'json', 'pjson', 'table']))\n .addOption(new Option('--abs-threshold [absThreshold]', 'absolute error threshold in nanoseconds').argParser(parseInt))\n .addOption(new Option('--rel-threshold [relThreshold]', 'relative error threshold (fraction between 0 and 1)').argParser(parseInt))\n .addOption(new Option('--warmup-cycles [warmupCycles]', 'number of warmup cycles before measuring').argParser(parseInt))\n .addOption(new Option('--max-cycles [maxCycles]', 'maximum measurement cycles per feed').argParser(parseInt))\n .addOption(new Option('--min-cycles [minCycles]', 'minimum measurement cycles per feed').argParser(parseInt))\n .addOption(new Option('--no-gc-observer', 'disable GC overlap detection'))\n .action(async (patterns: string[], executeOptions) => {\n const files = new Set<string>();\n await Promise.all(\n patterns.map(async (pattern) => {\n const matches = await glob(pattern, { absolute: true, cwd: process.cwd() }).catch(() => []);\n matches.forEach((file) => files.add(file));\n }),\n );\n\n for (const file of files) {\n const stats = await stat(file).catch(() => false as const);\n if (stats && stats.isFile()) {\n const content = await readFile(file, 'utf8');\n const identifier = pathToFileURL(file).href;\n const code = await transpile(content);\n let instance: Benchmark<unknown> | undefined;\n const benchmark = (...args: Parameters<(typeof Benchmark)['create']>) => {\n if (instance) {\n throw new Error('Only one benchmark per file is supported');\n }\n instance = Benchmark.create(...args);\n return instance;\n };\n const script = new SourceTextModule(code, {\n identifier,\n context: createContext({\n benchmark,\n Buffer,\n console,\n }),\n initializeImportMeta(meta) {\n meta.url = identifier;\n },\n async importModuleDynamically(specifier, referencingModule) {\n if (Module.isBuiltin(specifier)) {\n return import(specifier);\n }\n const baseIdentifier = referencingModule.identifier ?? identifier;\n const resolveFrom = createRequire(fileURLToPath(baseIdentifier));\n const resolved = resolveFrom.resolve(specifier);\n return import(resolved);\n },\n });\n const imports = new Map<string, SyntheticModule>();\n await script.link(async (specifier: string, referencingModule) => {\n const baseIdentifier = referencingModule.identifier ?? identifier;\n const resolveFrom = createRequire(fileURLToPath(baseIdentifier));\n const target = Module.isBuiltin(specifier) ? specifier : resolveFrom.resolve(specifier);\n const cached = imports.get(target);\n if (cached) {\n return cached;\n }\n const mod = await import(target);\n const exportNames = Object.keys(mod);\n const imported = new SyntheticModule(\n exportNames,\n () => {\n exportNames.forEach((key) => imported.setExport(key, mod[key]));\n },\n { identifier: target, context: referencingModule.context },\n );\n\n imports.set(target, imported);\n return imported;\n });\n await script.evaluate();\n\n if (instance) {\n const reports = await instance.execute({\n ...executeOptions,\n baseUrl: identifier,\n });\n switch (executeOptions.format) {\n case 'json':\n {\n printJSONReports(reports);\n }\n break;\n case 'pjson':\n {\n printJSONReports(reports, 2);\n }\n break;\n case 'table':\n {\n printTableReports(reports);\n }\n break;\n default:\n printSimpleReports(reports);\n }\n }\n }\n }\n });\n\ncommander.parse(process.argv);\n"],"names":["createRequire","Module","fileURLToPath","pathToFileURL","SyntheticModule","createContext","SourceTextModule","stat","readFile","Command","Option","glob","Benchmark","printTableReports","printJSONReports","printSimpleReports","DEFAULT_REPORT_TYPES","DEFAULT_WORKERS","transpile","REPORT_TYPES","require","url","name","description","version","commander","argument","addOption","choices","default","argParser","parseInt","action","patterns","executeOptions","files","Set","Promise","all","map","pattern","matches","absolute","cwd","process","catch","forEach","file","add","stats","isFile","content","identifier","href","code","instance","benchmark","args","Error","create","script","context","Buffer","console","initializeImportMeta","meta","importModuleDynamically","specifier","referencingModule","isBuiltin","baseIdentifier","resolveFrom","resolved","resolve","imports","Map","link","target","cached","get","mod","exportNames","Object","keys","imported","key","setExport","set","evaluate","reports","execute","baseUrl","format","parse","argv"],"mappings":"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"}
|
package/build/executor.cjs
CHANGED
|
@@ -11,11 +11,12 @@ Object.defineProperty(exports, "createExecutor", {
|
|
|
11
11
|
const _nodeworker_threads = require("node:worker_threads");
|
|
12
12
|
const _nodeevents = require("node:events");
|
|
13
13
|
const _async = require("async");
|
|
14
|
-
const
|
|
14
|
+
const _nodeurl = require("node:url");
|
|
15
15
|
const _reportercjs = require("./reporter.cjs");
|
|
16
16
|
const _utilscjs = require("./utils.cjs");
|
|
17
|
-
const
|
|
18
|
-
|
|
17
|
+
const _typescjs = require("./types.cjs");
|
|
18
|
+
const createExecutor = ({ baseUrl = (0, _nodeurl.pathToFileURL)(process.cwd()).href, workers, warmupCycles, maxCycles, minCycles, absThreshold, relThreshold, gcObserver = true, reportTypes })=>{
|
|
19
|
+
const executor = (0, _async.queue)(async ({ baseUrl: runBaseUrl = baseUrl, setup, teardown, pre, run, post, data })=>{
|
|
19
20
|
const setupCode = setup?.toString();
|
|
20
21
|
const teardownCode = teardown?.toString();
|
|
21
22
|
const preCode = pre?.toString();
|
|
@@ -25,6 +26,7 @@ const createExecutor = ({ workers, warmupCycles, maxCycles, minCycles, absThresh
|
|
|
25
26
|
const durationsSAB = new SharedArrayBuffer(BigUint64Array.BYTES_PER_ELEMENT * maxCycles);
|
|
26
27
|
const workerFile = new URL('./worker.js', require("url").pathToFileURL(__filename).toString());
|
|
27
28
|
const workerData = {
|
|
29
|
+
baseUrl: runBaseUrl,
|
|
28
30
|
setupCode,
|
|
29
31
|
teardownCode,
|
|
30
32
|
preCode,
|
|
@@ -35,6 +37,7 @@ const createExecutor = ({ workers, warmupCycles, maxCycles, minCycles, absThresh
|
|
|
35
37
|
minCycles,
|
|
36
38
|
absThreshold,
|
|
37
39
|
relThreshold,
|
|
40
|
+
gcObserver,
|
|
38
41
|
controlSAB,
|
|
39
42
|
durationsSAB
|
|
40
43
|
};
|
package/build/executor.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/executor.ts"],"sourcesContent":["import { Worker } from 'node:worker_threads';\nimport { once } from 'node:events';\nimport { queue } from 'async';\nimport {
|
|
1
|
+
{"version":3,"sources":["../src/executor.ts"],"sourcesContent":["import { Worker } from 'node:worker_threads';\nimport { once } from 'node:events';\nimport { queue } from 'async';\nimport { pathToFileURL } from 'node:url';\nimport { createReport, Report } from './reporter.js';\nimport { cmp } from './utils.js';\nimport { RunOptions, ReportOptions, WorkerOptions, BenchmarkOptions, Control, ReportType, ReportTypeList, CONTROL_SLOTS } from './types.js';\n\nexport type ExecutorReport<R extends ReportTypeList> = Record<R[number], Report> & { count: number };\n\nexport interface ExecutorOptions<R extends ReportTypeList> extends BenchmarkOptions, ReportOptions<R> {\n baseUrl?: string;\n workers?: number;\n maxCycles?: number;\n}\n\nexport const createExecutor = <TContext, TInput, R extends ReportTypeList>({\n baseUrl = pathToFileURL(process.cwd()).href,\n workers,\n warmupCycles,\n maxCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver = true,\n reportTypes,\n}: Required<ExecutorOptions<R>>) => {\n const executor = queue<RunOptions<TContext, TInput>>(async ({ baseUrl: runBaseUrl = baseUrl, setup, teardown, pre, run, post, data }) => {\n const setupCode = setup?.toString();\n const teardownCode = teardown?.toString();\n const preCode = pre?.toString();\n const runCode = run.toString()!;\n const postCode = post?.toString();\n\n const controlSAB = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * CONTROL_SLOTS);\n const durationsSAB = new SharedArrayBuffer(BigUint64Array.BYTES_PER_ELEMENT * maxCycles);\n\n const workerFile = new URL('./worker.js', import.meta.url);\n const workerData: WorkerOptions = {\n baseUrl: runBaseUrl,\n setupCode,\n teardownCode,\n preCode,\n runCode,\n postCode,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver,\n\n controlSAB,\n durationsSAB,\n };\n\n const worker = new Worker(workerFile, {\n workerData,\n });\n const [exitCode] = await once(worker, 'exit');\n if (exitCode !== 0) {\n throw new Error(`worker exited with code ${exitCode}`);\n }\n\n const control = new Int32Array(controlSAB);\n const count = control[Control.INDEX];\n const durations = new BigUint64Array(durationsSAB).slice(0, count).sort(cmp);\n\n const report = reportTypes.map<[string, unknown]>((type) => [type, createReport(durations, type)] as [ReportType, Report]).concat([['count', count]]);\n return Object.fromEntries(report);\n }, workers);\n\n executor.error((err) => {\n console.error(err);\n });\n\n return executor;\n};\n"],"names":["createExecutor","baseUrl","pathToFileURL","process","cwd","href","workers","warmupCycles","maxCycles","minCycles","absThreshold","relThreshold","gcObserver","reportTypes","executor","queue","runBaseUrl","setup","teardown","pre","run","post","data","setupCode","toString","teardownCode","preCode","runCode","postCode","controlSAB","SharedArrayBuffer","Int32Array","BYTES_PER_ELEMENT","CONTROL_SLOTS","durationsSAB","BigUint64Array","workerFile","URL","workerData","worker","Worker","exitCode","once","Error","control","count","Control","INDEX","durations","slice","sort","cmp","report","map","type","createReport","concat","Object","fromEntries","error","err","console"],"mappings":";;;;+BAgBaA;;;eAAAA;;;oCAhBU;4BACF;uBACC;yBACQ;6BACO;0BACjB;0BAC2G;AAUxH,MAAMA,iBAAiB,CAA6C,EACzEC,UAAUC,IAAAA,sBAAa,EAACC,QAAQC,GAAG,IAAIC,IAAI,EAC3CC,OAAO,EACPC,YAAY,EACZC,SAAS,EACTC,SAAS,EACTC,YAAY,EACZC,YAAY,EACZC,aAAa,IAAI,EACjBC,WAAW,EACkB;IAC7B,MAAMC,WAAWC,IAAAA,YAAK,EAA+B,OAAO,EAAEd,SAASe,aAAaf,OAAO,EAAEgB,KAAK,EAAEC,QAAQ,EAAEC,GAAG,EAAEC,GAAG,EAAEC,IAAI,EAAEC,IAAI,EAAE;QAClI,MAAMC,YAAYN,OAAOO;QACzB,MAAMC,eAAeP,UAAUM;QAC/B,MAAME,UAAUP,KAAKK;QACrB,MAAMG,UAAUP,IAAII,QAAQ;QAC5B,MAAMI,WAAWP,MAAMG;QAEvB,MAAMK,aAAa,IAAIC,kBAAkBC,WAAWC,iBAAiB,GAAGC,uBAAa;QACrF,MAAMC,eAAe,IAAIJ,kBAAkBK,eAAeH,iBAAiB,GAAGxB;QAE9E,MAAM4B,aAAa,IAAIC,IAAI,eAAe;QAC1C,MAAMC,aAA4B;YAChCrC,SAASe;YACTO;YACAE;YACAC;YACAC;YACAC;YACAN;YAEAf;YACAE;YACAC;YACAC;YACAC;YAEAiB;YACAK;QACF;QAEA,MAAMK,SAAS,IAAIC,0BAAM,CAACJ,YAAY;YACpCE;QACF;QACA,MAAM,CAACG,SAAS,GAAG,MAAMC,IAAAA,gBAAI,EAACH,QAAQ;QACtC,IAAIE,aAAa,GAAG;YAClB,MAAM,IAAIE,MAAM,CAAC,wBAAwB,EAAEF,UAAU;QACvD;QAEA,MAAMG,UAAU,IAAIb,WAAWF;QAC/B,MAAMgB,QAAQD,OAAO,CAACE,iBAAO,CAACC,KAAK,CAAC;QACpC,MAAMC,YAAY,IAAIb,eAAeD,cAAce,KAAK,CAAC,GAAGJ,OAAOK,IAAI,CAACC,aAAG;QAE3E,MAAMC,SAASvC,YAAYwC,GAAG,CAAoB,CAACC,OAAS;gBAACA;gBAAMC,IAAAA,yBAAY,EAACP,WAAWM;aAAM,EAA0BE,MAAM,CAAC;YAAC;gBAAC;gBAASX;aAAM;SAAC;QACpJ,OAAOY,OAAOC,WAAW,CAACN;IAC5B,GAAG9C;IAEHQ,SAAS6C,KAAK,CAAC,CAACC;QACdC,QAAQF,KAAK,CAACC;IAChB;IAEA,OAAO9C;AACT"}
|
package/build/executor.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { RunOptions, ReportOptions, BenchmarkOptions, ReportTypeList } from './types.js';
|
|
2
1
|
import { Report } from './reporter.js';
|
|
2
|
+
import { RunOptions, ReportOptions, BenchmarkOptions, ReportTypeList } from './types.js';
|
|
3
3
|
export type ExecutorReport<R extends ReportTypeList> = Record<R[number], Report> & {
|
|
4
4
|
count: number;
|
|
5
5
|
};
|
|
6
6
|
export interface ExecutorOptions<R extends ReportTypeList> extends BenchmarkOptions, ReportOptions<R> {
|
|
7
|
+
baseUrl?: string;
|
|
7
8
|
workers?: number;
|
|
8
9
|
maxCycles?: number;
|
|
9
10
|
}
|
|
10
|
-
export declare const createExecutor: <TContext, TInput, R extends ReportTypeList>({ workers, warmupCycles, maxCycles, minCycles, absThreshold, relThreshold, reportTypes, }: Required<ExecutorOptions<R>>) => import("async").QueueObject<RunOptions<TContext, TInput>>;
|
|
11
|
+
export declare const createExecutor: <TContext, TInput, R extends ReportTypeList>({ baseUrl, workers, warmupCycles, maxCycles, minCycles, absThreshold, relThreshold, gcObserver, reportTypes, }: Required<ExecutorOptions<R>>) => import("async").QueueObject<RunOptions<TContext, TInput>>;
|