overtake 1.4.0 → 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 +4 -15
- package/bin/overtake.js +1 -1
- package/build/executor.d.ts +7 -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 +6 -6
- package/build/utils.d.ts +1 -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 +48 -18
- package/src/index.ts +85 -57
- package/src/reporter.ts +27 -19
- package/src/runner.ts +2 -5
- package/src/types.ts +8 -8
- package/src/utils.ts +15 -54
- package/src/worker.ts +6 -3
- 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 -123
- package/build/executor.cjs.map +0 -1
- package/build/executor.js +0 -113
- 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 -442
- package/build/index.cjs.map +0 -1
- package/build/index.js +0 -377
- package/build/index.js.map +0 -1
- package/build/reporter.cjs +0 -311
- package/build/reporter.cjs.map +0 -1
- package/build/reporter.js +0 -293
- package/build/reporter.js.map +0 -1
- package/build/runner.cjs +0 -532
- package/build/runner.cjs.map +0 -1
- package/build/runner.js +0 -522
- 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 -174
- package/build/utils.cjs.map +0 -1
- package/build/utils.js +0 -132
- package/build/utils.js.map +0 -1
- package/build/worker.cjs +0 -155
- package/build/worker.cjs.map +0 -1
- package/build/worker.js +0 -110
- package/build/worker.js.map +0 -1
- package/src/__tests__/assert-no-closure.js +0 -134
package/src/reporter.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { div, max, divs, isqrt } from './utils.
|
|
2
|
-
import { ReportType, DURATION_SCALE } from './types.
|
|
1
|
+
import { div, max, divs, isqrt } from './utils.ts';
|
|
2
|
+
import { type ReportType, DURATION_SCALE } from './types.ts';
|
|
3
3
|
|
|
4
4
|
const units = [
|
|
5
5
|
{ unit: 'ns', factor: 1 },
|
|
@@ -18,12 +18,17 @@ function smartFixed(n: number): string {
|
|
|
18
18
|
});
|
|
19
19
|
}
|
|
20
20
|
export class Report {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
) {
|
|
21
|
+
readonly type: ReportType;
|
|
22
|
+
readonly value: bigint;
|
|
23
|
+
readonly uncertainty: number;
|
|
24
|
+
readonly scale: bigint;
|
|
25
|
+
|
|
26
|
+
constructor(type: ReportType, value: bigint, uncertainty: number = 0, scale: bigint = 1n) {
|
|
27
|
+
this.type = type;
|
|
28
|
+
this.value = value;
|
|
29
|
+
this.uncertainty = uncertainty;
|
|
30
|
+
this.scale = scale;
|
|
31
|
+
}
|
|
27
32
|
valueOf() {
|
|
28
33
|
return Number(div(this.value, this.scale));
|
|
29
34
|
}
|
|
@@ -75,7 +80,9 @@ const SQRT_SCALE_SQ = SQRT_SCALE * SQRT_SCALE;
|
|
|
75
80
|
const Z95_NUM = 196n;
|
|
76
81
|
const Z95_DENOM = 100n;
|
|
77
82
|
|
|
78
|
-
|
|
83
|
+
type Stats = { sum: bigint; mean: bigint; ssd: bigint; n: bigint };
|
|
84
|
+
|
|
85
|
+
export const computeStats = (durations: BigUint64Array): Stats => {
|
|
79
86
|
let sum = 0n;
|
|
80
87
|
for (const d of durations) sum += d;
|
|
81
88
|
const n = BigInt(durations.length);
|
|
@@ -88,11 +95,12 @@ const computeStats = (durations: BigUint64Array) => {
|
|
|
88
95
|
return { sum, mean, ssd, n };
|
|
89
96
|
};
|
|
90
97
|
|
|
91
|
-
export const createReport = (durations: BigUint64Array, type: ReportType): Report => {
|
|
98
|
+
export const createReport = (durations: BigUint64Array, type: ReportType, stats?: Stats): Report => {
|
|
92
99
|
const n = durations.length;
|
|
93
100
|
if (n === 0) {
|
|
94
101
|
return new Report(type, 0n);
|
|
95
102
|
}
|
|
103
|
+
const st = stats ?? computeStats(durations);
|
|
96
104
|
switch (type) {
|
|
97
105
|
case 'min': {
|
|
98
106
|
return new Report(type, durations[0], 0, DURATION_SCALE);
|
|
@@ -130,7 +138,7 @@ export const createReport = (durations: BigUint64Array, type: ReportType): Repor
|
|
|
130
138
|
}
|
|
131
139
|
|
|
132
140
|
case 'ops': {
|
|
133
|
-
const { mean: avgScaled, ssd, n: nBig } =
|
|
141
|
+
const { mean: avgScaled, ssd, n: nBig } = st;
|
|
134
142
|
const nsPerSecScaled = 1_000_000_000n * DURATION_SCALE;
|
|
135
143
|
const raw = Number(nsPerSecScaled) / Number(avgScaled);
|
|
136
144
|
const extra = raw < 1 ? Math.ceil(-Math.log10(raw)) : 0;
|
|
@@ -150,21 +158,21 @@ export const createReport = (durations: BigUint64Array, type: ReportType): Repor
|
|
|
150
158
|
return new Report(type, value, uncertainty, scale);
|
|
151
159
|
}
|
|
152
160
|
case 'mean': {
|
|
153
|
-
const { sum } =
|
|
161
|
+
const { sum } = st;
|
|
154
162
|
const value = divs(sum, BigInt(n), 1n);
|
|
155
163
|
return new Report(type, value, 0, DURATION_SCALE);
|
|
156
164
|
}
|
|
157
165
|
|
|
158
166
|
case 'variance': {
|
|
159
167
|
if (n < 2) return new Report(type, 0n, 0, DURATION_SCALE * DURATION_SCALE);
|
|
160
|
-
const { ssd } =
|
|
168
|
+
const { ssd } = st;
|
|
161
169
|
const variance = ssd / BigInt(n - 1);
|
|
162
170
|
return new Report(type, variance, 0, DURATION_SCALE * DURATION_SCALE);
|
|
163
171
|
}
|
|
164
172
|
|
|
165
173
|
case 'sd': {
|
|
166
174
|
if (n < 2) return new Report(type, 0n, 0, DURATION_SCALE);
|
|
167
|
-
const { ssd } =
|
|
175
|
+
const { ssd } = st;
|
|
168
176
|
const scaledVariance = (ssd * SQRT_SCALE_SQ) / BigInt(n - 1);
|
|
169
177
|
const sdScaled = isqrt(scaledVariance);
|
|
170
178
|
return new Report(type, sdScaled, 0, DURATION_SCALE * SQRT_SCALE);
|
|
@@ -172,7 +180,7 @@ export const createReport = (durations: BigUint64Array, type: ReportType): Repor
|
|
|
172
180
|
|
|
173
181
|
case 'sem': {
|
|
174
182
|
if (n < 2) return new Report(type, 0n, 0, DURATION_SCALE);
|
|
175
|
-
const { ssd, n: nBig } =
|
|
183
|
+
const { ssd, n: nBig } = st;
|
|
176
184
|
const semSqScaled = (ssd * SQRT_SCALE_SQ) / (BigInt(n - 1) * nBig);
|
|
177
185
|
const semScaled = isqrt(semSqScaled);
|
|
178
186
|
return new Report(type, semScaled, 0, DURATION_SCALE * SQRT_SCALE);
|
|
@@ -180,7 +188,7 @@ export const createReport = (durations: BigUint64Array, type: ReportType): Repor
|
|
|
180
188
|
|
|
181
189
|
case 'moe': {
|
|
182
190
|
if (n < 2) return new Report(type, 0n, 0, DURATION_SCALE);
|
|
183
|
-
const { ssd, n: nBig } =
|
|
191
|
+
const { ssd, n: nBig } = st;
|
|
184
192
|
const semSqScaled = (ssd * SQRT_SCALE_SQ) / (BigInt(n - 1) * nBig);
|
|
185
193
|
const semScaled = isqrt(semSqScaled);
|
|
186
194
|
const moeScaled = (Z95_NUM * semScaled) / Z95_DENOM;
|
|
@@ -189,7 +197,7 @@ export const createReport = (durations: BigUint64Array, type: ReportType): Repor
|
|
|
189
197
|
|
|
190
198
|
case 'rme': {
|
|
191
199
|
if (n < 2) return new Report(type, 0n);
|
|
192
|
-
const { mean, ssd, n: nBig } =
|
|
200
|
+
const { mean, ssd, n: nBig } = st;
|
|
193
201
|
if (mean === 0n) return new Report(type, 0n);
|
|
194
202
|
const RME_PRECISION = 1_000_000n;
|
|
195
203
|
const semOverMeanSqScaled = (ssd * RME_PRECISION * RME_PRECISION) / (BigInt(n - 1) * nBig * mean * mean);
|
|
@@ -223,7 +231,7 @@ export const createReport = (durations: BigUint64Array, type: ReportType): Repor
|
|
|
223
231
|
|
|
224
232
|
case 'ci_lower': {
|
|
225
233
|
if (n < 2) return new Report(type, 0n, 0, DURATION_SCALE);
|
|
226
|
-
const { mean, ssd, n: nBig } =
|
|
234
|
+
const { mean, ssd, n: nBig } = st;
|
|
227
235
|
const semSqScaled = (ssd * SQRT_SCALE_SQ) / (BigInt(n - 1) * nBig);
|
|
228
236
|
const semScaled = isqrt(semSqScaled);
|
|
229
237
|
const moeScaled = (Z95_NUM * semScaled) / Z95_DENOM;
|
|
@@ -233,7 +241,7 @@ export const createReport = (durations: BigUint64Array, type: ReportType): Repor
|
|
|
233
241
|
|
|
234
242
|
case 'ci_upper': {
|
|
235
243
|
if (n < 2) return new Report(type, 0n, 0, DURATION_SCALE);
|
|
236
|
-
const { mean, ssd, n: nBig } =
|
|
244
|
+
const { mean, ssd, n: nBig } = st;
|
|
237
245
|
const semSqScaled = (ssd * SQRT_SCALE_SQ) / (BigInt(n - 1) * nBig);
|
|
238
246
|
const semScaled = isqrt(semSqScaled);
|
|
239
247
|
const moeScaled = (Z95_NUM * semScaled) / Z95_DENOM;
|
package/src/runner.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
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
|
|
package/src/types.ts
CHANGED
|
@@ -99,14 +99,14 @@ 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.
|
|
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
110
|
export const DEFAULT_CYCLES = 10_000;
|
|
111
111
|
export const Z95 = 1.96;
|
|
112
112
|
export const DURATION_SCALE = 1000n;
|
package/src/utils.ts
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseSync } from '@swc/core';
|
|
2
|
+
|
|
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;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const resolveHookUrl = 'data:text/javascript,' + encodeURIComponent(`export ${resolve.toString()}`);
|
|
2
16
|
|
|
3
17
|
export const isqrt = (n: bigint): bigint => {
|
|
4
18
|
if (n < 0n) throw new RangeError('Square root of negative');
|
|
@@ -12,12 +26,6 @@ export const isqrt = (n: bigint): bigint => {
|
|
|
12
26
|
return x;
|
|
13
27
|
};
|
|
14
28
|
|
|
15
|
-
export const abs = (value: bigint) => {
|
|
16
|
-
if (value < 0n) {
|
|
17
|
-
return -value;
|
|
18
|
-
}
|
|
19
|
-
return value;
|
|
20
|
-
};
|
|
21
29
|
export const cmp = (a: bigint | number, b: bigint | number): number => {
|
|
22
30
|
if (a > b) {
|
|
23
31
|
return 1;
|
|
@@ -35,10 +43,6 @@ export const max = (a: bigint, b: bigint) => {
|
|
|
35
43
|
return b;
|
|
36
44
|
};
|
|
37
45
|
|
|
38
|
-
export const divMod = (a: bigint, b: bigint) => {
|
|
39
|
-
return { quotient: a / b, remainder: a % b };
|
|
40
|
-
};
|
|
41
|
-
|
|
42
46
|
export function div(a: bigint, b: bigint, decimals: number = 2): string {
|
|
43
47
|
if (b === 0n) throw new RangeError('Division by zero');
|
|
44
48
|
const scale = 10n ** BigInt(decimals);
|
|
@@ -53,31 +57,6 @@ export function divs(a: bigint, b: bigint, scale: bigint): bigint {
|
|
|
53
57
|
return (a * scale) / b;
|
|
54
58
|
}
|
|
55
59
|
|
|
56
|
-
export class ScaledBigInt {
|
|
57
|
-
constructor(
|
|
58
|
-
public value: bigint,
|
|
59
|
-
public scale: bigint,
|
|
60
|
-
) {}
|
|
61
|
-
add(value: bigint) {
|
|
62
|
-
this.value += value * this.scale;
|
|
63
|
-
}
|
|
64
|
-
sub(value: bigint) {
|
|
65
|
-
this.value -= value * this.scale;
|
|
66
|
-
}
|
|
67
|
-
div(value: bigint) {
|
|
68
|
-
this.value /= value;
|
|
69
|
-
}
|
|
70
|
-
mul(value: bigint) {
|
|
71
|
-
this.value *= value;
|
|
72
|
-
}
|
|
73
|
-
unscale() {
|
|
74
|
-
return this.value / this.scale;
|
|
75
|
-
}
|
|
76
|
-
number() {
|
|
77
|
-
return Number(div(this.value, this.scale));
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
60
|
const KNOWN_GLOBALS = new Set(Object.getOwnPropertyNames(globalThis));
|
|
82
61
|
KNOWN_GLOBALS.add('arguments');
|
|
83
62
|
|
|
@@ -120,21 +99,3 @@ export function assertNoClosure(code: string, name: string): void {
|
|
|
120
99
|
` - "data" option (passed as the second argument of run/pre/post)`,
|
|
121
100
|
);
|
|
122
101
|
}
|
|
123
|
-
|
|
124
|
-
export const transpile = async (code: string): Promise<string> => {
|
|
125
|
-
const output = await transform(code, {
|
|
126
|
-
filename: 'benchmark.ts',
|
|
127
|
-
jsc: {
|
|
128
|
-
parser: {
|
|
129
|
-
syntax: 'typescript',
|
|
130
|
-
tsx: false,
|
|
131
|
-
dynamicImport: true,
|
|
132
|
-
},
|
|
133
|
-
target: 'esnext',
|
|
134
|
-
},
|
|
135
|
-
module: {
|
|
136
|
-
type: 'es6',
|
|
137
|
-
},
|
|
138
|
-
});
|
|
139
|
-
return output.code;
|
|
140
|
-
};
|
package/src/worker.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { workerData } from 'node:worker_threads';
|
|
2
2
|
import { SourceTextModule, SyntheticModule } from 'node:vm';
|
|
3
|
-
import { createRequire } from 'node:module';
|
|
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,
|
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"}
|
package/build/cli.js
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { createRequire, Module } from 'node:module';
|
|
2
|
-
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
3
|
-
import { SyntheticModule, createContext, SourceTextModule } from 'node:vm';
|
|
4
|
-
import { stat, readFile, writeFile } from 'node:fs/promises';
|
|
5
|
-
import { Command, Option } from 'commander';
|
|
6
|
-
import { glob } from 'glob';
|
|
7
|
-
import { Benchmark, printTableReports, printJSONReports, printSimpleReports, printMarkdownReports, printHistogramReports, printComparisonReports, reportsToBaseline, DEFAULT_REPORT_TYPES, DEFAULT_WORKERS } from "./index.js";
|
|
8
|
-
import { transpile } from "./utils.js";
|
|
9
|
-
import { REPORT_TYPES } from "./types.js";
|
|
10
|
-
const require = createRequire(import.meta.url);
|
|
11
|
-
const { name, description, version } = require('../package.json');
|
|
12
|
-
const BENCHMARK_URL = Symbol.for('overtake.benchmarkUrl');
|
|
13
|
-
const commander = new Command();
|
|
14
|
-
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([
|
|
15
|
-
'simple',
|
|
16
|
-
'json',
|
|
17
|
-
'pjson',
|
|
18
|
-
'table',
|
|
19
|
-
'markdown',
|
|
20
|
-
'histogram'
|
|
21
|
-
])).addOption(new Option('--abs-threshold [absThreshold]', 'absolute error threshold in nanoseconds').argParser(parseFloat)).addOption(new Option('--rel-threshold [relThreshold]', 'relative error threshold (fraction between 0 and 1)').argParser(parseFloat)).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')).addOption(new Option('--progress', 'show progress bar during benchmark execution')).addOption(new Option('--save-baseline <file>', 'save benchmark results to baseline file')).addOption(new Option('--compare-baseline <file>', 'compare results against baseline file')).action(async (patterns, executeOptions)=>{
|
|
22
|
-
let baseline = null;
|
|
23
|
-
if (executeOptions.compareBaseline) {
|
|
24
|
-
try {
|
|
25
|
-
const content = await readFile(executeOptions.compareBaseline, 'utf8');
|
|
26
|
-
baseline = JSON.parse(content);
|
|
27
|
-
} catch {
|
|
28
|
-
console.error(`Warning: Could not load baseline file: ${executeOptions.compareBaseline}`);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
const files = new Set();
|
|
32
|
-
await Promise.all(patterns.map(async (pattern)=>{
|
|
33
|
-
const matches = await glob(pattern, {
|
|
34
|
-
absolute: true,
|
|
35
|
-
cwd: process.cwd()
|
|
36
|
-
}).catch(()=>[]);
|
|
37
|
-
matches.forEach((file)=>files.add(file));
|
|
38
|
-
}));
|
|
39
|
-
for (const file of files){
|
|
40
|
-
const stats = await stat(file).catch(()=>false);
|
|
41
|
-
if (stats && stats.isFile()) {
|
|
42
|
-
const content = await readFile(file, 'utf8');
|
|
43
|
-
const identifier = pathToFileURL(file).href;
|
|
44
|
-
const code = await transpile(content);
|
|
45
|
-
let instance;
|
|
46
|
-
const benchmark = (...args)=>{
|
|
47
|
-
if (instance) {
|
|
48
|
-
throw new Error('Only one benchmark per file is supported');
|
|
49
|
-
}
|
|
50
|
-
instance = Benchmark.create(...args);
|
|
51
|
-
return instance;
|
|
52
|
-
};
|
|
53
|
-
const globals = Object.create(null);
|
|
54
|
-
for (const k of Object.getOwnPropertyNames(globalThis)){
|
|
55
|
-
globals[k] = globalThis[k];
|
|
56
|
-
}
|
|
57
|
-
globals.benchmark = benchmark;
|
|
58
|
-
const script = new SourceTextModule(code, {
|
|
59
|
-
identifier,
|
|
60
|
-
context: createContext(globals),
|
|
61
|
-
initializeImportMeta (meta) {
|
|
62
|
-
meta.url = identifier;
|
|
63
|
-
},
|
|
64
|
-
async importModuleDynamically (specifier, referencingModule) {
|
|
65
|
-
if (Module.isBuiltin(specifier)) {
|
|
66
|
-
return import(specifier);
|
|
67
|
-
}
|
|
68
|
-
const baseIdentifier = referencingModule.identifier ?? identifier;
|
|
69
|
-
const resolveFrom = createRequire(fileURLToPath(baseIdentifier));
|
|
70
|
-
const resolved = resolveFrom.resolve(specifier);
|
|
71
|
-
return import(resolved);
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
const imports = new Map();
|
|
75
|
-
await script.link(async (specifier, referencingModule)=>{
|
|
76
|
-
const baseIdentifier = referencingModule.identifier ?? identifier;
|
|
77
|
-
const resolveFrom = createRequire(fileURLToPath(baseIdentifier));
|
|
78
|
-
const target = Module.isBuiltin(specifier) ? specifier : resolveFrom.resolve(specifier);
|
|
79
|
-
const cached = imports.get(target);
|
|
80
|
-
if (cached) {
|
|
81
|
-
return cached;
|
|
82
|
-
}
|
|
83
|
-
const mod = await import(target);
|
|
84
|
-
const exportNames = Object.keys(mod);
|
|
85
|
-
const imported = new SyntheticModule(exportNames, ()=>{
|
|
86
|
-
exportNames.forEach((key)=>imported.setExport(key, mod[key]));
|
|
87
|
-
}, {
|
|
88
|
-
identifier: target,
|
|
89
|
-
context: referencingModule.context
|
|
90
|
-
});
|
|
91
|
-
imports.set(target, imported);
|
|
92
|
-
return imported;
|
|
93
|
-
});
|
|
94
|
-
await script.evaluate();
|
|
95
|
-
if (instance) {
|
|
96
|
-
const reports = await instance.execute({
|
|
97
|
-
...executeOptions,
|
|
98
|
-
[BENCHMARK_URL]: identifier
|
|
99
|
-
});
|
|
100
|
-
if (executeOptions.saveBaseline) {
|
|
101
|
-
const baselineData = reportsToBaseline(reports);
|
|
102
|
-
await writeFile(executeOptions.saveBaseline, JSON.stringify(baselineData, null, 2));
|
|
103
|
-
console.log(`Baseline saved to: ${executeOptions.saveBaseline}`);
|
|
104
|
-
}
|
|
105
|
-
if (baseline) {
|
|
106
|
-
printComparisonReports(reports, baseline);
|
|
107
|
-
} else {
|
|
108
|
-
switch(executeOptions.format){
|
|
109
|
-
case 'json':
|
|
110
|
-
printJSONReports(reports);
|
|
111
|
-
break;
|
|
112
|
-
case 'pjson':
|
|
113
|
-
printJSONReports(reports, 2);
|
|
114
|
-
break;
|
|
115
|
-
case 'table':
|
|
116
|
-
printTableReports(reports);
|
|
117
|
-
break;
|
|
118
|
-
case 'markdown':
|
|
119
|
-
printMarkdownReports(reports);
|
|
120
|
-
break;
|
|
121
|
-
case 'histogram':
|
|
122
|
-
printHistogramReports(reports);
|
|
123
|
-
break;
|
|
124
|
-
default:
|
|
125
|
-
printSimpleReports(reports);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
commander.parse(process.argv);
|
|
133
|
-
|
|
134
|
-
//# sourceMappingURL=cli.js.map
|