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.
Files changed (57) hide show
  1. package/README.md +4 -15
  2. package/bin/overtake.js +1 -1
  3. package/build/executor.d.ts +7 -3
  4. package/build/index.d.ts +10 -11
  5. package/build/reporter.d.ts +10 -2
  6. package/build/runner.d.ts +1 -1
  7. package/build/types.d.ts +6 -6
  8. package/build/utils.d.ts +1 -17
  9. package/package.json +8 -26
  10. package/src/__tests__/assert-no-closure.ts +135 -0
  11. package/src/__tests__/benchmark-execute.ts +48 -0
  12. package/src/cli.ts +139 -144
  13. package/src/executor.ts +48 -18
  14. package/src/index.ts +85 -57
  15. package/src/reporter.ts +27 -19
  16. package/src/runner.ts +2 -5
  17. package/src/types.ts +8 -8
  18. package/src/utils.ts +15 -54
  19. package/src/worker.ts +6 -3
  20. package/tsconfig.json +3 -1
  21. package/build/cli.cjs +0 -179
  22. package/build/cli.cjs.map +0 -1
  23. package/build/cli.js +0 -134
  24. package/build/cli.js.map +0 -1
  25. package/build/executor.cjs +0 -123
  26. package/build/executor.cjs.map +0 -1
  27. package/build/executor.js +0 -113
  28. package/build/executor.js.map +0 -1
  29. package/build/gc-watcher.cjs +0 -30
  30. package/build/gc-watcher.cjs.map +0 -1
  31. package/build/gc-watcher.js +0 -20
  32. package/build/gc-watcher.js.map +0 -1
  33. package/build/index.cjs +0 -442
  34. package/build/index.cjs.map +0 -1
  35. package/build/index.js +0 -377
  36. package/build/index.js.map +0 -1
  37. package/build/reporter.cjs +0 -311
  38. package/build/reporter.cjs.map +0 -1
  39. package/build/reporter.js +0 -293
  40. package/build/reporter.js.map +0 -1
  41. package/build/runner.cjs +0 -532
  42. package/build/runner.cjs.map +0 -1
  43. package/build/runner.js +0 -522
  44. package/build/runner.js.map +0 -1
  45. package/build/types.cjs +0 -66
  46. package/build/types.cjs.map +0 -1
  47. package/build/types.js +0 -33
  48. package/build/types.js.map +0 -1
  49. package/build/utils.cjs +0 -174
  50. package/build/utils.cjs.map +0 -1
  51. package/build/utils.js +0 -132
  52. package/build/utils.js.map +0 -1
  53. package/build/worker.cjs +0 -155
  54. package/build/worker.cjs.map +0 -1
  55. package/build/worker.js +0 -110
  56. package/build/worker.js.map +0 -1
  57. 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.js';
2
- import { ReportType, DURATION_SCALE } from './types.js';
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
- constructor(
22
- public readonly type: ReportType,
23
- public readonly value: bigint,
24
- public readonly uncertainty: number = 0,
25
- public readonly scale: bigint = 1n,
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
- const computeStats = (durations: BigUint64Array) => {
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 } = computeStats(durations);
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 } = computeStats(durations);
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 } = computeStats(durations);
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 } = computeStats(durations);
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 } = computeStats(durations);
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 } = computeStats(durations);
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 } = computeStats(durations);
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 } = computeStats(durations);
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 } = computeStats(durations);
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.js';
3
- import { GCWatcher } from './gc-watcher.js';
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 enum Control {
103
- INDEX,
104
- PROGRESS,
105
- COMPLETE,
106
- HEAP_USED,
107
- }
108
-
109
- export const CONTROL_SLOTS = Object.values(Control).length / 2;
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 { transform, parseSync } from '@swc/core';
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.js';
7
- import { WorkerOptions } from './types.js';
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":"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"}
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