overtake 1.2.1 → 1.3.1
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 +260 -45
- package/build/cli.cjs +37 -17
- package/build/cli.cjs.map +1 -1
- package/build/cli.js +39 -19
- package/build/cli.js.map +1 -1
- package/build/executor.cjs +20 -0
- package/build/executor.cjs.map +1 -1
- package/build/executor.d.ts +2 -0
- package/build/executor.js +20 -0
- package/build/executor.js.map +1 -1
- package/build/index.cjs +121 -2
- package/build/index.cjs.map +1 -1
- package/build/index.d.ts +9 -0
- package/build/index.js +109 -2
- package/build/index.js.map +1 -1
- package/build/reporter.cjs +63 -0
- package/build/reporter.cjs.map +1 -1
- package/build/reporter.js +63 -0
- package/build/reporter.js.map +1 -1
- package/build/runner.cjs +29 -5
- package/build/runner.cjs.map +1 -1
- package/build/runner.js +29 -5
- package/build/runner.js.map +1 -1
- package/build/types.cjs +6 -1
- package/build/types.cjs.map +1 -1
- package/build/types.d.ts +3 -2
- package/build/types.js +6 -1
- package/build/types.js.map +1 -1
- package/context7.json +4 -0
- package/package.json +1 -1
- package/src/cli.ts +53 -18
- package/src/executor.ts +25 -2
- package/src/index.ts +123 -3
- package/src/reporter.ts +63 -0
- package/src/runner.ts +33 -5
- package/src/types.ts +22 -1
package/README.md
CHANGED
|
@@ -43,13 +43,20 @@ npx overtake benchmark.ts
|
|
|
43
43
|
|
|
44
44
|
**The Solution**: Overtake runs every benchmark in an isolated worker thread with a fresh V8 context. No contamination. No lies.
|
|
45
45
|
|
|
46
|
-
| Feature
|
|
47
|
-
|
|
|
48
|
-
| Worker isolation
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
46
|
+
| Feature | Overtake | Benchmark.js | Tinybench |
|
|
47
|
+
| ------------------------- | ---------------------------- | ----------------- | ----------------- |
|
|
48
|
+
| Worker isolation | ✅ Each benchmark isolated | ❌ Shared context | ❌ Shared context |
|
|
49
|
+
| GC interference detection | ✅ Discards affected samples | ❌ | ❌ |
|
|
50
|
+
| Outlier filtering | ✅ IQR-based automatic | ❌ | ❌ |
|
|
51
|
+
| Adaptive batch sizing | ✅ Auto-tuned | ❌ | ❌ |
|
|
52
|
+
| Statistical convergence | ✅ Auto-adjusts cycles | ⚠️ Manual config | ⚠️ Manual config |
|
|
53
|
+
| Memory tracking | ✅ heapUsedKB | ❌ | ❌ |
|
|
54
|
+
| DCE detection | ✅ Warning | ❌ | ❌ |
|
|
55
|
+
| Baseline comparison | ✅ CLI flag | ❌ | ❌ |
|
|
56
|
+
| Progress bar | ✅ --progress | ❌ | ❌ |
|
|
57
|
+
| Zero-copy timing | ✅ SharedArrayBuffer | ❌ Serialization | ❌ Serialization |
|
|
58
|
+
| TypeScript support | ✅ Built-in | ❌ Manual setup | ⚠️ Needs config |
|
|
59
|
+
| Active maintenance | ✅ 2025 | ❌ Archived 2017 | ✅ 2025 |
|
|
53
60
|
|
|
54
61
|
## Core Concepts
|
|
55
62
|
|
|
@@ -105,10 +112,12 @@ benchmark('local', () => 1)
|
|
|
105
112
|
|
|
106
113
|
// Programmatic usage – provide baseUrl
|
|
107
114
|
const suite = new Benchmark('local');
|
|
108
|
-
suite
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
115
|
+
suite
|
|
116
|
+
.target('helper', async () => {
|
|
117
|
+
const { helper } = await import('./helpers.js');
|
|
118
|
+
return { helper };
|
|
119
|
+
})
|
|
120
|
+
.measure('use helper', ({ helper }) => helper());
|
|
112
121
|
await suite.execute({ baseUrl: import.meta.url });
|
|
113
122
|
```
|
|
114
123
|
|
|
@@ -120,16 +129,15 @@ When using `npx overtake`, a global `benchmark` function is provided:
|
|
|
120
129
|
|
|
121
130
|
```typescript
|
|
122
131
|
// benchmark.ts - No imports needed!
|
|
123
|
-
benchmark('small', () => generateSmallData())
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
});
|
|
132
|
+
const suite = benchmark('small', () => generateSmallData()).feed('large', () => generateLargeData());
|
|
133
|
+
|
|
134
|
+
suite.target('algorithm A').measure('process', (_, input) => {
|
|
135
|
+
processA(input);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
suite.target('algorithm B').measure('process', (_, input) => {
|
|
139
|
+
processB(input);
|
|
140
|
+
});
|
|
133
141
|
```
|
|
134
142
|
|
|
135
143
|
```bash
|
|
@@ -164,21 +172,32 @@ printTableReports(reports);
|
|
|
164
172
|
|
|
165
173
|
```typescript
|
|
166
174
|
// Create with initial feed
|
|
167
|
-
benchmark('initial data', () => data)
|
|
168
|
-
.feed('more data', () => moreData) // Add more datasets
|
|
175
|
+
const suite = benchmark('initial data', () => data).feed('more data', () => moreData); // Add more datasets
|
|
169
176
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
})
|
|
177
|
+
// Define what to compare
|
|
178
|
+
suite.target('implementation A').measure('operation', (ctx, input) => {
|
|
179
|
+
/* ... */
|
|
180
|
+
});
|
|
175
181
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
});
|
|
182
|
+
suite.target('implementation B').measure('operation', (ctx, input) => {
|
|
183
|
+
/* ... */
|
|
184
|
+
});
|
|
180
185
|
```
|
|
181
186
|
|
|
187
|
+
### Method Chaining Reference
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
benchmark(name, feedFn) -> Benchmark
|
|
191
|
+
.feed(name, feedFn) -> Benchmark
|
|
192
|
+
.target(name, setup?) -> Target
|
|
193
|
+
.teardown(fn) -> Target
|
|
194
|
+
.measure(name, fn) -> Measure
|
|
195
|
+
.pre(fn) -> Measure
|
|
196
|
+
.post(fn) -> Measure
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Note: `.measure()` returns `Measure`, not `Benchmark`. To add multiple targets, call `suite.target()` separately for each.
|
|
200
|
+
|
|
182
201
|
### Targets with Setup
|
|
183
202
|
|
|
184
203
|
```typescript
|
|
@@ -236,12 +255,18 @@ sumBenchmark.target('reduce').measure('sum', (_, numbers) => {
|
|
|
236
255
|
|
|
237
256
|
```typescript
|
|
238
257
|
// examples/imports.ts - Correct way to import local files
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
258
|
+
const suite = benchmark('local modules', () => testData);
|
|
259
|
+
|
|
260
|
+
suite
|
|
261
|
+
.target('local files', async () => {
|
|
262
|
+
const { join } = await import('node:path');
|
|
263
|
+
const modulePath = join(process.cwd(), './build/myModule.js');
|
|
264
|
+
const { myFunction } = await import(modulePath);
|
|
265
|
+
return { myFunction };
|
|
266
|
+
})
|
|
267
|
+
.measure('call function', ({ myFunction }, input) => {
|
|
268
|
+
myFunction(input);
|
|
269
|
+
});
|
|
245
270
|
```
|
|
246
271
|
|
|
247
272
|
**[📁 See all examples](./examples/):**
|
|
@@ -257,13 +282,20 @@ sumBenchmark.target('reduce').measure('sum', (_, numbers) => {
|
|
|
257
282
|
npx overtake <pattern> [options]
|
|
258
283
|
```
|
|
259
284
|
|
|
260
|
-
| Option
|
|
261
|
-
|
|
|
262
|
-
| `--format`
|
|
263
|
-
| `--report-types`
|
|
264
|
-
| `--workers`
|
|
265
|
-
| `--min-cycles`
|
|
266
|
-
| `--max-cycles`
|
|
285
|
+
| Option | Short | Description | Default |
|
|
286
|
+
| -------------------- | ----- | ----------------------------------------------------- | --------- |
|
|
287
|
+
| `--format` | `-f` | Output format (see [Output Formats](#output-formats)) | `simple` |
|
|
288
|
+
| `--report-types` | `-r` | Stats to show (see [Metrics](#available-metrics)) | `['ops']` |
|
|
289
|
+
| `--workers` | `-w` | Concurrent workers | CPU count |
|
|
290
|
+
| `--min-cycles` | | Minimum measurement iterations | 50 |
|
|
291
|
+
| `--max-cycles` | | Maximum measurement iterations | 1000 |
|
|
292
|
+
| `--warmup-cycles` | | Warmup iterations before measuring | 20 |
|
|
293
|
+
| `--abs-threshold` | | Absolute error threshold (nanoseconds) | 1000 |
|
|
294
|
+
| `--rel-threshold` | | Relative error threshold (0-1) | 0.02 |
|
|
295
|
+
| `--no-gc-observer` | | Disable GC overlap detection | enabled |
|
|
296
|
+
| `--progress` | | Show progress bar during execution | disabled |
|
|
297
|
+
| `--save-baseline` | | Save results to baseline file | - |
|
|
298
|
+
| `--compare-baseline` | | Compare against baseline file | - |
|
|
267
299
|
|
|
268
300
|
### Example Commands
|
|
269
301
|
|
|
@@ -276,8 +308,191 @@ npx overtake bench.ts -r ops mean p95 p99
|
|
|
276
308
|
|
|
277
309
|
# Output JSON for CI
|
|
278
310
|
npx overtake bench.ts -f json > results.json
|
|
311
|
+
|
|
312
|
+
# Show progress bar for long benchmarks
|
|
313
|
+
npx overtake bench.ts --progress
|
|
314
|
+
|
|
315
|
+
# Markdown output for docs/PRs
|
|
316
|
+
npx overtake bench.ts -f markdown
|
|
317
|
+
|
|
318
|
+
# ASCII histogram chart
|
|
319
|
+
npx overtake bench.ts -f histogram
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Output Formats
|
|
323
|
+
|
|
324
|
+
| Format | Description |
|
|
325
|
+
| ----------- | -------------------------------- |
|
|
326
|
+
| `simple` | Grouped console output (default) |
|
|
327
|
+
| `table` | Console table format |
|
|
328
|
+
| `json` | Compact JSON |
|
|
329
|
+
| `pjson` | Pretty-printed JSON |
|
|
330
|
+
| `markdown` | Markdown table for docs/PRs |
|
|
331
|
+
| `histogram` | ASCII bar chart comparing ops/s |
|
|
332
|
+
|
|
333
|
+
**Markdown example:**
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
npx overtake bench.ts -f markdown
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
```markdown
|
|
340
|
+
## for loop - sum
|
|
341
|
+
|
|
342
|
+
| Feed | ops |
|
|
343
|
+
| ---------- | --------------------- |
|
|
344
|
+
| 1M numbers | 2,189 ops/s +/- 0.17% |
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**Histogram example:**
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
npx overtake bench.ts -f histogram
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
```
|
|
354
|
+
for loop - sum
|
|
355
|
+
|
|
356
|
+
1M numbers | ████████████████████████████████████████ 2,189 ops/s
|
|
357
|
+
|
|
358
|
+
reduce - sum
|
|
359
|
+
|
|
360
|
+
1M numbers | ████ 233 ops/s
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Available Metrics
|
|
364
|
+
|
|
365
|
+
Specify with `--report-types` or `reportTypes` option.
|
|
366
|
+
|
|
367
|
+
### Core Metrics
|
|
368
|
+
|
|
369
|
+
| Metric | Description |
|
|
370
|
+
| ------------- | ------------------------------- |
|
|
371
|
+
| `ops` | Operations per second (default) |
|
|
372
|
+
| `mean` | Average duration |
|
|
373
|
+
| `median` | Middle value (p50) |
|
|
374
|
+
| `min` / `max` | Range bounds |
|
|
375
|
+
| `mode` | Most frequent duration |
|
|
376
|
+
|
|
377
|
+
### Dispersion Metrics
|
|
378
|
+
|
|
379
|
+
| Metric | Description |
|
|
380
|
+
| ---------- | ------------------------- |
|
|
381
|
+
| `sd` | Standard deviation |
|
|
382
|
+
| `variance` | Statistical variance |
|
|
383
|
+
| `sem` | Standard error of mean |
|
|
384
|
+
| `mad` | Median absolute deviation |
|
|
385
|
+
| `iqr` | Interquartile range |
|
|
386
|
+
|
|
387
|
+
### Confidence Metrics
|
|
388
|
+
|
|
389
|
+
| Metric | Description |
|
|
390
|
+
| ---------- | ---------------------------- |
|
|
391
|
+
| `moe` | Margin of error (95% CI) |
|
|
392
|
+
| `rme` | Relative margin of error (%) |
|
|
393
|
+
| `ci_lower` | Lower bound of 95% CI |
|
|
394
|
+
| `ci_upper` | Upper bound of 95% CI |
|
|
395
|
+
|
|
396
|
+
### Percentiles
|
|
397
|
+
|
|
398
|
+
`p1` through `p99` - any percentile
|
|
399
|
+
|
|
400
|
+
**Example:**
|
|
401
|
+
|
|
402
|
+
```bash
|
|
403
|
+
npx overtake bench.ts -r ops mean sd rme p50 p95 p99
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## Baseline Comparison
|
|
407
|
+
|
|
408
|
+
Track performance regressions by saving and comparing baselines:
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
# Save current results as baseline
|
|
412
|
+
npx overtake bench.ts --save-baseline baseline.json
|
|
413
|
+
|
|
414
|
+
# Later, compare against baseline
|
|
415
|
+
npx overtake bench.ts --compare-baseline baseline.json
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Output shows:**
|
|
419
|
+
|
|
420
|
+
- `+` Green: Performance improved (>5% better)
|
|
421
|
+
- `!` Red: Performance regressed (>5% worse)
|
|
422
|
+
- No indicator: Within threshold
|
|
423
|
+
|
|
424
|
+
**CI usage:**
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
# In CI, fail if regression detected
|
|
428
|
+
npx overtake bench.ts --compare-baseline main-baseline.json
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## Additional Output Information
|
|
432
|
+
|
|
433
|
+
### Memory Tracking
|
|
434
|
+
|
|
435
|
+
Each benchmark reports heap memory delta:
|
|
436
|
+
|
|
437
|
+
```
|
|
438
|
+
1M numbers ops: 233 ops/s +/- 0.13% (heap: 1794KB)
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
This indicates memory allocated during the benchmark run.
|
|
442
|
+
|
|
443
|
+
### DCE Warning
|
|
444
|
+
|
|
445
|
+
If you see `[DCE warning]`, V8 may have eliminated your benchmark code:
|
|
446
|
+
|
|
447
|
+
```
|
|
448
|
+
1M numbers ops: 5,000,000,000 ops/s [DCE warning]
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
**Solutions:**
|
|
452
|
+
|
|
453
|
+
1. Ensure your function returns a value
|
|
454
|
+
2. Use the provided input data
|
|
455
|
+
3. Have observable side effects
|
|
456
|
+
|
|
457
|
+
The benchmark internally uses atomic operations to prevent DCE, but extremely simple operations may still trigger this warning.
|
|
458
|
+
|
|
459
|
+
## Advanced Configuration
|
|
460
|
+
|
|
461
|
+
### Environment Variables
|
|
462
|
+
|
|
463
|
+
| Variable | Description |
|
|
464
|
+
| -------------------------- | ----------------------------------------- |
|
|
465
|
+
| `OVERTAKE_PERTURB_INPUT=1` | Add nonce to inputs (defeats JIT caching) |
|
|
466
|
+
|
|
467
|
+
### Node.js Flags
|
|
468
|
+
|
|
469
|
+
The CLI automatically enables these flags:
|
|
470
|
+
|
|
471
|
+
- `--experimental-vm-modules` - Required for worker isolation
|
|
472
|
+
- `--expose-gc` - Enables explicit GC between samples
|
|
473
|
+
- `--no-warnings` - Suppresses experimental warnings
|
|
474
|
+
|
|
475
|
+
### Programmatic Options
|
|
476
|
+
|
|
477
|
+
```typescript
|
|
478
|
+
const reports = await suite.execute({
|
|
479
|
+
workers: 4, // Concurrent workers
|
|
480
|
+
warmupCycles: 20, // Warmup iterations
|
|
481
|
+
minCycles: 50, // Minimum measurement iterations
|
|
482
|
+
maxCycles: 1000, // Maximum measurement iterations
|
|
483
|
+
absThreshold: 1_000, // Stop if stddev < 1us
|
|
484
|
+
relThreshold: 0.02, // Stop if CoV < 2%
|
|
485
|
+
gcObserver: true, // Discard GC-affected samples
|
|
486
|
+
reportTypes: ['ops', 'mean', 'p95'],
|
|
487
|
+
progress: true, // Show progress bar
|
|
488
|
+
progressInterval: 100, // Progress update interval (ms)
|
|
489
|
+
});
|
|
279
490
|
```
|
|
280
491
|
|
|
492
|
+
### One Benchmark Per File
|
|
493
|
+
|
|
494
|
+
CLI mode enforces one benchmark per file. Calling `benchmark()` twice throws an error. For multiple benchmarks, use separate files or programmatic mode.
|
|
495
|
+
|
|
281
496
|
## Troubleshooting
|
|
282
497
|
|
|
283
498
|
### "Cannot find module" in worker
|
package/build/cli.cjs
CHANGED
|
@@ -60,8 +60,19 @@ commander.name(name).description(description).version(version).argument('<paths.
|
|
|
60
60
|
'simple',
|
|
61
61
|
'json',
|
|
62
62
|
'pjson',
|
|
63
|
-
'table'
|
|
64
|
-
|
|
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
|
+
}
|
|
65
76
|
const files = new Set();
|
|
66
77
|
await Promise.all(patterns.map(async (pattern)=>{
|
|
67
78
|
const matches = await (0, _glob.glob)(pattern, {
|
|
@@ -130,24 +141,33 @@ commander.name(name).description(description).version(version).argument('<paths.
|
|
|
130
141
|
...executeOptions,
|
|
131
142
|
[BENCHMARK_URL]: identifier
|
|
132
143
|
});
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
144
|
+
if (executeOptions.saveBaseline) {
|
|
145
|
+
const baselineData = (0, _indexcjs.reportsToBaseline)(reports);
|
|
146
|
+
await (0, _promises.writeFile)(executeOptions.saveBaseline, JSON.stringify(baselineData, null, 2));
|
|
147
|
+
console.log(`Baseline saved to: ${executeOptions.saveBaseline}`);
|
|
148
|
+
}
|
|
149
|
+
if (baseline) {
|
|
150
|
+
(0, _indexcjs.printComparisonReports)(reports, baseline);
|
|
151
|
+
} else {
|
|
152
|
+
switch(executeOptions.format){
|
|
153
|
+
case 'json':
|
|
136
154
|
(0, _indexcjs.printJSONReports)(reports);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
case 'pjson':
|
|
140
|
-
{
|
|
155
|
+
break;
|
|
156
|
+
case 'pjson':
|
|
141
157
|
(0, _indexcjs.printJSONReports)(reports, 2);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
case 'table':
|
|
145
|
-
{
|
|
158
|
+
break;
|
|
159
|
+
case 'table':
|
|
146
160
|
(0, _indexcjs.printTableReports)(reports);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
161
|
+
break;
|
|
162
|
+
case 'markdown':
|
|
163
|
+
(0, _indexcjs.printMarkdownReports)(reports);
|
|
164
|
+
break;
|
|
165
|
+
case 'histogram':
|
|
166
|
+
(0, _indexcjs.printHistogramReports)(reports);
|
|
167
|
+
break;
|
|
168
|
+
default:
|
|
169
|
+
(0, _indexcjs.printSimpleReports)(reports);
|
|
170
|
+
}
|
|
151
171
|
}
|
|
152
172
|
}
|
|
153
173
|
}
|
package/build/cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { createRequire, Module } from 'node:module';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { SyntheticModule, createContext, SourceTextModule } from 'node:vm';\nimport { stat, readFile } from 'node:fs/promises';\nimport { Command, Option } from 'commander';\nimport { glob } from 'glob';\nimport { Benchmark, printTableReports, printJSONReports, printSimpleReports, DEFAULT_REPORT_TYPES, DEFAULT_WORKERS } from './index.js';\nimport { transpile } from './utils.js';\nimport { REPORT_TYPES } from './types.js';\n\nconst require = createRequire(import.meta.url);\nconst { name, description, version } = require('../package.json');\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']))\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 .action(async (patterns: string[], executeOptions) => {\n const files = new Set<string>();\n await Promise.all(\n patterns.map(async (pattern) => {\n const matches = await glob(pattern, { absolute: true, cwd: process.cwd() }).catch(() => []);\n matches.forEach((file) => files.add(file));\n }),\n );\n\n for (const file of files) {\n const stats = await stat(file).catch(() => false as const);\n if (stats && stats.isFile()) {\n const content = await readFile(file, 'utf8');\n const identifier = pathToFileURL(file).href;\n const code = await transpile(content);\n let instance: Benchmark<unknown> | undefined;\n const benchmark = (...args: Parameters<(typeof Benchmark)['create']>) => {\n if (instance) {\n throw new Error('Only one benchmark per file is supported');\n }\n instance = Benchmark.create(...args);\n return instance;\n };\n const script = new SourceTextModule(code, {\n identifier,\n context: createContext({\n benchmark,\n Buffer,\n console,\n }),\n initializeImportMeta(meta) {\n meta.url = identifier;\n },\n async importModuleDynamically(specifier, referencingModule) {\n if (Module.isBuiltin(specifier)) {\n return import(specifier);\n }\n const baseIdentifier = referencingModule.identifier ?? identifier;\n const resolveFrom = createRequire(fileURLToPath(baseIdentifier));\n const resolved = resolveFrom.resolve(specifier);\n return import(resolved);\n },\n });\n const imports = new Map<string, SyntheticModule>();\n await script.link(async (specifier: string, referencingModule) => {\n const baseIdentifier = referencingModule.identifier ?? identifier;\n const resolveFrom = createRequire(fileURLToPath(baseIdentifier));\n const target = Module.isBuiltin(specifier) ? specifier : resolveFrom.resolve(specifier);\n const cached = imports.get(target);\n if (cached) {\n return cached;\n }\n const mod = await import(target);\n const exportNames = Object.keys(mod);\n const imported = new SyntheticModule(\n exportNames,\n () => {\n exportNames.forEach((key) => imported.setExport(key, mod[key]));\n },\n { identifier: target, context: referencingModule.context },\n );\n\n imports.set(target, imported);\n return imported;\n });\n await script.evaluate();\n\n if (instance) {\n const reports = await instance.execute({\n ...executeOptions,\n [BENCHMARK_URL]: identifier,\n } as typeof executeOptions);\n switch (executeOptions.format) {\n case 'json':\n {\n printJSONReports(reports);\n }\n break;\n case 'pjson':\n {\n printJSONReports(reports, 2);\n }\n break;\n case 'table':\n {\n printTableReports(reports);\n }\n break;\n default:\n printSimpleReports(reports);\n }\n }\n }\n }\n });\n\ncommander.parse(process.argv);\n"],"names":["require","createRequire","name","description","version","BENCHMARK_URL","Symbol","for","commander","Command","argument","addOption","Option","choices","REPORT_TYPES","default","DEFAULT_REPORT_TYPES","DEFAULT_WORKERS","argParser","parseInt","parseFloat","action","patterns","executeOptions","files","Set","Promise","all","map","pattern","matches","glob","absolute","cwd","process","catch","forEach","file","add","stats","stat","isFile","content","readFile","identifier","pathToFileURL","href","code","transpile","instance","benchmark","args","Error","Benchmark","create","script","SourceTextModule","context","createContext","Buffer","console","initializeImportMeta","meta","url","importModuleDynamically","specifier","referencingModule","Module","isBuiltin","baseIdentifier","resolveFrom","fileURLToPath","resolved","resolve","imports","Map","link","target","cached","get","mod","exportNames","Object","keys","imported","SyntheticModule","key","setExport","set","evaluate","reports","execute","format","printJSONReports","printTableReports","printSimpleReports","parse","argv"],"mappings":"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"}
|
|
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 script = new SourceTextModule(code, {\n identifier,\n context: createContext({\n benchmark,\n Buffer,\n console,\n }),\n initializeImportMeta(meta) {\n meta.url = identifier;\n },\n async importModuleDynamically(specifier, referencingModule) {\n if (Module.isBuiltin(specifier)) {\n return import(specifier);\n }\n const baseIdentifier = referencingModule.identifier ?? identifier;\n const resolveFrom = createRequire(fileURLToPath(baseIdentifier));\n const resolved = resolveFrom.resolve(specifier);\n return import(resolved);\n },\n });\n const imports = new Map<string, SyntheticModule>();\n await script.link(async (specifier: string, referencingModule) => {\n const baseIdentifier = referencingModule.identifier ?? identifier;\n const resolveFrom = createRequire(fileURLToPath(baseIdentifier));\n const target = Module.isBuiltin(specifier) ? specifier : resolveFrom.resolve(specifier);\n const cached = imports.get(target);\n if (cached) {\n return cached;\n }\n const mod = await import(target);\n const exportNames = Object.keys(mod);\n const imported = new SyntheticModule(\n exportNames,\n () => {\n exportNames.forEach((key) => imported.setExport(key, mod[key]));\n },\n { identifier: target, context: referencingModule.context },\n );\n\n imports.set(target, imported);\n return imported;\n });\n await script.evaluate();\n\n if (instance) {\n const reports = await instance.execute({\n ...executeOptions,\n [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","script","SourceTextModule","context","createContext","Buffer","initializeImportMeta","meta","url","importModuleDynamically","specifier","referencingModule","Module","isBuiltin","baseIdentifier","resolveFrom","fileURLToPath","resolved","resolve","imports","Map","link","target","cached","get","mod","exportNames","Object","keys","imported","SyntheticModule","key","setExport","set","evaluate","reports","execute","saveBaseline","baselineData","reportsToBaseline","writeFile","stringify","log","printComparisonReports","format","printJSONReports","printTableReports","printMarkdownReports","printHistogramReports","printSimpleReports","argv"],"mappings":"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"}
|
package/build/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createRequire, Module } from 'node:module';
|
|
2
2
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
3
3
|
import { SyntheticModule, createContext, SourceTextModule } from 'node:vm';
|
|
4
|
-
import { stat, readFile } from 'node:fs/promises';
|
|
4
|
+
import { stat, readFile, writeFile } from 'node:fs/promises';
|
|
5
5
|
import { Command, Option } from 'commander';
|
|
6
6
|
import { glob } from 'glob';
|
|
7
|
-
import { Benchmark, printTableReports, printJSONReports, printSimpleReports, DEFAULT_REPORT_TYPES, DEFAULT_WORKERS } from "./index.js";
|
|
7
|
+
import { Benchmark, printTableReports, printJSONReports, printSimpleReports, printMarkdownReports, printHistogramReports, printComparisonReports, reportsToBaseline, DEFAULT_REPORT_TYPES, DEFAULT_WORKERS } from "./index.js";
|
|
8
8
|
import { transpile } from "./utils.js";
|
|
9
9
|
import { REPORT_TYPES } from "./types.js";
|
|
10
10
|
const require = createRequire(import.meta.url);
|
|
@@ -15,8 +15,19 @@ commander.name(name).description(description).version(version).argument('<paths.
|
|
|
15
15
|
'simple',
|
|
16
16
|
'json',
|
|
17
17
|
'pjson',
|
|
18
|
-
'table'
|
|
19
|
-
|
|
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
|
+
}
|
|
20
31
|
const files = new Set();
|
|
21
32
|
await Promise.all(patterns.map(async (pattern)=>{
|
|
22
33
|
const matches = await glob(pattern, {
|
|
@@ -85,24 +96,33 @@ commander.name(name).description(description).version(version).argument('<paths.
|
|
|
85
96
|
...executeOptions,
|
|
86
97
|
[BENCHMARK_URL]: identifier
|
|
87
98
|
});
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
99
|
+
if (executeOptions.saveBaseline) {
|
|
100
|
+
const baselineData = reportsToBaseline(reports);
|
|
101
|
+
await writeFile(executeOptions.saveBaseline, JSON.stringify(baselineData, null, 2));
|
|
102
|
+
console.log(`Baseline saved to: ${executeOptions.saveBaseline}`);
|
|
103
|
+
}
|
|
104
|
+
if (baseline) {
|
|
105
|
+
printComparisonReports(reports, baseline);
|
|
106
|
+
} else {
|
|
107
|
+
switch(executeOptions.format){
|
|
108
|
+
case 'json':
|
|
91
109
|
printJSONReports(reports);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
case 'pjson':
|
|
95
|
-
{
|
|
110
|
+
break;
|
|
111
|
+
case 'pjson':
|
|
96
112
|
printJSONReports(reports, 2);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
case 'table':
|
|
100
|
-
{
|
|
113
|
+
break;
|
|
114
|
+
case 'table':
|
|
101
115
|
printTableReports(reports);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
116
|
+
break;
|
|
117
|
+
case 'markdown':
|
|
118
|
+
printMarkdownReports(reports);
|
|
119
|
+
break;
|
|
120
|
+
case 'histogram':
|
|
121
|
+
printHistogramReports(reports);
|
|
122
|
+
break;
|
|
123
|
+
default:
|
|
124
|
+
printSimpleReports(reports);
|
|
125
|
+
}
|
|
106
126
|
}
|
|
107
127
|
}
|
|
108
128
|
}
|
package/build/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { createRequire, Module } from 'node:module';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { SyntheticModule, createContext, SourceTextModule } from 'node:vm';\nimport { stat, readFile } from 'node:fs/promises';\nimport { Command, Option } from 'commander';\nimport { glob } from 'glob';\nimport { Benchmark, printTableReports, printJSONReports, printSimpleReports, DEFAULT_REPORT_TYPES, DEFAULT_WORKERS } from './index.js';\nimport { transpile } from './utils.js';\nimport { REPORT_TYPES } from './types.js';\n\nconst require = createRequire(import.meta.url);\nconst { name, description, version } = require('../package.json');\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']))\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 .action(async (patterns: string[], executeOptions) => {\n const files = new Set<string>();\n await Promise.all(\n patterns.map(async (pattern) => {\n const matches = await glob(pattern, { absolute: true, cwd: process.cwd() }).catch(() => []);\n matches.forEach((file) => files.add(file));\n }),\n );\n\n for (const file of files) {\n const stats = await stat(file).catch(() => false as const);\n if (stats && stats.isFile()) {\n const content = await readFile(file, 'utf8');\n const identifier = pathToFileURL(file).href;\n const code = await transpile(content);\n let instance: Benchmark<unknown> | undefined;\n const benchmark = (...args: Parameters<(typeof Benchmark)['create']>) => {\n if (instance) {\n throw new Error('Only one benchmark per file is supported');\n }\n instance = Benchmark.create(...args);\n return instance;\n };\n const script = new SourceTextModule(code, {\n identifier,\n context: createContext({\n benchmark,\n Buffer,\n console,\n }),\n initializeImportMeta(meta) {\n meta.url = identifier;\n },\n async importModuleDynamically(specifier, referencingModule) {\n if (Module.isBuiltin(specifier)) {\n return import(specifier);\n }\n const baseIdentifier = referencingModule.identifier ?? identifier;\n const resolveFrom = createRequire(fileURLToPath(baseIdentifier));\n const resolved = resolveFrom.resolve(specifier);\n return import(resolved);\n },\n });\n const imports = new Map<string, SyntheticModule>();\n await script.link(async (specifier: string, referencingModule) => {\n const baseIdentifier = referencingModule.identifier ?? identifier;\n const resolveFrom = createRequire(fileURLToPath(baseIdentifier));\n const target = Module.isBuiltin(specifier) ? specifier : resolveFrom.resolve(specifier);\n const cached = imports.get(target);\n if (cached) {\n return cached;\n }\n const mod = await import(target);\n const exportNames = Object.keys(mod);\n const imported = new SyntheticModule(\n exportNames,\n () => {\n exportNames.forEach((key) => imported.setExport(key, mod[key]));\n },\n { identifier: target, context: referencingModule.context },\n );\n\n imports.set(target, imported);\n return imported;\n });\n await script.evaluate();\n\n if (instance) {\n const reports = await instance.execute({\n ...executeOptions,\n [BENCHMARK_URL]: identifier,\n } as typeof executeOptions);\n switch (executeOptions.format) {\n case 'json':\n {\n printJSONReports(reports);\n }\n break;\n case 'pjson':\n {\n printJSONReports(reports, 2);\n }\n break;\n case 'table':\n {\n printTableReports(reports);\n }\n break;\n default:\n printSimpleReports(reports);\n }\n }\n }\n }\n });\n\ncommander.parse(process.argv);\n"],"names":["createRequire","Module","fileURLToPath","pathToFileURL","SyntheticModule","createContext","SourceTextModule","stat","readFile","Command","Option","glob","Benchmark","printTableReports","printJSONReports","printSimpleReports","DEFAULT_REPORT_TYPES","DEFAULT_WORKERS","transpile","REPORT_TYPES","require","url","name","description","version","BENCHMARK_URL","Symbol","for","commander","argument","addOption","choices","default","argParser","parseInt","parseFloat","action","patterns","executeOptions","files","Set","Promise","all","map","pattern","matches","absolute","cwd","process","catch","forEach","file","add","stats","isFile","content","identifier","href","code","instance","benchmark","args","Error","create","script","context","Buffer","console","initializeImportMeta","meta","importModuleDynamically","specifier","referencingModule","isBuiltin","baseIdentifier","resolveFrom","resolved","resolve","imports","Map","link","target","cached","get","mod","exportNames","Object","keys","imported","key","setExport","set","evaluate","reports","execute","format","parse","argv"],"mappings":"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"}
|
|
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 script = new SourceTextModule(code, {\n identifier,\n context: createContext({\n benchmark,\n Buffer,\n console,\n }),\n initializeImportMeta(meta) {\n meta.url = identifier;\n },\n async importModuleDynamically(specifier, referencingModule) {\n if (Module.isBuiltin(specifier)) {\n return import(specifier);\n }\n const baseIdentifier = referencingModule.identifier ?? identifier;\n const resolveFrom = createRequire(fileURLToPath(baseIdentifier));\n const resolved = resolveFrom.resolve(specifier);\n return import(resolved);\n },\n });\n const imports = new Map<string, SyntheticModule>();\n await script.link(async (specifier: string, referencingModule) => {\n const baseIdentifier = referencingModule.identifier ?? identifier;\n const resolveFrom = createRequire(fileURLToPath(baseIdentifier));\n const target = Module.isBuiltin(specifier) ? specifier : resolveFrom.resolve(specifier);\n const cached = imports.get(target);\n if (cached) {\n return cached;\n }\n const mod = await import(target);\n const exportNames = Object.keys(mod);\n const imported = new SyntheticModule(\n exportNames,\n () => {\n exportNames.forEach((key) => imported.setExport(key, mod[key]));\n },\n { identifier: target, context: referencingModule.context },\n );\n\n imports.set(target, imported);\n return imported;\n });\n await script.evaluate();\n\n if (instance) {\n const reports = await instance.execute({\n ...executeOptions,\n [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":["createRequire","Module","fileURLToPath","pathToFileURL","SyntheticModule","createContext","SourceTextModule","stat","readFile","writeFile","Command","Option","glob","Benchmark","printTableReports","printJSONReports","printSimpleReports","printMarkdownReports","printHistogramReports","printComparisonReports","reportsToBaseline","DEFAULT_REPORT_TYPES","DEFAULT_WORKERS","transpile","REPORT_TYPES","require","url","name","description","version","BENCHMARK_URL","Symbol","for","commander","argument","addOption","choices","default","argParser","parseInt","parseFloat","action","patterns","executeOptions","baseline","compareBaseline","content","JSON","parse","console","error","files","Set","Promise","all","map","pattern","matches","absolute","cwd","process","catch","forEach","file","add","stats","isFile","identifier","href","code","instance","benchmark","args","Error","create","script","context","Buffer","initializeImportMeta","meta","importModuleDynamically","specifier","referencingModule","isBuiltin","baseIdentifier","resolveFrom","resolved","resolve","imports","Map","link","target","cached","get","mod","exportNames","Object","keys","imported","key","setExport","set","evaluate","reports","execute","saveBaseline","baselineData","stringify","log","format","argv"],"mappings":"AAAA,SAASA,aAAa,EAAEC,MAAM,QAAQ,cAAc;AACpD,SAASC,aAAa,EAAEC,aAAa,QAAQ,WAAW;AACxD,SAASC,eAAe,EAAEC,aAAa,EAAEC,gBAAgB,QAAQ,UAAU;AAC3E,SAASC,IAAI,EAAEC,QAAQ,EAAEC,SAAS,QAAQ,mBAAmB;AAC7D,SAASC,OAAO,EAAEC,MAAM,QAAQ,YAAY;AAC5C,SAASC,IAAI,QAAQ,OAAO;AAC5B,SACEC,SAAS,EACTC,iBAAiB,EACjBC,gBAAgB,EAChBC,kBAAkB,EAClBC,oBAAoB,EACpBC,qBAAqB,EACrBC,sBAAsB,EACtBC,iBAAiB,EAEjBC,oBAAoB,EACpBC,eAAe,QACV,aAAa;AACpB,SAASC,SAAS,QAAQ,aAAa;AACvC,SAASC,YAAY,QAAQ,aAAa;AAE1C,MAAMC,UAAUzB,cAAc,YAAY0B,GAAG;AAC7C,MAAM,EAAEC,IAAI,EAAEC,WAAW,EAAEC,OAAO,EAAE,GAAGJ,QAAQ;AAC/C,MAAMK,gBAAgBC,OAAOC,GAAG,CAAC;AAEjC,MAAMC,YAAY,IAAIvB;AAEtBuB,UACGN,IAAI,CAACA,MACLC,WAAW,CAACA,aACZC,OAAO,CAACA,SACRK,QAAQ,CAAC,cAAc,mCACvBC,SAAS,CAAC,IAAIxB,OAAO,uCAAuC,4CAA4CyB,OAAO,CAACZ,cAAca,OAAO,CAAChB,uBACtIc,SAAS,CAAC,IAAIxB,OAAO,2BAA2B,+BAA+B0B,OAAO,CAACf,iBAAiBgB,SAAS,CAACC,WAClHJ,SAAS,CAAC,IAAIxB,OAAO,yBAAyB,iBAAiB0B,OAAO,CAAC,UAAUD,OAAO,CAAC;IAAC;IAAU;IAAQ;IAAS;IAAS;IAAY;CAAY,GACtJD,SAAS,CAAC,IAAIxB,OAAO,kCAAkC,2CAA2C2B,SAAS,CAACE,aAC5GL,SAAS,CAAC,IAAIxB,OAAO,kCAAkC,uDAAuD2B,SAAS,CAACE,aACxHL,SAAS,CAAC,IAAIxB,OAAO,kCAAkC,4CAA4C2B,SAAS,CAACC,WAC7GJ,SAAS,CAAC,IAAIxB,OAAO,4BAA4B,uCAAuC2B,SAAS,CAACC,WAClGJ,SAAS,CAAC,IAAIxB,OAAO,4BAA4B,uCAAuC2B,SAAS,CAACC,WAClGJ,SAAS,CAAC,IAAIxB,OAAO,oBAAoB,iCACzCwB,SAAS,CAAC,IAAIxB,OAAO,cAAc,iDACnCwB,SAAS,CAAC,IAAIxB,OAAO,0BAA0B,4CAC/CwB,SAAS,CAAC,IAAIxB,OAAO,6BAA6B,0CAClD8B,MAAM,CAAC,OAAOC,UAAoBC;IACjC,IAAIC,WAAgC;IACpC,IAAID,eAAeE,eAAe,EAAE;QAClC,IAAI;YACF,MAAMC,UAAU,MAAMtC,SAASmC,eAAeE,eAAe,EAAE;YAC/DD,WAAWG,KAAKC,KAAK,CAACF;QACxB,EAAE,OAAM;YACNG,QAAQC,KAAK,CAAC,CAAC,uCAAuC,EAAEP,eAAeE,eAAe,EAAE;QAC1F;IACF;IAEA,MAAMM,QAAQ,IAAIC;IAClB,MAAMC,QAAQC,GAAG,CACfZ,SAASa,GAAG,CAAC,OAAOC;QAClB,MAAMC,UAAU,MAAM7C,KAAK4C,SAAS;YAAEE,UAAU;YAAMC,KAAKC,QAAQD,GAAG;QAAG,GAAGE,KAAK,CAAC,IAAM,EAAE;QAC1FJ,QAAQK,OAAO,CAAC,CAACC,OAASZ,MAAMa,GAAG,CAACD;IACtC;IAGF,KAAK,MAAMA,QAAQZ,MAAO;QACxB,MAAMc,QAAQ,MAAM1D,KAAKwD,MAAMF,KAAK,CAAC,IAAM;QAC3C,IAAII,SAASA,MAAMC,MAAM,IAAI;YAC3B,MAAMpB,UAAU,MAAMtC,SAASuD,MAAM;YACrC,MAAMI,aAAahE,cAAc4D,MAAMK,IAAI;YAC3C,MAAMC,OAAO,MAAM9C,UAAUuB;YAC7B,IAAIwB;YACJ,MAAMC,YAAY,CAAC,GAAGC;gBACpB,IAAIF,UAAU;oBACZ,MAAM,IAAIG,MAAM;gBAClB;gBACAH,WAAWzD,UAAU6D,MAAM,IAAIF;gBAC/B,OAAOF;YACT;YACA,MAAMK,SAAS,IAAIrE,iBAAiB+D,MAAM;gBACxCF;gBACAS,SAASvE,cAAc;oBACrBkE;oBACAM;oBACA5B;gBACF;gBACA6B,sBAAqBC,IAAI;oBACvBA,KAAKrD,GAAG,GAAGyC;gBACb;gBACA,MAAMa,yBAAwBC,SAAS,EAAEC,iBAAiB;oBACxD,IAAIjF,OAAOkF,SAAS,CAACF,YAAY;wBAC/B,OAAO,MAAM,CAACA;oBAChB;oBACA,MAAMG,iBAAiBF,kBAAkBf,UAAU,IAAIA;oBACvD,MAAMkB,cAAcrF,cAAcE,cAAckF;oBAChD,MAAME,WAAWD,YAAYE,OAAO,CAACN;oBACrC,OAAO,MAAM,CAACK;gBAChB;YACF;YACA,MAAME,UAAU,IAAIC;YACpB,MAAMd,OAAOe,IAAI,CAAC,OAAOT,WAAmBC;gBAC1C,MAAME,iBAAiBF,kBAAkBf,UAAU,IAAIA;gBACvD,MAAMkB,cAAcrF,cAAcE,cAAckF;gBAChD,MAAMO,SAAS1F,OAAOkF,SAAS,CAACF,aAAaA,YAAYI,YAAYE,OAAO,CAACN;gBAC7E,MAAMW,SAASJ,QAAQK,GAAG,CAACF;gBAC3B,IAAIC,QAAQ;oBACV,OAAOA;gBACT;gBACA,MAAME,MAAM,MAAM,MAAM,CAACH;gBACzB,MAAMI,cAAcC,OAAOC,IAAI,CAACH;gBAChC,MAAMI,WAAW,IAAI9F,gBACnB2F,aACA;oBACEA,YAAYjC,OAAO,CAAC,CAACqC,MAAQD,SAASE,SAAS,CAACD,KAAKL,GAAG,CAACK,IAAI;gBAC/D,GACA;oBAAEhC,YAAYwB;oBAAQf,SAASM,kBAAkBN,OAAO;gBAAC;gBAG3DY,QAAQa,GAAG,CAACV,QAAQO;gBACpB,OAAOA;YACT;YACA,MAAMvB,OAAO2B,QAAQ;YAErB,IAAIhC,UAAU;gBACZ,MAAMiC,UAAU,MAAMjC,SAASkC,OAAO,CAAC;oBACrC,GAAG7D,cAAc;oBACjB,CAACb,cAAc,EAAEqC;gBACnB;gBAEA,IAAIxB,eAAe8D,YAAY,EAAE;oBAC/B,MAAMC,eAAetF,kBAAkBmF;oBACvC,MAAM9F,UAAUkC,eAAe8D,YAAY,EAAE1D,KAAK4D,SAAS,CAACD,cAAc,MAAM;oBAChFzD,QAAQ2D,GAAG,CAAC,CAAC,mBAAmB,EAAEjE,eAAe8D,YAAY,EAAE;gBACjE;gBAEA,IAAI7D,UAAU;oBACZzB,uBAAuBoF,SAAS3D;gBAClC,OAAO;oBACL,OAAQD,eAAekE,MAAM;wBAC3B,KAAK;4BACH9F,iBAAiBwF;4BACjB;wBACF,KAAK;4BACHxF,iBAAiBwF,SAAS;4BAC1B;wBACF,KAAK;4BACHzF,kBAAkByF;4BAClB;wBACF,KAAK;4BACHtF,qBAAqBsF;4BACrB;wBACF,KAAK;4BACHrF,sBAAsBqF;4BACtB;wBACF;4BACEvF,mBAAmBuF;oBACvB;gBACF;YACF;QACF;IACF;AACF;AAEFtE,UAAUe,KAAK,CAACY,QAAQkD,IAAI"}
|