overtake 1.2.0 → 1.3.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 +211 -14
- 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 +188 -0
- package/build/reporter.cjs.map +1 -1
- package/build/reporter.js +189 -1
- 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 +11 -1
- package/build/types.cjs.map +1 -1
- package/build/types.d.ts +3 -2
- package/build/types.js +11 -1
- package/build/types.js.map +1 -1
- 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 +180 -1
- package/src/runner.ts +33 -5
- package/src/types.ts +35 -2
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
|
|
|
@@ -257,13 +264,20 @@ sumBenchmark.target('reduce').measure('sum', (_, numbers) => {
|
|
|
257
264
|
npx overtake <pattern> [options]
|
|
258
265
|
```
|
|
259
266
|
|
|
260
|
-
| Option
|
|
261
|
-
|
|
|
262
|
-
| `--format`
|
|
263
|
-
| `--report-types`
|
|
264
|
-
| `--workers`
|
|
265
|
-
| `--min-cycles`
|
|
266
|
-
| `--max-cycles`
|
|
267
|
+
| Option | Short | Description | Default |
|
|
268
|
+
| -------------------- | ----- | ----------------------------------------------------- | --------- |
|
|
269
|
+
| `--format` | `-f` | Output format (see [Output Formats](#output-formats)) | `simple` |
|
|
270
|
+
| `--report-types` | `-r` | Stats to show (see [Metrics](#available-metrics)) | `['ops']` |
|
|
271
|
+
| `--workers` | `-w` | Concurrent workers | CPU count |
|
|
272
|
+
| `--min-cycles` | | Minimum measurement iterations | 50 |
|
|
273
|
+
| `--max-cycles` | | Maximum measurement iterations | 1000 |
|
|
274
|
+
| `--warmup-cycles` | | Warmup iterations before measuring | 20 |
|
|
275
|
+
| `--abs-threshold` | | Absolute error threshold (nanoseconds) | 1000 |
|
|
276
|
+
| `--rel-threshold` | | Relative error threshold (0-1) | 0.02 |
|
|
277
|
+
| `--no-gc-observer` | | Disable GC overlap detection | enabled |
|
|
278
|
+
| `--progress` | | Show progress bar during execution | disabled |
|
|
279
|
+
| `--save-baseline` | | Save results to baseline file | - |
|
|
280
|
+
| `--compare-baseline` | | Compare against baseline file | - |
|
|
267
281
|
|
|
268
282
|
### Example Commands
|
|
269
283
|
|
|
@@ -276,7 +290,190 @@ npx overtake bench.ts -r ops mean p95 p99
|
|
|
276
290
|
|
|
277
291
|
# Output JSON for CI
|
|
278
292
|
npx overtake bench.ts -f json > results.json
|
|
293
|
+
|
|
294
|
+
# Show progress bar for long benchmarks
|
|
295
|
+
npx overtake bench.ts --progress
|
|
296
|
+
|
|
297
|
+
# Markdown output for docs/PRs
|
|
298
|
+
npx overtake bench.ts -f markdown
|
|
299
|
+
|
|
300
|
+
# ASCII histogram chart
|
|
301
|
+
npx overtake bench.ts -f histogram
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Output Formats
|
|
305
|
+
|
|
306
|
+
| Format | Description |
|
|
307
|
+
| ----------- | -------------------------------- |
|
|
308
|
+
| `simple` | Grouped console output (default) |
|
|
309
|
+
| `table` | Console table format |
|
|
310
|
+
| `json` | Compact JSON |
|
|
311
|
+
| `pjson` | Pretty-printed JSON |
|
|
312
|
+
| `markdown` | Markdown table for docs/PRs |
|
|
313
|
+
| `histogram` | ASCII bar chart comparing ops/s |
|
|
314
|
+
|
|
315
|
+
**Markdown example:**
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
npx overtake bench.ts -f markdown
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
```markdown
|
|
322
|
+
## for loop - sum
|
|
323
|
+
|
|
324
|
+
| Feed | ops |
|
|
325
|
+
| ---------- | --------------------- |
|
|
326
|
+
| 1M numbers | 2,189 ops/s +/- 0.17% |
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Histogram example:**
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
npx overtake bench.ts -f histogram
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
```
|
|
336
|
+
for loop - sum
|
|
337
|
+
|
|
338
|
+
1M numbers | ████████████████████████████████████████ 2,189 ops/s
|
|
339
|
+
|
|
340
|
+
reduce - sum
|
|
341
|
+
|
|
342
|
+
1M numbers | ████ 233 ops/s
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Available Metrics
|
|
346
|
+
|
|
347
|
+
Specify with `--report-types` or `reportTypes` option.
|
|
348
|
+
|
|
349
|
+
### Core Metrics
|
|
350
|
+
|
|
351
|
+
| Metric | Description |
|
|
352
|
+
| ------------- | ------------------------------- |
|
|
353
|
+
| `ops` | Operations per second (default) |
|
|
354
|
+
| `mean` | Average duration |
|
|
355
|
+
| `median` | Middle value (p50) |
|
|
356
|
+
| `min` / `max` | Range bounds |
|
|
357
|
+
| `mode` | Most frequent duration |
|
|
358
|
+
|
|
359
|
+
### Dispersion Metrics
|
|
360
|
+
|
|
361
|
+
| Metric | Description |
|
|
362
|
+
| ---------- | ------------------------- |
|
|
363
|
+
| `sd` | Standard deviation |
|
|
364
|
+
| `variance` | Statistical variance |
|
|
365
|
+
| `sem` | Standard error of mean |
|
|
366
|
+
| `mad` | Median absolute deviation |
|
|
367
|
+
| `iqr` | Interquartile range |
|
|
368
|
+
|
|
369
|
+
### Confidence Metrics
|
|
370
|
+
|
|
371
|
+
| Metric | Description |
|
|
372
|
+
| ---------- | ---------------------------- |
|
|
373
|
+
| `moe` | Margin of error (95% CI) |
|
|
374
|
+
| `rme` | Relative margin of error (%) |
|
|
375
|
+
| `ci_lower` | Lower bound of 95% CI |
|
|
376
|
+
| `ci_upper` | Upper bound of 95% CI |
|
|
377
|
+
|
|
378
|
+
### Percentiles
|
|
379
|
+
|
|
380
|
+
`p1` through `p99` - any percentile
|
|
381
|
+
|
|
382
|
+
**Example:**
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
npx overtake bench.ts -r ops mean sd rme p50 p95 p99
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
## Baseline Comparison
|
|
389
|
+
|
|
390
|
+
Track performance regressions by saving and comparing baselines:
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
# Save current results as baseline
|
|
394
|
+
npx overtake bench.ts --save-baseline baseline.json
|
|
395
|
+
|
|
396
|
+
# Later, compare against baseline
|
|
397
|
+
npx overtake bench.ts --compare-baseline baseline.json
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
**Output shows:**
|
|
401
|
+
|
|
402
|
+
- `+` Green: Performance improved (>5% better)
|
|
403
|
+
- `!` Red: Performance regressed (>5% worse)
|
|
404
|
+
- No indicator: Within threshold
|
|
405
|
+
|
|
406
|
+
**CI usage:**
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
# In CI, fail if regression detected
|
|
410
|
+
npx overtake bench.ts --compare-baseline main-baseline.json
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
## Additional Output Information
|
|
414
|
+
|
|
415
|
+
### Memory Tracking
|
|
416
|
+
|
|
417
|
+
Each benchmark reports heap memory delta:
|
|
418
|
+
|
|
419
|
+
```
|
|
420
|
+
1M numbers ops: 233 ops/s +/- 0.13% (heap: 1794KB)
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
This indicates memory allocated during the benchmark run.
|
|
424
|
+
|
|
425
|
+
### DCE Warning
|
|
426
|
+
|
|
427
|
+
If you see `[DCE warning]`, V8 may have eliminated your benchmark code:
|
|
428
|
+
|
|
279
429
|
```
|
|
430
|
+
1M numbers ops: 5,000,000,000 ops/s [DCE warning]
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
**Solutions:**
|
|
434
|
+
|
|
435
|
+
1. Ensure your function returns a value
|
|
436
|
+
2. Use the provided input data
|
|
437
|
+
3. Have observable side effects
|
|
438
|
+
|
|
439
|
+
The benchmark internally uses atomic operations to prevent DCE, but extremely simple operations may still trigger this warning.
|
|
440
|
+
|
|
441
|
+
## Advanced Configuration
|
|
442
|
+
|
|
443
|
+
### Environment Variables
|
|
444
|
+
|
|
445
|
+
| Variable | Description |
|
|
446
|
+
| -------------------------- | ----------------------------------------- |
|
|
447
|
+
| `OVERTAKE_PERTURB_INPUT=1` | Add nonce to inputs (defeats JIT caching) |
|
|
448
|
+
|
|
449
|
+
### Node.js Flags
|
|
450
|
+
|
|
451
|
+
The CLI automatically enables these flags:
|
|
452
|
+
|
|
453
|
+
- `--experimental-vm-modules` - Required for worker isolation
|
|
454
|
+
- `--expose-gc` - Enables explicit GC between samples
|
|
455
|
+
- `--no-warnings` - Suppresses experimental warnings
|
|
456
|
+
|
|
457
|
+
### Programmatic Options
|
|
458
|
+
|
|
459
|
+
```typescript
|
|
460
|
+
const reports = await suite.execute({
|
|
461
|
+
workers: 4, // Concurrent workers
|
|
462
|
+
warmupCycles: 20, // Warmup iterations
|
|
463
|
+
minCycles: 50, // Minimum measurement iterations
|
|
464
|
+
maxCycles: 1000, // Maximum measurement iterations
|
|
465
|
+
absThreshold: 1_000, // Stop if stddev < 1us
|
|
466
|
+
relThreshold: 0.02, // Stop if CoV < 2%
|
|
467
|
+
gcObserver: true, // Discard GC-affected samples
|
|
468
|
+
reportTypes: ['ops', 'mean', 'p95'],
|
|
469
|
+
progress: true, // Show progress bar
|
|
470
|
+
progressInterval: 100, // Progress update interval (ms)
|
|
471
|
+
});
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### One Benchmark Per File
|
|
475
|
+
|
|
476
|
+
CLI mode enforces one benchmark per file. Calling `benchmark()` twice throws an error. For multiple benchmarks, use separate files or programmatic mode.
|
|
280
477
|
|
|
281
478
|
## Troubleshooting
|
|
282
479
|
|
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":"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"}
|
package/build/executor.cjs
CHANGED
|
@@ -75,7 +75,19 @@ const createExecutor = (options)=>{
|
|
|
75
75
|
throw err;
|
|
76
76
|
}
|
|
77
77
|
const count = control[_typescjs.Control.INDEX];
|
|
78
|
+
const heapUsedKB = control[_typescjs.Control.HEAP_USED];
|
|
78
79
|
const durations = new BigUint64Array(durationsSAB).slice(0, count).sort(_utilscjs.cmp);
|
|
80
|
+
const DCE_THRESHOLD_OPS = 5_000_000_000;
|
|
81
|
+
let dceWarning = false;
|
|
82
|
+
if (count > 0) {
|
|
83
|
+
let sum = 0n;
|
|
84
|
+
for (const d of durations)sum += d;
|
|
85
|
+
const avgNs = Number(sum / BigInt(count)) / 1000;
|
|
86
|
+
const opsPerSec = avgNs > 0 ? 1_000_000_000 / avgNs : Infinity;
|
|
87
|
+
if (opsPerSec > DCE_THRESHOLD_OPS) {
|
|
88
|
+
dceWarning = true;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
79
91
|
const report = reportTypes.map((type)=>[
|
|
80
92
|
type,
|
|
81
93
|
(0, _reportercjs.createReport)(durations, type)
|
|
@@ -83,6 +95,14 @@ const createExecutor = (options)=>{
|
|
|
83
95
|
[
|
|
84
96
|
'count',
|
|
85
97
|
count
|
|
98
|
+
],
|
|
99
|
+
[
|
|
100
|
+
'heapUsedKB',
|
|
101
|
+
heapUsedKB
|
|
102
|
+
],
|
|
103
|
+
[
|
|
104
|
+
'dceWarning',
|
|
105
|
+
dceWarning
|
|
86
106
|
]
|
|
87
107
|
]);
|
|
88
108
|
return Object.fromEntries(report);
|
package/build/executor.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/executor.ts"],"sourcesContent":["import { Worker } from 'node:worker_threads';\nimport { once } from 'node:events';\nimport { queue } from 'async';\nimport { pathToFileURL } from 'node:url';\nimport { createReport, Report } from './reporter.js';\nimport { cmp } from './utils.js';\nimport {\n ExecutorRunOptions,\n ReportOptions,\n WorkerOptions,\n BenchmarkOptions,\n Control,\n ReportType,\n ReportTypeList,\n CONTROL_SLOTS,\n COMPLETE_VALUE,\n ProgressCallback,\n} from './types.js';\n\nexport type ExecutorReport<R extends ReportTypeList> = Record<R[number], Report> & {
|
|
1
|
+
{"version":3,"sources":["../src/executor.ts"],"sourcesContent":["import { Worker } from 'node:worker_threads';\nimport { once } from 'node:events';\nimport { queue } from 'async';\nimport { pathToFileURL } from 'node:url';\nimport { createReport, Report } from './reporter.js';\nimport { cmp } from './utils.js';\nimport {\n ExecutorRunOptions,\n ReportOptions,\n WorkerOptions,\n BenchmarkOptions,\n Control,\n ReportType,\n ReportTypeList,\n CONTROL_SLOTS,\n COMPLETE_VALUE,\n ProgressCallback,\n} from './types.js';\n\nexport type ExecutorReport<R extends ReportTypeList> = Record<R[number], Report> & {\n count: number;\n heapUsedKB: number;\n dceWarning: boolean;\n};\n\nexport interface ExecutorOptions<R extends ReportTypeList> extends BenchmarkOptions, ReportOptions<R> {\n workers?: number;\n maxCycles?: number;\n onProgress?: ProgressCallback;\n progressInterval?: number;\n}\n\nconst BENCHMARK_URL = Symbol.for('overtake.benchmarkUrl');\n\nexport const createExecutor = <TContext, TInput, R extends ReportTypeList>(options: Required<ExecutorOptions<R>>) => {\n const { workers, warmupCycles, maxCycles, minCycles, absThreshold, relThreshold, gcObserver = true, reportTypes, onProgress, progressInterval = 100 } = options;\n const benchmarkUrl = (options as Record<symbol, unknown>)[BENCHMARK_URL];\n const resolvedBenchmarkUrl = typeof benchmarkUrl === 'string' ? benchmarkUrl : pathToFileURL(process.cwd()).href;\n\n const executor = queue<ExecutorRunOptions<TContext, TInput>>(async ({ id, setup, teardown, pre, run, post, data }) => {\n const setupCode = setup?.toString();\n const teardownCode = teardown?.toString();\n const preCode = pre?.toString();\n const runCode = run.toString()!;\n const postCode = post?.toString();\n\n const controlSAB = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * CONTROL_SLOTS);\n const durationsSAB = new SharedArrayBuffer(BigUint64Array.BYTES_PER_ELEMENT * maxCycles);\n\n const workerFile = new URL('./worker.js', import.meta.url);\n const workerData: WorkerOptions = {\n benchmarkUrl: resolvedBenchmarkUrl,\n setupCode,\n teardownCode,\n preCode,\n runCode,\n postCode,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver,\n\n controlSAB,\n durationsSAB,\n };\n\n const worker = new Worker(workerFile, {\n workerData,\n });\n\n const control = new Int32Array(controlSAB);\n let progressIntervalId: ReturnType<typeof setInterval> | undefined;\n if (onProgress && id) {\n progressIntervalId = setInterval(() => {\n const progress = control[Control.PROGRESS] / COMPLETE_VALUE;\n onProgress({ id, progress });\n }, progressInterval);\n }\n\n const WORKER_TIMEOUT_MS = 300_000;\n const exitPromise = once(worker, 'exit');\n const timeoutId = setTimeout(() => worker.terminate(), WORKER_TIMEOUT_MS);\n try {\n const [exitCode] = await exitPromise;\n clearTimeout(timeoutId);\n if (progressIntervalId) clearInterval(progressIntervalId);\n if (exitCode !== 0) {\n throw new Error(`worker exited with code ${exitCode}`);\n }\n } catch (err) {\n clearTimeout(timeoutId);\n if (progressIntervalId) clearInterval(progressIntervalId);\n throw err;\n }\n\n const count = control[Control.INDEX];\n const heapUsedKB = control[Control.HEAP_USED];\n const durations = new BigUint64Array(durationsSAB).slice(0, count).sort(cmp);\n\n const DCE_THRESHOLD_OPS = 5_000_000_000;\n let dceWarning = false;\n if (count > 0) {\n let sum = 0n;\n for (const d of durations) sum += d;\n const avgNs = Number(sum / BigInt(count)) / 1000;\n const opsPerSec = avgNs > 0 ? 1_000_000_000 / avgNs : Infinity;\n if (opsPerSec > DCE_THRESHOLD_OPS) {\n dceWarning = true;\n }\n }\n\n const report = reportTypes\n .map<[string, unknown]>((type) => [type, createReport(durations, type)] as [ReportType, Report])\n .concat([\n ['count', count],\n ['heapUsedKB', heapUsedKB],\n ['dceWarning', dceWarning],\n ]);\n return Object.fromEntries(report);\n }, workers);\n\n executor.error((err) => {\n console.error(err);\n });\n\n return executor;\n};\n"],"names":["createExecutor","BENCHMARK_URL","Symbol","for","options","workers","warmupCycles","maxCycles","minCycles","absThreshold","relThreshold","gcObserver","reportTypes","onProgress","progressInterval","benchmarkUrl","resolvedBenchmarkUrl","pathToFileURL","process","cwd","href","executor","queue","id","setup","teardown","pre","run","post","data","setupCode","toString","teardownCode","preCode","runCode","postCode","controlSAB","SharedArrayBuffer","Int32Array","BYTES_PER_ELEMENT","CONTROL_SLOTS","durationsSAB","BigUint64Array","workerFile","URL","workerData","worker","Worker","control","progressIntervalId","setInterval","progress","Control","PROGRESS","COMPLETE_VALUE","WORKER_TIMEOUT_MS","exitPromise","once","timeoutId","setTimeout","terminate","exitCode","clearTimeout","clearInterval","Error","err","count","INDEX","heapUsedKB","HEAP_USED","durations","slice","sort","cmp","DCE_THRESHOLD_OPS","dceWarning","sum","d","avgNs","Number","BigInt","opsPerSec","Infinity","report","map","type","createReport","concat","Object","fromEntries","error","console"],"mappings":"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"}
|
package/build/executor.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { Report } from './reporter.js';
|
|
|
2
2
|
import { ExecutorRunOptions, ReportOptions, BenchmarkOptions, ReportTypeList, ProgressCallback } from './types.js';
|
|
3
3
|
export type ExecutorReport<R extends ReportTypeList> = Record<R[number], Report> & {
|
|
4
4
|
count: number;
|
|
5
|
+
heapUsedKB: number;
|
|
6
|
+
dceWarning: boolean;
|
|
5
7
|
};
|
|
6
8
|
export interface ExecutorOptions<R extends ReportTypeList> extends BenchmarkOptions, ReportOptions<R> {
|
|
7
9
|
workers?: number;
|