overtake 1.0.5 → 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 +25 -29
- package/build/cli.cjs +43 -33
- package/build/cli.cjs.map +1 -1
- package/build/cli.js +42 -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 -24
- package/build/runner.cjs.map +1 -1
- package/build/runner.d.ts +1 -1
- package/build/runner.js +226 -24
- 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 -14
- package/build/worker.cjs.map +1 -1
- package/build/worker.d.ts +1 -1
- package/build/worker.js +54 -8
- package/build/worker.js.map +1 -1
- package/examples/accuracy.ts +54 -0
- package/examples/custom-reports.ts +0 -1
- package/examples/imports.ts +3 -7
- package/examples/quick-start.ts +2 -0
- package/package.json +10 -9
- package/src/cli.ts +42 -29
- package/src/executor.ts +8 -2
- package/src/gc-watcher.ts +23 -0
- package/src/index.ts +11 -0
- package/src/runner.ts +265 -23
- package/src/types.ts +4 -0
- package/src/utils.ts +20 -0
- package/src/worker.ts +59 -9
package/README.md
CHANGED
|
@@ -71,50 +71,46 @@ pnpm add -D overtake
|
|
|
71
71
|
yarn add -D overtake
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
## ⚠️ Critical:
|
|
74
|
+
## ⚠️ Critical: Capture-Free Functions Required
|
|
75
75
|
|
|
76
|
-
|
|
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(...)`.
|
|
77
77
|
|
|
78
78
|
```typescript
|
|
79
|
-
// ❌ WRONG
|
|
79
|
+
// ❌ WRONG: closes over serialize; it is undefined in the worker
|
|
80
80
|
import { serialize } from 'node:v8';
|
|
81
|
-
benchmark('data', getData).target('v8', () => ({ serialize }));
|
|
81
|
+
benchmark('data', getData).target('v8', () => ({ serialize }));
|
|
82
82
|
|
|
83
|
-
// ✅ CORRECT
|
|
83
|
+
// ✅ CORRECT: import inside the worker-run function
|
|
84
84
|
benchmark('data', getData)
|
|
85
85
|
.target('v8', async () => {
|
|
86
86
|
const { serialize } = await import('node:v8');
|
|
87
87
|
return { serialize };
|
|
88
88
|
})
|
|
89
|
-
.measure('serialize', ({ serialize }, input) =>
|
|
90
|
-
serialize(input);
|
|
91
|
-
});
|
|
89
|
+
.measure('serialize', ({ serialize }, input) => serialize(input));
|
|
92
90
|
```
|
|
93
91
|
|
|
94
92
|
### Importing Local Files
|
|
95
93
|
|
|
96
|
-
**
|
|
97
|
-
|
|
98
|
-
````typescript
|
|
99
|
-
// ❌ WRONG - Relative path resolves from worker location
|
|
100
|
-
.target('myImpl', async () => {
|
|
101
|
-
const { myFunc } = await import('./utils/myModule.js'); // Error: looks in node_modules/overtake/build/utils/
|
|
102
|
-
return { myFunc };
|
|
103
|
-
})
|
|
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.
|
|
104
96
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
})
|
|
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());
|
|
112
105
|
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
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
|
+
```
|
|
118
114
|
|
|
119
115
|
## Usage
|
|
120
116
|
|
|
@@ -134,7 +130,7 @@ benchmark('small', () => generateSmallData())
|
|
|
134
130
|
.measure('process', (_, input) => {
|
|
135
131
|
processB(input);
|
|
136
132
|
});
|
|
137
|
-
|
|
133
|
+
```
|
|
138
134
|
|
|
139
135
|
```bash
|
|
140
136
|
npx overtake benchmark.ts --format table
|
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,30 +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
89
|
benchmark,
|
|
100
|
-
Buffer
|
|
101
|
-
|
|
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
|
+
}
|
|
102
105
|
});
|
|
103
106
|
const imports = new Map();
|
|
104
107
|
await script.link(async (specifier, referencingModule)=>{
|
|
105
|
-
|
|
106
|
-
|
|
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;
|
|
107
114
|
}
|
|
108
|
-
const mod = await Promise.resolve(
|
|
115
|
+
const mod = await Promise.resolve(target).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
|
|
109
116
|
const exportNames = Object.keys(mod);
|
|
110
117
|
const imported = new _nodevm.SyntheticModule(exportNames, ()=>{
|
|
111
118
|
exportNames.forEach((key)=>imported.setExport(key, mod[key]));
|
|
112
119
|
}, {
|
|
113
|
-
identifier:
|
|
120
|
+
identifier: target,
|
|
114
121
|
context: referencingModule.context
|
|
115
122
|
});
|
|
116
|
-
imports.set(
|
|
123
|
+
imports.set(target, imported);
|
|
117
124
|
return imported;
|
|
118
125
|
});
|
|
119
126
|
await script.evaluate();
|
|
120
127
|
if (instance) {
|
|
121
|
-
const reports = await instance.execute(
|
|
128
|
+
const reports = await instance.execute({
|
|
129
|
+
...executeOptions,
|
|
130
|
+
baseUrl: identifier
|
|
131
|
+
});
|
|
122
132
|
switch(executeOptions.format){
|
|
123
133
|
case 'json':
|
|
124
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":"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"}
|
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,30 +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
44
|
benchmark,
|
|
55
|
-
Buffer
|
|
56
|
-
|
|
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
|
+
}
|
|
57
60
|
});
|
|
58
61
|
const imports = new Map();
|
|
59
62
|
await script.link(async (specifier, referencingModule)=>{
|
|
60
|
-
|
|
61
|
-
|
|
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;
|
|
62
69
|
}
|
|
63
|
-
const mod = await import(
|
|
70
|
+
const mod = await import(target);
|
|
64
71
|
const exportNames = Object.keys(mod);
|
|
65
72
|
const imported = new SyntheticModule(exportNames, ()=>{
|
|
66
73
|
exportNames.forEach((key)=>imported.setExport(key, mod[key]));
|
|
67
74
|
}, {
|
|
68
|
-
identifier:
|
|
75
|
+
identifier: target,
|
|
69
76
|
context: referencingModule.context
|
|
70
77
|
});
|
|
71
|
-
imports.set(
|
|
78
|
+
imports.set(target, imported);
|
|
72
79
|
return imported;
|
|
73
80
|
});
|
|
74
81
|
await script.evaluate();
|
|
75
82
|
if (instance) {
|
|
76
|
-
const reports = await instance.execute(
|
|
83
|
+
const reports = await instance.execute({
|
|
84
|
+
...executeOptions,
|
|
85
|
+
baseUrl: identifier
|
|
86
|
+
});
|
|
77
87
|
switch(executeOptions.format){
|
|
78
88
|
case 'json':
|
|
79
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>>;
|
package/build/executor.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Worker } from 'node:worker_threads';
|
|
2
2
|
import { once } from 'node:events';
|
|
3
3
|
import { queue } from 'async';
|
|
4
|
-
import {
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
5
|
import { createReport } from "./reporter.js";
|
|
6
6
|
import { cmp } from "./utils.js";
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import { Control, CONTROL_SLOTS } from "./types.js";
|
|
8
|
+
export const createExecutor = ({ baseUrl = pathToFileURL(process.cwd()).href, workers, warmupCycles, maxCycles, minCycles, absThreshold, relThreshold, gcObserver = true, reportTypes })=>{
|
|
9
|
+
const executor = queue(async ({ baseUrl: runBaseUrl = baseUrl, setup, teardown, pre, run, post, data })=>{
|
|
9
10
|
const setupCode = setup?.toString();
|
|
10
11
|
const teardownCode = teardown?.toString();
|
|
11
12
|
const preCode = pre?.toString();
|
|
@@ -15,6 +16,7 @@ export const createExecutor = ({ workers, warmupCycles, maxCycles, minCycles, ab
|
|
|
15
16
|
const durationsSAB = new SharedArrayBuffer(BigUint64Array.BYTES_PER_ELEMENT * maxCycles);
|
|
16
17
|
const workerFile = new URL('./worker.js', import.meta.url);
|
|
17
18
|
const workerData = {
|
|
19
|
+
baseUrl: runBaseUrl,
|
|
18
20
|
setupCode,
|
|
19
21
|
teardownCode,
|
|
20
22
|
preCode,
|
|
@@ -25,6 +27,7 @@ export const createExecutor = ({ workers, warmupCycles, maxCycles, minCycles, ab
|
|
|
25
27
|
minCycles,
|
|
26
28
|
absThreshold,
|
|
27
29
|
relThreshold,
|
|
30
|
+
gcObserver,
|
|
28
31
|
controlSAB,
|
|
29
32
|
durationsSAB
|
|
30
33
|
};
|
package/build/executor.js.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":["Worker","once","queue","pathToFileURL","createReport","cmp","Control","CONTROL_SLOTS","createExecutor","baseUrl","process","cwd","href","workers","warmupCycles","maxCycles","minCycles","absThreshold","relThreshold","gcObserver","reportTypes","executor","runBaseUrl","setup","teardown","pre","run","post","data","setupCode","toString","teardownCode","preCode","runCode","postCode","controlSAB","SharedArrayBuffer","Int32Array","BYTES_PER_ELEMENT","durationsSAB","BigUint64Array","workerFile","URL","url","workerData","worker","exitCode","Error","control","count","INDEX","durations","slice","sort","report","map","type","concat","Object","fromEntries","error","err","console"],"mappings":"AAAA,SAASA,MAAM,QAAQ,sBAAsB;AAC7C,SAASC,IAAI,QAAQ,cAAc;AACnC,SAASC,KAAK,QAAQ,QAAQ;AAC9B,SAASC,aAAa,QAAQ,WAAW;AACzC,SAASC,YAAY,QAAgB,gBAAgB;AACrD,SAASC,GAAG,QAAQ,aAAa;AACjC,SAAqEC,OAAO,EAA8BC,aAAa,QAAQ,aAAa;AAU5I,OAAO,MAAMC,iBAAiB,CAA6C,EACzEC,UAAUN,cAAcO,QAAQC,GAAG,IAAIC,IAAI,EAC3CC,OAAO,EACPC,YAAY,EACZC,SAAS,EACTC,SAAS,EACTC,YAAY,EACZC,YAAY,EACZC,aAAa,IAAI,EACjBC,WAAW,EACkB;IAC7B,MAAMC,WAAWnB,MAAoC,OAAO,EAAEO,SAASa,aAAab,OAAO,EAAEc,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,GAAG/B;QACxE,MAAMgC,eAAe,IAAIH,kBAAkBI,eAAeF,iBAAiB,GAAGvB;QAE9E,MAAM0B,aAAa,IAAIC,IAAI,eAAe,YAAYC,GAAG;QACzD,MAAMC,aAA4B;YAChCnC,SAASa;YACTO;YACAE;YACAC;YACAC;YACAC;YACAN;YAEAd;YACAE;YACAC;YACAC;YACAC;YAEAgB;YACAI;QACF;QAEA,MAAMM,SAAS,IAAI7C,OAAOyC,YAAY;YACpCG;QACF;QACA,MAAM,CAACE,SAAS,GAAG,MAAM7C,KAAK4C,QAAQ;QACtC,IAAIC,aAAa,GAAG;YAClB,MAAM,IAAIC,MAAM,CAAC,wBAAwB,EAAED,UAAU;QACvD;QAEA,MAAME,UAAU,IAAIX,WAAWF;QAC/B,MAAMc,QAAQD,OAAO,CAAC1C,QAAQ4C,KAAK,CAAC;QACpC,MAAMC,YAAY,IAAIX,eAAeD,cAAca,KAAK,CAAC,GAAGH,OAAOI,IAAI,CAAChD;QAExE,MAAMiD,SAASlC,YAAYmC,GAAG,CAAoB,CAACC,OAAS;gBAACA;gBAAMpD,aAAa+C,WAAWK;aAAM,EAA0BC,MAAM,CAAC;YAAC;gBAAC;gBAASR;aAAM;SAAC;QACpJ,OAAOS,OAAOC,WAAW,CAACL;IAC5B,GAAGzC;IAEHQ,SAASuC,KAAK,CAAC,CAACC;QACdC,QAAQF,KAAK,CAACC;IAChB;IAEA,OAAOxC;AACT,EAAE"}
|