overtake 1.3.2 → 2.0.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 +12 -15
- package/bin/overtake.js +1 -1
- package/build/executor.d.ts +8 -3
- package/build/index.d.ts +10 -11
- package/build/reporter.d.ts +10 -2
- package/build/runner.d.ts +1 -1
- package/build/types.d.ts +7 -7
- package/build/utils.d.ts +3 -17
- package/package.json +8 -26
- package/src/__tests__/assert-no-closure.ts +135 -0
- package/src/__tests__/benchmark-execute.ts +48 -0
- package/src/cli.ts +139 -144
- package/src/executor.ts +59 -24
- package/src/index.ts +135 -68
- package/src/reporter.ts +77 -125
- package/src/runner.ts +28 -25
- package/src/types.ts +9 -9
- package/src/utils.ts +62 -46
- package/src/worker.ts +13 -12
- package/tsconfig.json +3 -1
- package/build/cli.cjs +0 -179
- package/build/cli.cjs.map +0 -1
- package/build/cli.js +0 -134
- package/build/cli.js.map +0 -1
- package/build/executor.cjs +0 -116
- package/build/executor.cjs.map +0 -1
- package/build/executor.js +0 -106
- package/build/executor.js.map +0 -1
- package/build/gc-watcher.cjs +0 -30
- package/build/gc-watcher.cjs.map +0 -1
- package/build/gc-watcher.js +0 -20
- package/build/gc-watcher.js.map +0 -1
- package/build/index.cjs +0 -400
- package/build/index.cjs.map +0 -1
- package/build/index.js +0 -335
- package/build/index.js.map +0 -1
- package/build/reporter.cjs +0 -364
- package/build/reporter.cjs.map +0 -1
- package/build/reporter.js +0 -346
- package/build/reporter.js.map +0 -1
- package/build/runner.cjs +0 -528
- package/build/runner.cjs.map +0 -1
- package/build/runner.js +0 -518
- package/build/runner.js.map +0 -1
- package/build/types.cjs +0 -66
- package/build/types.cjs.map +0 -1
- package/build/types.js +0 -33
- package/build/types.js.map +0 -1
- package/build/utils.cjs +0 -121
- package/build/utils.cjs.map +0 -1
- package/build/utils.js +0 -85
- package/build/utils.js.map +0 -1
- package/build/worker.cjs +0 -158
- package/build/worker.cjs.map +0 -1
- package/build/worker.js +0 -113
- package/build/worker.js.map +0 -1
package/src/runner.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { performance, PerformanceObserver } from 'node:perf_hooks';
|
|
2
|
-
import { Options, Control, DURATION_SCALE } from './types.
|
|
3
|
-
import { GCWatcher } from './gc-watcher.
|
|
4
|
-
import { StepFn } from './types.js';
|
|
5
|
-
|
|
6
|
-
const COMPLETE_VALUE = 100_00;
|
|
2
|
+
import { type Options, Control, DURATION_SCALE, COMPLETE_VALUE, type StepFn } from './types.ts';
|
|
3
|
+
import { GCWatcher } from './gc-watcher.ts';
|
|
7
4
|
|
|
8
5
|
const hr = process.hrtime.bigint.bind(process.hrtime);
|
|
9
6
|
|
|
10
|
-
const sink = new Int32Array(
|
|
7
|
+
const sink = new Int32Array(1);
|
|
11
8
|
const consume = (value: unknown) => {
|
|
12
9
|
let payload = 0;
|
|
13
10
|
switch (typeof value) {
|
|
@@ -32,7 +29,7 @@ const consume = (value: unknown) => {
|
|
|
32
29
|
default:
|
|
33
30
|
payload = -1;
|
|
34
31
|
}
|
|
35
|
-
|
|
32
|
+
sink[0] ^= payload;
|
|
36
33
|
};
|
|
37
34
|
|
|
38
35
|
const runSync = (run: Function, overhead: bigint) => {
|
|
@@ -518,13 +515,23 @@ export const benchmark = async <TContext, TInput>({
|
|
|
518
515
|
}
|
|
519
516
|
|
|
520
517
|
let i = 0;
|
|
521
|
-
|
|
522
|
-
let
|
|
518
|
+
const WELFORD_SCALE = 1_000_000n;
|
|
519
|
+
let meanS = 0n;
|
|
520
|
+
let m2S = 0n;
|
|
523
521
|
const outlierWindow: number[] = [];
|
|
524
522
|
let skipped = 0;
|
|
525
523
|
const maxSkipped = maxCycles * 10;
|
|
526
524
|
let disableFiltering = false;
|
|
527
525
|
|
|
526
|
+
const absThScaled = BigInt(Math.round(absThreshold)) * WELFORD_SCALE;
|
|
527
|
+
const absThSq = absThScaled * absThScaled;
|
|
528
|
+
const REL_PRECISION = 1_000_000n;
|
|
529
|
+
const relThBigint = BigInt(Math.round(relThreshold * Number(REL_PRECISION)));
|
|
530
|
+
const relThSq = relThBigint * relThBigint;
|
|
531
|
+
const relPrecSq = REL_PRECISION * REL_PRECISION;
|
|
532
|
+
const Z95_SQ_NUM = 38416n;
|
|
533
|
+
const Z95_SQ_DENOM = 10000n;
|
|
534
|
+
|
|
528
535
|
while (true) {
|
|
529
536
|
if (i >= maxCycles) break;
|
|
530
537
|
if (!disableFiltering && skipped >= maxSkipped) {
|
|
@@ -583,26 +590,28 @@ export const benchmark = async <TContext, TInput>({
|
|
|
583
590
|
}
|
|
584
591
|
|
|
585
592
|
const durationNumber = Number(sampleDuration);
|
|
586
|
-
pushWindow(outlierWindow, durationNumber, OUTLIER_WINDOW);
|
|
587
593
|
if (!disableFiltering) {
|
|
588
594
|
const { median, iqr } = medianAndIqr(outlierWindow);
|
|
595
|
+
pushWindow(outlierWindow, durationNumber, OUTLIER_WINDOW);
|
|
589
596
|
const maxAllowed = median + OUTLIER_IQR_MULTIPLIER * iqr || Number.POSITIVE_INFINITY;
|
|
590
597
|
if (outlierWindow.length >= 8 && durationNumber > maxAllowed && durationNumber - median > OUTLIER_ABS_THRESHOLD) {
|
|
591
598
|
skipped++;
|
|
592
599
|
continue;
|
|
593
600
|
}
|
|
594
601
|
|
|
595
|
-
const meanNumber = Number(
|
|
602
|
+
const meanNumber = Number(meanS / WELFORD_SCALE);
|
|
596
603
|
if (i >= 8 && meanNumber > 0 && durationNumber > OUTLIER_MULTIPLIER * meanNumber && durationNumber - meanNumber > OUTLIER_ABS_THRESHOLD) {
|
|
597
604
|
skipped++;
|
|
598
605
|
continue;
|
|
599
606
|
}
|
|
607
|
+
} else {
|
|
608
|
+
pushWindow(outlierWindow, durationNumber, OUTLIER_WINDOW);
|
|
600
609
|
}
|
|
601
610
|
|
|
602
611
|
durations[i++] = sampleDuration;
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
612
|
+
const deltaS = sampleDuration * WELFORD_SCALE - meanS;
|
|
613
|
+
meanS += deltaS / BigInt(i);
|
|
614
|
+
m2S += deltaS * (sampleDuration * WELFORD_SCALE - meanS);
|
|
606
615
|
|
|
607
616
|
const progress = (i / maxCycles) * COMPLETE_VALUE;
|
|
608
617
|
if (i % PROGRESS_STRIDE === 0) {
|
|
@@ -610,17 +619,11 @@ export const benchmark = async <TContext, TInput>({
|
|
|
610
619
|
}
|
|
611
620
|
|
|
612
621
|
if (i >= minCycles) {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
const meanNum = Number(mean);
|
|
620
|
-
const cov = stddev / (meanNum || 1);
|
|
621
|
-
if (cov <= relThreshold) {
|
|
622
|
-
break;
|
|
623
|
-
}
|
|
622
|
+
if (m2S <= absThSq * BigInt(i - 1)) break;
|
|
623
|
+
// RME convergence: Z95 * sem/mean <= relThreshold
|
|
624
|
+
// Z95^2 * m2S / (n*(n-1)*meanS^2) <= relThreshold^2
|
|
625
|
+
const ni = BigInt(i);
|
|
626
|
+
if (meanS !== 0n && Z95_SQ_NUM * m2S * relPrecSq <= relThSq * ni * (ni - 1n) * meanS * meanS * Z95_SQ_DENOM) break;
|
|
624
627
|
}
|
|
625
628
|
}
|
|
626
629
|
|
package/src/types.ts
CHANGED
|
@@ -99,15 +99,15 @@ export interface Options<TContext, TInput> extends RunOptions<TContext, TInput>,
|
|
|
99
99
|
controlSAB: SharedArrayBuffer;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
export
|
|
103
|
-
INDEX,
|
|
104
|
-
PROGRESS,
|
|
105
|
-
COMPLETE,
|
|
106
|
-
HEAP_USED,
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export const CONTROL_SLOTS = Object.
|
|
110
|
-
export const DEFAULT_CYCLES =
|
|
102
|
+
export const Control = {
|
|
103
|
+
INDEX: 0,
|
|
104
|
+
PROGRESS: 1,
|
|
105
|
+
COMPLETE: 2,
|
|
106
|
+
HEAP_USED: 3,
|
|
107
|
+
} as const;
|
|
108
|
+
|
|
109
|
+
export const CONTROL_SLOTS = Object.keys(Control).length;
|
|
110
|
+
export const DEFAULT_CYCLES = 10_000;
|
|
111
111
|
export const Z95 = 1.96;
|
|
112
112
|
export const DURATION_SCALE = 1000n;
|
|
113
113
|
export const COMPLETE_VALUE = 100_00;
|
package/src/utils.ts
CHANGED
|
@@ -1,11 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseSync } from '@swc/core';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
return
|
|
3
|
+
async function resolve(s: string, c: unknown, n: (...args: unknown[]) => unknown) {
|
|
4
|
+
try {
|
|
5
|
+
return await n(s, c);
|
|
6
|
+
} catch (e) {
|
|
7
|
+
if (s.endsWith('.js'))
|
|
8
|
+
try {
|
|
9
|
+
return await n(s.slice(0, -3) + '.ts', c);
|
|
10
|
+
} catch {}
|
|
11
|
+
throw e;
|
|
6
12
|
}
|
|
7
|
-
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const resolveHookUrl = 'data:text/javascript,' + encodeURIComponent(`export ${resolve.toString()}`);
|
|
16
|
+
|
|
17
|
+
export const isqrt = (n: bigint): bigint => {
|
|
18
|
+
if (n < 0n) throw new RangeError('Square root of negative');
|
|
19
|
+
if (n < 2n) return n;
|
|
20
|
+
let x = n;
|
|
21
|
+
let y = (x + 1n) >> 1n;
|
|
22
|
+
while (y < x) {
|
|
23
|
+
x = y;
|
|
24
|
+
y = (x + n / x) >> 1n;
|
|
25
|
+
}
|
|
26
|
+
return x;
|
|
8
27
|
};
|
|
28
|
+
|
|
9
29
|
export const cmp = (a: bigint | number, b: bigint | number): number => {
|
|
10
30
|
if (a > b) {
|
|
11
31
|
return 1;
|
|
@@ -23,10 +43,6 @@ export const max = (a: bigint, b: bigint) => {
|
|
|
23
43
|
return b;
|
|
24
44
|
};
|
|
25
45
|
|
|
26
|
-
export const divMod = (a: bigint, b: bigint) => {
|
|
27
|
-
return { quotient: a / b, remainder: a % b };
|
|
28
|
-
};
|
|
29
|
-
|
|
30
46
|
export function div(a: bigint, b: bigint, decimals: number = 2): string {
|
|
31
47
|
if (b === 0n) throw new RangeError('Division by zero');
|
|
32
48
|
const scale = 10n ** BigInt(decimals);
|
|
@@ -41,45 +57,45 @@ export function divs(a: bigint, b: bigint, scale: bigint): bigint {
|
|
|
41
57
|
return (a * scale) / b;
|
|
42
58
|
}
|
|
43
59
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
sub(value: bigint) {
|
|
53
|
-
this.value -= value * this.scale;
|
|
54
|
-
}
|
|
55
|
-
div(value: bigint) {
|
|
56
|
-
this.value /= value;
|
|
57
|
-
}
|
|
58
|
-
mul(value: bigint) {
|
|
59
|
-
this.value *= value;
|
|
60
|
+
const KNOWN_GLOBALS = new Set(Object.getOwnPropertyNames(globalThis));
|
|
61
|
+
KNOWN_GLOBALS.add('arguments');
|
|
62
|
+
|
|
63
|
+
function collectUnresolved(node: unknown, result: Set<string>) {
|
|
64
|
+
if (!node || typeof node !== 'object') return;
|
|
65
|
+
if (Array.isArray(node)) {
|
|
66
|
+
for (const item of node) collectUnresolved(item, result);
|
|
67
|
+
return;
|
|
60
68
|
}
|
|
61
|
-
|
|
62
|
-
|
|
69
|
+
const obj = node as Record<string, unknown>;
|
|
70
|
+
if (obj.type === 'Identifier' && obj.ctxt === 1 && typeof obj.value === 'string') {
|
|
71
|
+
result.add(obj.value);
|
|
63
72
|
}
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
for (const key of Object.keys(obj)) {
|
|
74
|
+
if (key === 'span') continue;
|
|
75
|
+
collectUnresolved(obj[key], result);
|
|
66
76
|
}
|
|
67
77
|
}
|
|
68
78
|
|
|
69
|
-
export
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
export function assertNoClosure(code: string, name: string): void {
|
|
80
|
+
let ast;
|
|
81
|
+
try {
|
|
82
|
+
ast = parseSync(`var __fn = ${code}`, { syntax: 'ecmascript', target: 'esnext' });
|
|
83
|
+
} catch {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const unresolved = new Set<string>();
|
|
87
|
+
collectUnresolved(ast, unresolved);
|
|
88
|
+
for (const g of KNOWN_GLOBALS) unresolved.delete(g);
|
|
89
|
+
if (unresolved.size === 0) return;
|
|
90
|
+
|
|
91
|
+
const vars = [...unresolved].join(', ');
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Benchmark "${name}" function references outer-scope variables: ${vars}\n\n` +
|
|
94
|
+
`Benchmark functions are serialized with .toString() and executed in an isolated\n` +
|
|
95
|
+
`worker thread. Closed-over variables from the original module scope are not\n` +
|
|
96
|
+
`available in the worker and will cause a ReferenceError at runtime.\n\n` +
|
|
97
|
+
`To fix this, move the referenced values into:\n` +
|
|
98
|
+
` - "setup" function (returned value becomes the first argument of run/pre/post)\n` +
|
|
99
|
+
` - "data" option (passed as the second argument of run/pre/post)`,
|
|
100
|
+
);
|
|
101
|
+
}
|
package/src/worker.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { workerData } from 'node:worker_threads';
|
|
2
|
-
import { SourceTextModule, SyntheticModule
|
|
3
|
-
import { createRequire } from 'node:module';
|
|
2
|
+
import { SourceTextModule, SyntheticModule } from 'node:vm';
|
|
3
|
+
import { createRequire, register } from 'node:module';
|
|
4
4
|
import { isAbsolute } from 'node:path';
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
|
-
import { benchmark } from './runner.
|
|
7
|
-
import { WorkerOptions } from './types.
|
|
6
|
+
import { benchmark } from './runner.ts';
|
|
7
|
+
import { type WorkerOptions } from './types.ts';
|
|
8
|
+
import { resolveHookUrl } from './utils.ts';
|
|
9
|
+
|
|
10
|
+
register(resolveHookUrl);
|
|
8
11
|
|
|
9
12
|
const {
|
|
10
13
|
benchmarkUrl,
|
|
@@ -41,7 +44,11 @@ const resolveSpecifier = (specifier: string) => {
|
|
|
41
44
|
if (isAbsolute(specifier)) {
|
|
42
45
|
return pathToFileURL(specifier).href;
|
|
43
46
|
}
|
|
44
|
-
|
|
47
|
+
try {
|
|
48
|
+
return requireFrom.resolve(specifier);
|
|
49
|
+
} catch {
|
|
50
|
+
return specifier;
|
|
51
|
+
}
|
|
45
52
|
};
|
|
46
53
|
|
|
47
54
|
const source = `
|
|
@@ -52,11 +59,6 @@ export const run = ${serialize(runCode)};
|
|
|
52
59
|
export const post = ${serialize(postCode)};
|
|
53
60
|
`;
|
|
54
61
|
|
|
55
|
-
const globals = Object.create(null);
|
|
56
|
-
for (const k of Object.getOwnPropertyNames(globalThis)) {
|
|
57
|
-
globals[k] = (globalThis as any)[k];
|
|
58
|
-
}
|
|
59
|
-
const context = createContext(globals);
|
|
60
62
|
const imports = new Map<string, SyntheticModule>();
|
|
61
63
|
|
|
62
64
|
const createSyntheticModule = (moduleExports: unknown, exportNames: string[], identifier: string) => {
|
|
@@ -71,7 +73,7 @@ const createSyntheticModule = (moduleExports: unknown, exportNames: string[], id
|
|
|
71
73
|
mod.setExport(name, (moduleExports as Record<string, unknown>)[name]);
|
|
72
74
|
}
|
|
73
75
|
},
|
|
74
|
-
{ identifier
|
|
76
|
+
{ identifier },
|
|
75
77
|
);
|
|
76
78
|
return mod;
|
|
77
79
|
};
|
|
@@ -111,7 +113,6 @@ const loadDynamicModule = async (target: string) => {
|
|
|
111
113
|
};
|
|
112
114
|
const mod = new SourceTextModule(source, {
|
|
113
115
|
identifier: resolvedBenchmarkUrl,
|
|
114
|
-
context,
|
|
115
116
|
initializeImportMeta(meta) {
|
|
116
117
|
meta.url = resolvedBenchmarkUrl;
|
|
117
118
|
},
|
package/tsconfig.json
CHANGED
|
@@ -8,10 +8,12 @@
|
|
|
8
8
|
"resolveJsonModule": true,
|
|
9
9
|
"allowJs": true,
|
|
10
10
|
"moduleResolution": "NodeNext",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"verbatimModuleSyntax": true,
|
|
11
13
|
"lib": ["ESNext"],
|
|
12
14
|
"rootDir": "src",
|
|
13
15
|
"outDir": "build"
|
|
14
16
|
},
|
|
15
17
|
"include": ["src/**/*.ts"],
|
|
16
|
-
"exclude": ["node_modules", "**/*.tmp.ts"]
|
|
18
|
+
"exclude": ["node_modules", "**/__tests__", "**/*.tmp.ts"]
|
|
17
19
|
}
|
package/build/cli.cjs
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
const _nodemodule = require("node:module");
|
|
6
|
-
const _nodeurl = require("node:url");
|
|
7
|
-
const _nodevm = require("node:vm");
|
|
8
|
-
const _promises = require("node:fs/promises");
|
|
9
|
-
const _commander = require("commander");
|
|
10
|
-
const _glob = require("glob");
|
|
11
|
-
const _indexcjs = require("./index.cjs");
|
|
12
|
-
const _utilscjs = require("./utils.cjs");
|
|
13
|
-
const _typescjs = require("./types.cjs");
|
|
14
|
-
function _getRequireWildcardCache(nodeInterop) {
|
|
15
|
-
if (typeof WeakMap !== "function") return null;
|
|
16
|
-
var cacheBabelInterop = new WeakMap();
|
|
17
|
-
var cacheNodeInterop = new WeakMap();
|
|
18
|
-
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
19
|
-
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
20
|
-
})(nodeInterop);
|
|
21
|
-
}
|
|
22
|
-
function _interop_require_wildcard(obj, nodeInterop) {
|
|
23
|
-
if (!nodeInterop && obj && obj.__esModule) {
|
|
24
|
-
return obj;
|
|
25
|
-
}
|
|
26
|
-
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
27
|
-
return {
|
|
28
|
-
default: obj
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
var cache = _getRequireWildcardCache(nodeInterop);
|
|
32
|
-
if (cache && cache.has(obj)) {
|
|
33
|
-
return cache.get(obj);
|
|
34
|
-
}
|
|
35
|
-
var newObj = {
|
|
36
|
-
__proto__: null
|
|
37
|
-
};
|
|
38
|
-
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
39
|
-
for(var key in obj){
|
|
40
|
-
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
41
|
-
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
42
|
-
if (desc && (desc.get || desc.set)) {
|
|
43
|
-
Object.defineProperty(newObj, key, desc);
|
|
44
|
-
} else {
|
|
45
|
-
newObj[key] = obj[key];
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
newObj.default = obj;
|
|
50
|
-
if (cache) {
|
|
51
|
-
cache.set(obj, newObj);
|
|
52
|
-
}
|
|
53
|
-
return newObj;
|
|
54
|
-
}
|
|
55
|
-
const require1 = (0, _nodemodule.createRequire)(require("url").pathToFileURL(__filename).toString());
|
|
56
|
-
const { name, description, version } = require1('../package.json');
|
|
57
|
-
const BENCHMARK_URL = Symbol.for('overtake.benchmarkUrl');
|
|
58
|
-
const commander = new _commander.Command();
|
|
59
|
-
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([
|
|
60
|
-
'simple',
|
|
61
|
-
'json',
|
|
62
|
-
'pjson',
|
|
63
|
-
'table',
|
|
64
|
-
'markdown',
|
|
65
|
-
'histogram'
|
|
66
|
-
])).addOption(new _commander.Option('--abs-threshold [absThreshold]', 'absolute error threshold in nanoseconds').argParser(parseFloat)).addOption(new _commander.Option('--rel-threshold [relThreshold]', 'relative error threshold (fraction between 0 and 1)').argParser(parseFloat)).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')).addOption(new _commander.Option('--progress', 'show progress bar during benchmark execution')).addOption(new _commander.Option('--save-baseline <file>', 'save benchmark results to baseline file')).addOption(new _commander.Option('--compare-baseline <file>', 'compare results against baseline file')).action(async (patterns, executeOptions)=>{
|
|
67
|
-
let baseline = null;
|
|
68
|
-
if (executeOptions.compareBaseline) {
|
|
69
|
-
try {
|
|
70
|
-
const content = await (0, _promises.readFile)(executeOptions.compareBaseline, 'utf8');
|
|
71
|
-
baseline = JSON.parse(content);
|
|
72
|
-
} catch {
|
|
73
|
-
console.error(`Warning: Could not load baseline file: ${executeOptions.compareBaseline}`);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
const files = new Set();
|
|
77
|
-
await Promise.all(patterns.map(async (pattern)=>{
|
|
78
|
-
const matches = await (0, _glob.glob)(pattern, {
|
|
79
|
-
absolute: true,
|
|
80
|
-
cwd: process.cwd()
|
|
81
|
-
}).catch(()=>[]);
|
|
82
|
-
matches.forEach((file)=>files.add(file));
|
|
83
|
-
}));
|
|
84
|
-
for (const file of files){
|
|
85
|
-
const stats = await (0, _promises.stat)(file).catch(()=>false);
|
|
86
|
-
if (stats && stats.isFile()) {
|
|
87
|
-
const content = await (0, _promises.readFile)(file, 'utf8');
|
|
88
|
-
const identifier = (0, _nodeurl.pathToFileURL)(file).href;
|
|
89
|
-
const code = await (0, _utilscjs.transpile)(content);
|
|
90
|
-
let instance;
|
|
91
|
-
const benchmark = (...args)=>{
|
|
92
|
-
if (instance) {
|
|
93
|
-
throw new Error('Only one benchmark per file is supported');
|
|
94
|
-
}
|
|
95
|
-
instance = _indexcjs.Benchmark.create(...args);
|
|
96
|
-
return instance;
|
|
97
|
-
};
|
|
98
|
-
const globals = Object.create(null);
|
|
99
|
-
for (const k of Object.getOwnPropertyNames(globalThis)){
|
|
100
|
-
globals[k] = globalThis[k];
|
|
101
|
-
}
|
|
102
|
-
globals.benchmark = benchmark;
|
|
103
|
-
const script = new _nodevm.SourceTextModule(code, {
|
|
104
|
-
identifier,
|
|
105
|
-
context: (0, _nodevm.createContext)(globals),
|
|
106
|
-
initializeImportMeta (meta) {
|
|
107
|
-
meta.url = identifier;
|
|
108
|
-
},
|
|
109
|
-
async importModuleDynamically (specifier, referencingModule) {
|
|
110
|
-
if (_nodemodule.Module.isBuiltin(specifier)) {
|
|
111
|
-
return Promise.resolve(specifier).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
|
|
112
|
-
}
|
|
113
|
-
const baseIdentifier = referencingModule.identifier ?? identifier;
|
|
114
|
-
const resolveFrom = (0, _nodemodule.createRequire)((0, _nodeurl.fileURLToPath)(baseIdentifier));
|
|
115
|
-
const resolved = resolveFrom.resolve(specifier);
|
|
116
|
-
return Promise.resolve(resolved).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
const imports = new Map();
|
|
120
|
-
await script.link(async (specifier, referencingModule)=>{
|
|
121
|
-
const baseIdentifier = referencingModule.identifier ?? identifier;
|
|
122
|
-
const resolveFrom = (0, _nodemodule.createRequire)((0, _nodeurl.fileURLToPath)(baseIdentifier));
|
|
123
|
-
const target = _nodemodule.Module.isBuiltin(specifier) ? specifier : resolveFrom.resolve(specifier);
|
|
124
|
-
const cached = imports.get(target);
|
|
125
|
-
if (cached) {
|
|
126
|
-
return cached;
|
|
127
|
-
}
|
|
128
|
-
const mod = await Promise.resolve(target).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
|
|
129
|
-
const exportNames = Object.keys(mod);
|
|
130
|
-
const imported = new _nodevm.SyntheticModule(exportNames, ()=>{
|
|
131
|
-
exportNames.forEach((key)=>imported.setExport(key, mod[key]));
|
|
132
|
-
}, {
|
|
133
|
-
identifier: target,
|
|
134
|
-
context: referencingModule.context
|
|
135
|
-
});
|
|
136
|
-
imports.set(target, imported);
|
|
137
|
-
return imported;
|
|
138
|
-
});
|
|
139
|
-
await script.evaluate();
|
|
140
|
-
if (instance) {
|
|
141
|
-
const reports = await instance.execute({
|
|
142
|
-
...executeOptions,
|
|
143
|
-
[BENCHMARK_URL]: identifier
|
|
144
|
-
});
|
|
145
|
-
if (executeOptions.saveBaseline) {
|
|
146
|
-
const baselineData = (0, _indexcjs.reportsToBaseline)(reports);
|
|
147
|
-
await (0, _promises.writeFile)(executeOptions.saveBaseline, JSON.stringify(baselineData, null, 2));
|
|
148
|
-
console.log(`Baseline saved to: ${executeOptions.saveBaseline}`);
|
|
149
|
-
}
|
|
150
|
-
if (baseline) {
|
|
151
|
-
(0, _indexcjs.printComparisonReports)(reports, baseline);
|
|
152
|
-
} else {
|
|
153
|
-
switch(executeOptions.format){
|
|
154
|
-
case 'json':
|
|
155
|
-
(0, _indexcjs.printJSONReports)(reports);
|
|
156
|
-
break;
|
|
157
|
-
case 'pjson':
|
|
158
|
-
(0, _indexcjs.printJSONReports)(reports, 2);
|
|
159
|
-
break;
|
|
160
|
-
case 'table':
|
|
161
|
-
(0, _indexcjs.printTableReports)(reports);
|
|
162
|
-
break;
|
|
163
|
-
case 'markdown':
|
|
164
|
-
(0, _indexcjs.printMarkdownReports)(reports);
|
|
165
|
-
break;
|
|
166
|
-
case 'histogram':
|
|
167
|
-
(0, _indexcjs.printHistogramReports)(reports);
|
|
168
|
-
break;
|
|
169
|
-
default:
|
|
170
|
-
(0, _indexcjs.printSimpleReports)(reports);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
commander.parse(process.argv);
|
|
178
|
-
|
|
179
|
-
//# sourceMappingURL=cli.cjs.map
|
package/build/cli.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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, writeFile } from 'node:fs/promises';\nimport { Command, Option } from 'commander';\nimport { glob } from 'glob';\nimport {\n Benchmark,\n printTableReports,\n printJSONReports,\n printSimpleReports,\n printMarkdownReports,\n printHistogramReports,\n printComparisonReports,\n reportsToBaseline,\n BaselineData,\n DEFAULT_REPORT_TYPES,\n DEFAULT_WORKERS,\n} 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');\nconst BENCHMARK_URL = Symbol.for('overtake.benchmarkUrl');\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', 'markdown', 'histogram']))\n .addOption(new Option('--abs-threshold [absThreshold]', 'absolute error threshold in nanoseconds').argParser(parseFloat))\n .addOption(new Option('--rel-threshold [relThreshold]', 'relative error threshold (fraction between 0 and 1)').argParser(parseFloat))\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 .addOption(new Option('--progress', 'show progress bar during benchmark execution'))\n .addOption(new Option('--save-baseline <file>', 'save benchmark results to baseline file'))\n .addOption(new Option('--compare-baseline <file>', 'compare results against baseline file'))\n .action(async (patterns: string[], executeOptions) => {\n let baseline: BaselineData | null = null;\n if (executeOptions.compareBaseline) {\n try {\n const content = await readFile(executeOptions.compareBaseline, 'utf8');\n baseline = JSON.parse(content) as BaselineData;\n } catch {\n console.error(`Warning: Could not load baseline file: ${executeOptions.compareBaseline}`);\n }\n }\n\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 globals = Object.create(null);\n for (const k of Object.getOwnPropertyNames(globalThis)) {\n globals[k] = (globalThis as any)[k];\n }\n globals.benchmark = benchmark;\n const script = new SourceTextModule(code, {\n identifier,\n context: createContext(globals),\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 [BENCHMARK_URL]: identifier,\n } as typeof executeOptions);\n\n if (executeOptions.saveBaseline) {\n const baselineData = reportsToBaseline(reports);\n await writeFile(executeOptions.saveBaseline, JSON.stringify(baselineData, null, 2));\n console.log(`Baseline saved to: ${executeOptions.saveBaseline}`);\n }\n\n if (baseline) {\n printComparisonReports(reports, baseline);\n } else {\n switch (executeOptions.format) {\n case 'json':\n printJSONReports(reports);\n break;\n case 'pjson':\n printJSONReports(reports, 2);\n break;\n case 'table':\n printTableReports(reports);\n break;\n case 'markdown':\n printMarkdownReports(reports);\n break;\n case 'histogram':\n printHistogramReports(reports);\n break;\n default:\n printSimpleReports(reports);\n }\n }\n }\n }\n }\n });\n\ncommander.parse(process.argv);\n"],"names":["require","createRequire","name","description","version","BENCHMARK_URL","Symbol","for","commander","Command","argument","addOption","Option","choices","REPORT_TYPES","default","DEFAULT_REPORT_TYPES","DEFAULT_WORKERS","argParser","parseInt","parseFloat","action","patterns","executeOptions","baseline","compareBaseline","content","readFile","JSON","parse","console","error","files","Set","Promise","all","map","pattern","matches","glob","absolute","cwd","process","catch","forEach","file","add","stats","stat","isFile","identifier","pathToFileURL","href","code","transpile","instance","benchmark","args","Error","Benchmark","create","globals","Object","k","getOwnPropertyNames","globalThis","script","SourceTextModule","context","createContext","initializeImportMeta","meta","url","importModuleDynamically","specifier","referencingModule","Module","isBuiltin","baseIdentifier","resolveFrom","fileURLToPath","resolved","resolve","imports","Map","link","target","cached","get","mod","exportNames","keys","imported","SyntheticModule","key","setExport","set","evaluate","reports","execute","saveBaseline","baselineData","reportsToBaseline","writeFile","stringify","log","printComparisonReports","format","printJSONReports","printTableReports","printMarkdownReports","printHistogramReports","printSimpleReports","argv"],"mappings":";;;;4BAAsC;yBACO;wBACoB;0BACvB;2BACV;sBACX;0BAad;0BACmB;0BACG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE7B,MAAMA,WAAUC,IAAAA,yBAAa,EAAC;AAC9B,MAAM,EAAEC,IAAI,EAAEC,WAAW,EAAEC,OAAO,EAAE,GAAGJ,SAAQ;AAC/C,MAAMK,gBAAgBC,OAAOC,GAAG,CAAC;AAEjC,MAAMC,YAAY,IAAIC,kBAAO;AAE7BD,UACGN,IAAI,CAACA,MACLC,WAAW,CAACA,aACZC,OAAO,CAACA,SACRM,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;IAAS;IAAY;CAAY,GACtJF,SAAS,CAAC,IAAIC,iBAAM,CAAC,kCAAkC,2CAA2CM,SAAS,CAACE,aAC5GT,SAAS,CAAC,IAAIC,iBAAM,CAAC,kCAAkC,uDAAuDM,SAAS,CAACE,aACxHT,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,iCACzCD,SAAS,CAAC,IAAIC,iBAAM,CAAC,cAAc,iDACnCD,SAAS,CAAC,IAAIC,iBAAM,CAAC,0BAA0B,4CAC/CD,SAAS,CAAC,IAAIC,iBAAM,CAAC,6BAA6B,0CAClDS,MAAM,CAAC,OAAOC,UAAoBC;IACjC,IAAIC,WAAgC;IACpC,IAAID,eAAeE,eAAe,EAAE;QAClC,IAAI;YACF,MAAMC,UAAU,MAAMC,IAAAA,kBAAQ,EAACJ,eAAeE,eAAe,EAAE;YAC/DD,WAAWI,KAAKC,KAAK,CAACH;QACxB,EAAE,OAAM;YACNI,QAAQC,KAAK,CAAC,CAAC,uCAAuC,EAAER,eAAeE,eAAe,EAAE;QAC1F;IACF;IAEA,MAAMO,QAAQ,IAAIC;IAClB,MAAMC,QAAQC,GAAG,CACfb,SAASc,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,MAAMvB,UAAU,MAAMC,IAAAA,kBAAQ,EAACkB,MAAM;YACrC,MAAMK,aAAaC,IAAAA,sBAAa,EAACN,MAAMO,IAAI;YAC3C,MAAMC,OAAO,MAAMC,IAAAA,mBAAS,EAAC5B;YAC7B,IAAI6B;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,UAAUC,OAAOF,MAAM,CAAC;YAC9B,KAAK,MAAMG,KAAKD,OAAOE,mBAAmB,CAACC,YAAa;gBACtDJ,OAAO,CAACE,EAAE,GAAG,AAACE,UAAkB,CAACF,EAAE;YACrC;YACAF,QAAQL,SAAS,GAAGA;YACpB,MAAMU,SAAS,IAAIC,wBAAgB,CAACd,MAAM;gBACxCH;gBACAkB,SAASC,IAAAA,qBAAa,EAACR;gBACvBS,sBAAqBC,IAAI;oBACvBA,KAAKC,GAAG,GAAGtB;gBACb;gBACA,MAAMuB,yBAAwBC,SAAS,EAAEC,iBAAiB;oBACxD,IAAIC,kBAAM,CAACC,SAAS,CAACH,YAAY;wBAC/B,OAAO,gBAAOA,6DAAP;oBACT;oBACA,MAAMI,iBAAiBH,kBAAkBzB,UAAU,IAAIA;oBACvD,MAAM6B,cAAc9E,IAAAA,yBAAa,EAAC+E,IAAAA,sBAAa,EAACF;oBAChD,MAAMG,WAAWF,YAAYG,OAAO,CAACR;oBACrC,OAAO,gBAAOO,4DAAP;gBACT;YACF;YACA,MAAME,UAAU,IAAIC;YACpB,MAAMlB,OAAOmB,IAAI,CAAC,OAAOX,WAAmBC;gBAC1C,MAAMG,iBAAiBH,kBAAkBzB,UAAU,IAAIA;gBACvD,MAAM6B,cAAc9E,IAAAA,yBAAa,EAAC+E,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,cAAc5B,OAAO6B,IAAI,CAACF;gBAChC,MAAMG,WAAW,IAAIC,uBAAe,CAClCH,aACA;oBACEA,YAAY9C,OAAO,CAAC,CAACkD,MAAQF,SAASG,SAAS,CAACD,KAAKL,GAAG,CAACK,IAAI;gBAC/D,GACA;oBAAE5C,YAAYoC;oBAAQlB,SAASO,kBAAkBP,OAAO;gBAAC;gBAG3De,QAAQa,GAAG,CAACV,QAAQM;gBACpB,OAAOA;YACT;YACA,MAAM1B,OAAO+B,QAAQ;YAErB,IAAI1C,UAAU;gBACZ,MAAM2C,UAAU,MAAM3C,SAAS4C,OAAO,CAAC;oBACrC,GAAG5E,cAAc;oBACjB,CAAClB,cAAc,EAAE6C;gBACnB;gBAEA,IAAI3B,eAAe6E,YAAY,EAAE;oBAC/B,MAAMC,eAAeC,IAAAA,2BAAiB,EAACJ;oBACvC,MAAMK,IAAAA,mBAAS,EAAChF,eAAe6E,YAAY,EAAExE,KAAK4E,SAAS,CAACH,cAAc,MAAM;oBAChFvE,QAAQ2E,GAAG,CAAC,CAAC,mBAAmB,EAAElF,eAAe6E,YAAY,EAAE;gBACjE;gBAEA,IAAI5E,UAAU;oBACZkF,IAAAA,gCAAsB,EAACR,SAAS1E;gBAClC,OAAO;oBACL,OAAQD,eAAeoF,MAAM;wBAC3B,KAAK;4BACHC,IAAAA,0BAAgB,EAACV;4BACjB;wBACF,KAAK;4BACHU,IAAAA,0BAAgB,EAACV,SAAS;4BAC1B;wBACF,KAAK;4BACHW,IAAAA,2BAAiB,EAACX;4BAClB;wBACF,KAAK;4BACHY,IAAAA,8BAAoB,EAACZ;4BACrB;wBACF,KAAK;4BACHa,IAAAA,+BAAqB,EAACb;4BACtB;wBACF;4BACEc,IAAAA,4BAAkB,EAACd;oBACvB;gBACF;YACF;QACF;IACF;AACF;AAEF1F,UAAUqB,KAAK,CAACa,QAAQuE,IAAI"}
|