overtake 1.0.0-rc.3 → 1.0.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 +187 -50
- package/build/index.cjs +14 -15
- package/build/index.cjs.map +1 -1
- package/build/index.js +1 -2
- package/build/index.js.map +1 -1
- package/build/reporter.cjs +3 -3
- package/build/reporter.cjs.map +1 -1
- package/build/reporter.js.map +1 -1
- package/build/types.cjs +6 -6
- package/build/types.cjs.map +1 -1
- package/build/utils.cjs +9 -9
- package/build/utils.cjs.map +1 -1
- package/build/utils.js +1 -1
- package/build/utils.js.map +1 -1
- package/examples/object-merge.ts +41 -0
- package/package.json +19 -21
- package/src/utils.ts +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Overtake
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
High-precision performance benchmarking library for Node.js with isolated worker thread execution and statistical convergence.
|
|
4
4
|
|
|
5
5
|
[![Build Status][github-image]][github-url]
|
|
6
6
|
[![NPM version][npm-image]][npm-url]
|
|
@@ -12,16 +12,42 @@ Performance benchmark for NodeJS
|
|
|
12
12
|
|
|
13
13
|
## Table of Contents
|
|
14
14
|
|
|
15
|
+
- [Why Overtake?](#why-overtake)
|
|
15
16
|
- [Features](#features)
|
|
16
17
|
- [Installing](#installing)
|
|
18
|
+
- [Quick Start](#quick-start)
|
|
19
|
+
- [API Guide](#api-guide)
|
|
17
20
|
- [Examples](#examples)
|
|
21
|
+
- [CLI Usage](#cli-usage)
|
|
18
22
|
- [License](#license)
|
|
19
23
|
|
|
24
|
+
## Why Overtake?
|
|
25
|
+
|
|
26
|
+
Traditional JavaScript benchmarking tools often suffer from:
|
|
27
|
+
|
|
28
|
+
- **JIT optimization interference** - Code runs differently in benchmarks vs production
|
|
29
|
+
- **Memory pressure artifacts** - GC pauses and memory allocation affect timing
|
|
30
|
+
- **Cross-benchmark contamination** - Previous tests affect subsequent measurements
|
|
31
|
+
- **Insufficient sample sizes** - Results vary wildly between runs
|
|
32
|
+
|
|
33
|
+
Overtake solves these problems by:
|
|
34
|
+
|
|
35
|
+
- **Worker thread isolation** - Each benchmark runs in a separate thread with fresh V8 context
|
|
36
|
+
- **Statistical convergence** - Automatically runs until results are statistically stable
|
|
37
|
+
- **Zero-copy result collection** - Uses SharedArrayBuffer to eliminate serialization overhead
|
|
38
|
+
- **Proper warmup cycles** - Ensures JIT optimization before measurement
|
|
39
|
+
- **Concurrent execution** - Runs multiple benchmarks in parallel for faster results
|
|
40
|
+
|
|
20
41
|
## Features
|
|
21
42
|
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
43
|
+
- 🚀 **Worker thread isolation** for accurate measurements
|
|
44
|
+
- 📊 **Statistical convergence** with configurable confidence thresholds
|
|
45
|
+
- 🔄 **Automatic warmup cycles** to stabilize JIT optimization
|
|
46
|
+
- 💻 **TypeScript support** with transpilation built-in
|
|
47
|
+
- 🎯 **Multiple comparison targets** in a single benchmark
|
|
48
|
+
- 📈 **Rich statistics** including percentiles, mean, median, mode
|
|
49
|
+
- 🖥️ **CLI and programmatic API**
|
|
50
|
+
- ⚡ **Zero-copy communication** using SharedArrayBuffer
|
|
25
51
|
|
|
26
52
|
## Installing
|
|
27
53
|
|
|
@@ -37,14 +63,108 @@ Using npm:
|
|
|
37
63
|
$ npm install -D overtake
|
|
38
64
|
```
|
|
39
65
|
|
|
66
|
+
## Quick Start
|
|
67
|
+
|
|
68
|
+
### Basic Benchmark
|
|
69
|
+
|
|
70
|
+
Compare different implementations of the same operation:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// benchmark.ts
|
|
74
|
+
const suite = benchmark('Process 1000 items')
|
|
75
|
+
.target('for loop')
|
|
76
|
+
.measure('sum', (_, input) => {
|
|
77
|
+
let sum = 0;
|
|
78
|
+
for (let i = 0; i < 1000; i++) {
|
|
79
|
+
sum += i;
|
|
80
|
+
}
|
|
81
|
+
return sum;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
suite.target('reduce').measure('sum', (_, input) => {
|
|
85
|
+
return Array.from({ length: 1000 }, (_, i) => i).reduce((a, b) => a + b, 0);
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Run with CLI:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npx overtake benchmark.ts -f table
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## API Guide
|
|
96
|
+
|
|
97
|
+
### Core Concepts
|
|
98
|
+
|
|
99
|
+
1. **Benchmark**: The main container for your performance tests
|
|
100
|
+
2. **Feed**: Different input data sets to test with
|
|
101
|
+
3. **Target**: Different implementations to compare (e.g., "for loop" vs "reduce")
|
|
102
|
+
4. **Measure**: Specific operations to measure for each target
|
|
103
|
+
|
|
104
|
+
### Creating Benchmarks
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// Create a benchmark with optional initial feed
|
|
108
|
+
const suite = benchmark('Test name', () => generateInputData());
|
|
109
|
+
|
|
110
|
+
// Add more input variations
|
|
111
|
+
suite.feed('small dataset', () => generateSmallData()).feed('large dataset', () => generateLargeData());
|
|
112
|
+
|
|
113
|
+
// Define implementations to compare
|
|
114
|
+
suite.target('implementation A').measure('operation', (ctx, input) => {
|
|
115
|
+
// Your code here
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
suite.target('implementation B').measure('operation', (ctx, input) => {
|
|
119
|
+
// Alternative implementation
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Setup and Teardown
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
suite
|
|
127
|
+
.target('with setup', async () => {
|
|
128
|
+
// Setup: runs once before measurements
|
|
129
|
+
const connection = await createConnection();
|
|
130
|
+
return { connection };
|
|
131
|
+
})
|
|
132
|
+
.measure('query', async (ctx, input) => {
|
|
133
|
+
// ctx contains the setup return value
|
|
134
|
+
await ctx.connection.query(input);
|
|
135
|
+
})
|
|
136
|
+
.teardown(async (ctx) => {
|
|
137
|
+
// Cleanup: runs once after measurements
|
|
138
|
+
await ctx.connection.close();
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Pre and Post Hooks
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
suite
|
|
146
|
+
.target('with hooks')
|
|
147
|
+
.measure('process', (ctx, input) => {
|
|
148
|
+
processData(input);
|
|
149
|
+
})
|
|
150
|
+
.pre((ctx, input) => {
|
|
151
|
+
// Runs before EACH measurement
|
|
152
|
+
prepareData(input);
|
|
153
|
+
})
|
|
154
|
+
.post((ctx, input) => {
|
|
155
|
+
// Runs after EACH measurement
|
|
156
|
+
cleanupData(input);
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
40
160
|
## Examples
|
|
41
161
|
|
|
42
|
-
###
|
|
162
|
+
### Example 1: Array Operations Comparison
|
|
43
163
|
|
|
44
|
-
|
|
164
|
+
This example compares different methods for copying array elements:
|
|
45
165
|
|
|
46
166
|
```typescript
|
|
47
|
-
//
|
|
167
|
+
// array-copy.ts
|
|
48
168
|
const suite = benchmark('1M array of strings', () => Array.from({ length: 1_000_000 }, (_, idx) => `${idx}`))
|
|
49
169
|
.feed('1M array of numbers', () => Array.from({ length: 1_000_000 }, (_, idx) => idx))
|
|
50
170
|
.feed('1M typed array', () => new Uint32Array(1_000_000).map((_, idx) => idx));
|
|
@@ -64,69 +184,86 @@ suite.target('copyWithin').measure('copy half', (_, input) => {
|
|
|
64
184
|
});
|
|
65
185
|
```
|
|
66
186
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
npx overtake src/__bench__/array-copy.ts -f table -r ops mode mean p99
|
|
71
|
-
```
|
|
187
|
+
**Key insights from results:**
|
|
72
188
|
|
|
73
|
-
|
|
74
|
-
for loop
|
|
75
|
-
|
|
76
|
-
│ (index) │ ops │ mode │ mean │ p99 │ count │
|
|
77
|
-
├─────────────────────┼──────────────────────┼─────────────┼─────────────┼─────────────────────┼────────┤
|
|
78
|
-
│ 1M typed array │ '3698 ops/s ± 0.81%' │ '256.65 µs' │ '270.38 µs' │ '574.7 µs ± 0.19%' │ '1000' │
|
|
79
|
-
│ 1M array of numbers │ '2902 ops/s ± 0.3%' │ '343.92 µs' │ '344.51 µs' │ '429.24 µs ± 0.2%' │ '1000' │
|
|
80
|
-
│ 1M array of strings │ '2277 ops/s ± 0.46%' │ '397.15 µs' │ '438.99 µs' │ '569.15 µs ± 0.12%' │ '1000' │
|
|
81
|
-
└─────────────────────┴──────────────────────┴─────────────┴─────────────┴─────────────────────┴────────┘
|
|
82
|
-
|
|
83
|
-
copyWithin copy half
|
|
84
|
-
┌─────────────────────┬───────────────────────┬────────────┬────────────┬────────────────────┬────────┐
|
|
85
|
-
│ (index) │ ops │ mode │ mean │ p99 │ count │
|
|
86
|
-
├─────────────────────┼───────────────────────┼────────────┼────────────┼────────────────────┼────────┤
|
|
87
|
-
│ 1M typed array │ '17454 ops/s ± 0.67%' │ '53.11 µs' │ '57.29 µs' │ '81.18 µs ± 1.54%' │ '1000' │
|
|
88
|
-
│ 1M array of numbers │ '103 ops/s ± 0.02%' │ '9.49 ms' │ '9.64 ms' │ '9.91 ms ± 0.38%' │ '50' │
|
|
89
|
-
│ 1M array of strings │ '101 ops/s ± 0.06%' │ '9.55 ms' │ '9.87 ms' │ '10.87 ms ± 2.67%' │ '98' │
|
|
90
|
-
└─────────────────────┴───────────────────────┴────────────┴────────────┴────────────────────┴────────┘
|
|
91
|
-
```
|
|
189
|
+
- `copyWithin` is ~5x faster for typed arrays
|
|
190
|
+
- `for loop` performs consistently across all array types
|
|
191
|
+
- Regular arrays have different performance characteristics than typed arrays
|
|
92
192
|
|
|
93
|
-
###
|
|
193
|
+
### Example 2: Object Merging Strategies
|
|
94
194
|
|
|
95
|
-
|
|
195
|
+
Compare different approaches to merge arrays of objects:
|
|
96
196
|
|
|
97
197
|
```typescript
|
|
98
|
-
//
|
|
198
|
+
// object-merge.ts
|
|
99
199
|
import { Benchmark, printTableReports } from 'overtake';
|
|
100
200
|
|
|
101
|
-
const benchmark = Benchmark
|
|
102
|
-
.feed('1M array of numbers', () => Array.from({ length: 1_000_000 }, (_, idx) => idx))
|
|
103
|
-
.feed('1M typed array', () => new Uint32Array(1_000_000).map((_, idx) => idx));
|
|
201
|
+
const benchmark = new Benchmark('1K objects', () => Array.from({ length: 1_000 }, (_, idx) => ({ [idx]: idx })));
|
|
104
202
|
|
|
105
|
-
benchmark.target('
|
|
106
|
-
|
|
107
|
-
const mid = n / 2;
|
|
108
|
-
for (let i = 0; i < mid; i++) {
|
|
109
|
-
input[i + mid] = input[i];
|
|
110
|
-
}
|
|
203
|
+
benchmark.target('spread operator').measure('merge', (_, input) => {
|
|
204
|
+
return input.reduce((acc, obj) => ({ ...acc, ...obj }), {});
|
|
111
205
|
});
|
|
112
206
|
|
|
113
|
-
benchmark.target('
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
207
|
+
benchmark.target('Object.assign in reduce').measure('merge', (_, input) => {
|
|
208
|
+
return input.reduce((acc, obj) => {
|
|
209
|
+
Object.assign(acc, obj);
|
|
210
|
+
return acc;
|
|
211
|
+
}, {});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
benchmark.target('Object.assign spread').measure('merge', (_, input) => {
|
|
215
|
+
return Object.assign({}, ...input);
|
|
117
216
|
});
|
|
118
217
|
|
|
119
218
|
const reports = await benchmark.execute({
|
|
120
|
-
reportTypes: ['ops', '
|
|
219
|
+
reportTypes: ['ops', 'mean'],
|
|
220
|
+
maxCycles: 10_000,
|
|
121
221
|
});
|
|
122
222
|
|
|
123
223
|
printTableReports(reports);
|
|
124
224
|
```
|
|
125
225
|
|
|
126
|
-
|
|
226
|
+
**Key insights:**
|
|
227
|
+
|
|
228
|
+
- Spread operator in reduce is ~50% slower due to object recreation
|
|
229
|
+
- `Object.assign` with spread is most concise and performant
|
|
230
|
+
- Mutating approaches (assign in reduce) offer similar performance
|
|
231
|
+
|
|
232
|
+
## CLI Usage
|
|
233
|
+
|
|
234
|
+
### Basic Command
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
npx overtake <pattern> [options]
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Options
|
|
241
|
+
|
|
242
|
+
| Option | Description | Default |
|
|
243
|
+
| -------------------- | ------------------------------------------------------------------------------------------------------- | --------- |
|
|
244
|
+
| `-f, --format` | Output format: `simple`, `table`, `json`, `pjson` | `simple` |
|
|
245
|
+
| `-r, --report-types` | Statistics to display: `ops`, `mean`, `median`, `mode`, `min`, `max`, `p50`, `p75`, `p90`, `p95`, `p99` | `['ops']` |
|
|
246
|
+
| `-w, --workers` | Number of concurrent worker threads | CPU count |
|
|
247
|
+
| `--warmup-cycles` | Number of warmup iterations before measurement | 20 |
|
|
248
|
+
| `--min-cycles` | Minimum measurement cycles | 50 |
|
|
249
|
+
| `--max-cycles` | Maximum measurement cycles | 1000 |
|
|
250
|
+
| `--abs-threshold` | Absolute error threshold in nanoseconds | 1000 |
|
|
251
|
+
| `--rel-threshold` | Relative error threshold (0-1) | 0.02 |
|
|
252
|
+
|
|
253
|
+
### Examples
|
|
127
254
|
|
|
128
255
|
```bash
|
|
129
|
-
|
|
256
|
+
# Run all benchmarks in a directory
|
|
257
|
+
npx overtake "src/**/*.bench.ts" -f table
|
|
258
|
+
|
|
259
|
+
# Show detailed statistics
|
|
260
|
+
npx overtake benchmark.ts -r ops mean median p95 p99
|
|
261
|
+
|
|
262
|
+
# Increase precision with more cycles
|
|
263
|
+
npx overtake benchmark.ts --min-cycles 100 --max-cycles 10000
|
|
264
|
+
|
|
265
|
+
# Output JSON for CI/CD integration
|
|
266
|
+
npx overtake benchmark.ts -f json > results.json
|
|
130
267
|
```
|
|
131
268
|
|
|
132
269
|
## License
|
package/build/index.cjs
CHANGED
|
@@ -5,44 +5,44 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
function _export(target, all) {
|
|
6
6
|
for(var name in all)Object.defineProperty(target, name, {
|
|
7
7
|
enumerable: true,
|
|
8
|
-
get: all
|
|
8
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
_export(exports, {
|
|
12
|
-
AsyncFunction
|
|
12
|
+
get AsyncFunction () {
|
|
13
13
|
return AsyncFunction;
|
|
14
14
|
},
|
|
15
|
-
Benchmark
|
|
15
|
+
get Benchmark () {
|
|
16
16
|
return Benchmark;
|
|
17
17
|
},
|
|
18
|
-
DEFAULT_REPORT_TYPES
|
|
18
|
+
get DEFAULT_REPORT_TYPES () {
|
|
19
19
|
return DEFAULT_REPORT_TYPES;
|
|
20
20
|
},
|
|
21
|
-
DEFAULT_WORKERS
|
|
21
|
+
get DEFAULT_WORKERS () {
|
|
22
22
|
return DEFAULT_WORKERS;
|
|
23
23
|
},
|
|
24
|
-
FeedContext
|
|
24
|
+
get FeedContext () {
|
|
25
25
|
return FeedContext;
|
|
26
26
|
},
|
|
27
|
-
Measure
|
|
27
|
+
get Measure () {
|
|
28
28
|
return Measure;
|
|
29
29
|
},
|
|
30
|
-
MeasureContext
|
|
30
|
+
get MeasureContext () {
|
|
31
31
|
return MeasureContext;
|
|
32
32
|
},
|
|
33
|
-
Target
|
|
33
|
+
get Target () {
|
|
34
34
|
return Target;
|
|
35
35
|
},
|
|
36
|
-
TargetContext
|
|
36
|
+
get TargetContext () {
|
|
37
37
|
return TargetContext;
|
|
38
38
|
},
|
|
39
|
-
printJSONReports
|
|
39
|
+
get printJSONReports () {
|
|
40
40
|
return printJSONReports;
|
|
41
41
|
},
|
|
42
|
-
printSimpleReports
|
|
42
|
+
get printSimpleReports () {
|
|
43
43
|
return printSimpleReports;
|
|
44
44
|
},
|
|
45
|
-
printTableReports
|
|
45
|
+
get printTableReports () {
|
|
46
46
|
return printTableReports;
|
|
47
47
|
}
|
|
48
48
|
});
|
|
@@ -82,11 +82,10 @@ class TargetContext {
|
|
|
82
82
|
title;
|
|
83
83
|
setup;
|
|
84
84
|
teardown;
|
|
85
|
-
measures;
|
|
85
|
+
measures = [];
|
|
86
86
|
constructor(title, setup){
|
|
87
87
|
this.title = title;
|
|
88
88
|
this.setup = setup;
|
|
89
|
-
this.measures = [];
|
|
90
89
|
}
|
|
91
90
|
}
|
|
92
91
|
class Target {
|
package/build/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { cpus } from 'node:os';\nimport { createExecutor, ExecutorOptions, ExecutorReport } from './executor.js';\nimport { MaybePromise, StepFn, SetupFn, TeardownFn, FeedFn, ReportType, ReportTypeList, DEFAULT_CYCLES } from './types.js';\n\ndeclare global {\n const benchmark: typeof Benchmark.create;\n}\n\nexport const DEFAULT_WORKERS = cpus().length;\n\nexport const AsyncFunction = (async () => {}).constructor;\n\nexport interface TargetReport<R extends ReportTypeList> {\n target: string;\n measures: MeasureReport<R>[];\n}\n\nexport interface MeasureReport<R extends ReportTypeList> {\n measure: string;\n feeds: FeedReport<R>[];\n}\n\nexport interface FeedReport<R extends ReportTypeList> {\n feed: string;\n data: ExecutorReport<R>;\n}\n\nexport const DEFAULT_REPORT_TYPES = ['ops'] as const;\nexport type DefaultReportTypes = (typeof DEFAULT_REPORT_TYPES)[number];\n\nexport class MeasureContext<TContext, TInput> {\n public pre?: StepFn<TContext, TInput>;\n public post?: StepFn<TContext, TInput>;\n\n constructor(\n public title: string,\n public run: StepFn<TContext, TInput>,\n ) {}\n}\n\nexport class Measure<TContext, TInput> {\n #ctx: MeasureContext<TContext, TInput>;\n\n constructor(ctx: MeasureContext<TContext, TInput>) {\n this.#ctx = ctx;\n }\n\n pre(fn: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n this.#ctx.pre = fn;\n return this;\n }\n post(fn: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n this.#ctx.post = fn;\n return this;\n }\n}\n\nexport class TargetContext<TContext, TInput> {\n public teardown?: TeardownFn<TContext>;\n public measures: MeasureContext<TContext, TInput>[] = [];\n\n constructor(\n readonly title: string,\n readonly setup?: SetupFn<MaybePromise<TContext>>,\n ) {}\n}\n\nexport class Target<TContext, TInput> {\n #ctx: TargetContext<TContext, TInput>;\n\n constructor(ctx: TargetContext<TContext, TInput>) {\n this.#ctx = ctx;\n }\n teardown(fn: TeardownFn<TContext>): Target<TContext, TInput> {\n this.#ctx.teardown = fn;\n\n return this;\n }\n measure(title: string, run: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n const measure = new MeasureContext(title, run);\n this.#ctx.measures.push(measure);\n\n return new Measure(measure);\n }\n}\n\nexport class FeedContext<TInput> {\n constructor(\n readonly title: string,\n readonly fn?: FeedFn<TInput>,\n ) {}\n}\n\nexport class Benchmark<TInput> {\n #targets: TargetContext<unknown, TInput>[] = [];\n #feeds: FeedContext<TInput>[] = [];\n #executed = false;\n\n static create(title: string): Benchmark<void>;\n static create<I>(title: string, fn: FeedFn<I>): Benchmark<I>;\n static create<I>(title: string, fn?: FeedFn<I> | undefined): Benchmark<I> {\n if (fn) {\n return new Benchmark(title, fn);\n } else {\n return new Benchmark(title);\n }\n }\n\n constructor(title: string);\n constructor(title: string, fn: FeedFn<TInput>);\n constructor(title: string, fn?: FeedFn<TInput> | undefined) {\n if (fn) {\n this.feed(title, fn);\n } else {\n this.feed(title);\n }\n }\n\n feed(title: string): Benchmark<TInput | void>;\n feed<I>(title: string, fn: FeedFn<I>): Benchmark<TInput | I>;\n feed<I>(title: string, fn?: FeedFn<I> | undefined): Benchmark<TInput | I> {\n const self = this as Benchmark<TInput | I>;\n self.#feeds.push(fn ? new FeedContext(title, fn) : new FeedContext(title));\n\n return self;\n }\n\n target<TContext>(title: string): Target<void, TInput>;\n target<TContext>(title: string, setup: SetupFn<Awaited<TContext>>): Target<TContext, TInput>;\n target<TContext>(title: string, setup?: SetupFn<Awaited<TContext>> | undefined): Target<TContext, TInput> {\n const target = new TargetContext<TContext, TInput>(title, setup);\n this.#targets.push(target as TargetContext<unknown, TInput>);\n\n return new Target<TContext, TInput>(target);\n }\n\n async execute<R extends readonly ReportType[] = typeof DEFAULT_REPORT_TYPES>({\n workers = DEFAULT_WORKERS,\n warmupCycles = 20,\n maxCycles = DEFAULT_CYCLES,\n minCycles = 50,\n absThreshold = 1_000,\n relThreshold = 0.02,\n reportTypes = DEFAULT_REPORT_TYPES as unknown as R,\n }: ExecutorOptions<R>): Promise<TargetReport<R>[]> {\n if (this.#executed) {\n throw new Error(\"Benchmark is executed and can't be reused\");\n }\n this.#executed = true;\n\n const executor = createExecutor<unknown, TInput, R>({\n workers,\n warmupCycles,\n maxCycles,\n minCycles,\n absThreshold,\n relThreshold,\n reportTypes,\n });\n\n const reports: TargetReport<R>[] = [];\n for (const target of this.#targets) {\n const targetReport: TargetReport<R> = { target: target.title, measures: [] };\n for (const measure of target.measures) {\n const measureReport: MeasureReport<R> = { measure: measure.title, feeds: [] };\n for (const feed of this.#feeds) {\n const data = await feed.fn?.();\n executor\n .push<ExecutorReport<R>>({\n setup: target.setup,\n teardown: target.teardown,\n pre: measure.pre,\n run: measure.run,\n post: measure.post,\n data,\n })\n .then((data) => {\n measureReport.feeds.push({\n feed: feed.title,\n data,\n });\n });\n }\n targetReport.measures.push(measureReport);\n }\n reports.push(targetReport);\n }\n await executor.drain();\n executor.kill();\n\n return reports;\n }\n}\n\nexport const printSimpleReports = <R extends ReportTypeList>(reports: TargetReport<R>[]) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.group('\\n', report.target, measure);\n for (const { feed, data } of feeds) {\n const output = Object.entries(data)\n .map(([key, report]) => `${key}: ${report.toString()}`)\n .join('; ');\n console.log(feed, output);\n }\n console.groupEnd();\n }\n }\n};\n\nexport const printTableReports = <R extends ReportTypeList>(reports: TargetReport<R>[]) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.log('\\n', report.target, measure);\n const table: Record<string, unknown> = {};\n for (const { feed, data } of feeds) {\n table[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n console.table(table);\n }\n }\n};\n\nexport const printJSONReports = <R extends ReportTypeList>(reports: TargetReport<R>[], padding?: number) => {\n const output = {} as Record<string, Record<string, Record<string, string>>>;\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n const row = {} as Record<string, Record<string, string>>;\n for (const { feed, data } of feeds) {\n row[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n output[`${report.target} ${measure}`] = row;\n }\n }\n console.log(JSON.stringify(output, null, padding));\n};\n"],"names":["AsyncFunction","Benchmark","DEFAULT_REPORT_TYPES","DEFAULT_WORKERS","FeedContext","Measure","MeasureContext","Target","TargetContext","printJSONReports","printSimpleReports","printTableReports","cpus","length","constructor","pre","post","title","run","ctx","fn","teardown","measures","setup","measure","push","create","feed","self","target","execute","workers","warmupCycles","maxCycles","DEFAULT_CYCLES","minCycles","absThreshold","relThreshold","reportTypes","Error","executor","createExecutor","reports","targetReport","measureReport","feeds","data","then","drain","kill","report","console","group","output","Object","entries","map","key","toString","join","log","groupEnd","table","fromEntries","padding","row","JSON","stringify"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { cpus } from 'node:os';\nimport { createExecutor, ExecutorOptions, ExecutorReport } from './executor.js';\nimport { MaybePromise, StepFn, SetupFn, TeardownFn, FeedFn, ReportType, ReportTypeList, DEFAULT_CYCLES } from './types.js';\n\ndeclare global {\n const benchmark: typeof Benchmark.create;\n}\n\nexport const DEFAULT_WORKERS = cpus().length;\n\nexport const AsyncFunction = (async () => {}).constructor;\n\nexport interface TargetReport<R extends ReportTypeList> {\n target: string;\n measures: MeasureReport<R>[];\n}\n\nexport interface MeasureReport<R extends ReportTypeList> {\n measure: string;\n feeds: FeedReport<R>[];\n}\n\nexport interface FeedReport<R extends ReportTypeList> {\n feed: string;\n data: ExecutorReport<R>;\n}\n\nexport const DEFAULT_REPORT_TYPES = ['ops'] as const;\nexport type DefaultReportTypes = (typeof DEFAULT_REPORT_TYPES)[number];\n\nexport class MeasureContext<TContext, TInput> {\n public pre?: StepFn<TContext, TInput>;\n public post?: StepFn<TContext, TInput>;\n\n constructor(\n public title: string,\n public run: StepFn<TContext, TInput>,\n ) {}\n}\n\nexport class Measure<TContext, TInput> {\n #ctx: MeasureContext<TContext, TInput>;\n\n constructor(ctx: MeasureContext<TContext, TInput>) {\n this.#ctx = ctx;\n }\n\n pre(fn: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n this.#ctx.pre = fn;\n return this;\n }\n post(fn: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n this.#ctx.post = fn;\n return this;\n }\n}\n\nexport class TargetContext<TContext, TInput> {\n public teardown?: TeardownFn<TContext>;\n public measures: MeasureContext<TContext, TInput>[] = [];\n\n constructor(\n readonly title: string,\n readonly setup?: SetupFn<MaybePromise<TContext>>,\n ) {}\n}\n\nexport class Target<TContext, TInput> {\n #ctx: TargetContext<TContext, TInput>;\n\n constructor(ctx: TargetContext<TContext, TInput>) {\n this.#ctx = ctx;\n }\n teardown(fn: TeardownFn<TContext>): Target<TContext, TInput> {\n this.#ctx.teardown = fn;\n\n return this;\n }\n measure(title: string, run: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n const measure = new MeasureContext(title, run);\n this.#ctx.measures.push(measure);\n\n return new Measure(measure);\n }\n}\n\nexport class FeedContext<TInput> {\n constructor(\n readonly title: string,\n readonly fn?: FeedFn<TInput>,\n ) {}\n}\n\nexport class Benchmark<TInput> {\n #targets: TargetContext<unknown, TInput>[] = [];\n #feeds: FeedContext<TInput>[] = [];\n #executed = false;\n\n static create(title: string): Benchmark<void>;\n static create<I>(title: string, fn: FeedFn<I>): Benchmark<I>;\n static create<I>(title: string, fn?: FeedFn<I> | undefined): Benchmark<I> {\n if (fn) {\n return new Benchmark(title, fn);\n } else {\n return new Benchmark(title);\n }\n }\n\n constructor(title: string);\n constructor(title: string, fn: FeedFn<TInput>);\n constructor(title: string, fn?: FeedFn<TInput> | undefined) {\n if (fn) {\n this.feed(title, fn);\n } else {\n this.feed(title);\n }\n }\n\n feed(title: string): Benchmark<TInput | void>;\n feed<I>(title: string, fn: FeedFn<I>): Benchmark<TInput | I>;\n feed<I>(title: string, fn?: FeedFn<I> | undefined): Benchmark<TInput | I> {\n const self = this as Benchmark<TInput | I>;\n self.#feeds.push(fn ? new FeedContext(title, fn) : new FeedContext(title));\n\n return self;\n }\n\n target<TContext>(title: string): Target<void, TInput>;\n target<TContext>(title: string, setup: SetupFn<Awaited<TContext>>): Target<TContext, TInput>;\n target<TContext>(title: string, setup?: SetupFn<Awaited<TContext>> | undefined): Target<TContext, TInput> {\n const target = new TargetContext<TContext, TInput>(title, setup);\n this.#targets.push(target as TargetContext<unknown, TInput>);\n\n return new Target<TContext, TInput>(target);\n }\n\n async execute<R extends readonly ReportType[] = typeof DEFAULT_REPORT_TYPES>({\n workers = DEFAULT_WORKERS,\n warmupCycles = 20,\n maxCycles = DEFAULT_CYCLES,\n minCycles = 50,\n absThreshold = 1_000,\n relThreshold = 0.02,\n reportTypes = DEFAULT_REPORT_TYPES as unknown as R,\n }: ExecutorOptions<R>): Promise<TargetReport<R>[]> {\n if (this.#executed) {\n throw new Error(\"Benchmark is executed and can't be reused\");\n }\n this.#executed = true;\n\n const executor = createExecutor<unknown, TInput, R>({\n workers,\n warmupCycles,\n maxCycles,\n minCycles,\n absThreshold,\n relThreshold,\n reportTypes,\n });\n\n const reports: TargetReport<R>[] = [];\n for (const target of this.#targets) {\n const targetReport: TargetReport<R> = { target: target.title, measures: [] };\n for (const measure of target.measures) {\n const measureReport: MeasureReport<R> = { measure: measure.title, feeds: [] };\n for (const feed of this.#feeds) {\n const data = await feed.fn?.();\n executor\n .push<ExecutorReport<R>>({\n setup: target.setup,\n teardown: target.teardown,\n pre: measure.pre,\n run: measure.run,\n post: measure.post,\n data,\n })\n .then((data) => {\n measureReport.feeds.push({\n feed: feed.title,\n data,\n });\n });\n }\n targetReport.measures.push(measureReport);\n }\n reports.push(targetReport);\n }\n await executor.drain();\n executor.kill();\n\n return reports;\n }\n}\n\nexport const printSimpleReports = <R extends ReportTypeList>(reports: TargetReport<R>[]) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.group('\\n', report.target, measure);\n for (const { feed, data } of feeds) {\n const output = Object.entries(data)\n .map(([key, report]) => `${key}: ${report.toString()}`)\n .join('; ');\n console.log(feed, output);\n }\n console.groupEnd();\n }\n }\n};\n\nexport const printTableReports = <R extends ReportTypeList>(reports: TargetReport<R>[]) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.log('\\n', report.target, measure);\n const table: Record<string, unknown> = {};\n for (const { feed, data } of feeds) {\n table[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n console.table(table);\n }\n }\n};\n\nexport const printJSONReports = <R extends ReportTypeList>(reports: TargetReport<R>[], padding?: number) => {\n const output = {} as Record<string, Record<string, Record<string, string>>>;\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n const row = {} as Record<string, Record<string, string>>;\n for (const { feed, data } of feeds) {\n row[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n output[`${report.target} ${measure}`] = row;\n }\n }\n console.log(JSON.stringify(output, null, padding));\n};\n"],"names":["AsyncFunction","Benchmark","DEFAULT_REPORT_TYPES","DEFAULT_WORKERS","FeedContext","Measure","MeasureContext","Target","TargetContext","printJSONReports","printSimpleReports","printTableReports","cpus","length","pre","post","title","run","ctx","fn","teardown","measures","setup","measure","push","create","feed","self","target","execute","workers","warmupCycles","maxCycles","DEFAULT_CYCLES","minCycles","absThreshold","relThreshold","reportTypes","Error","executor","createExecutor","reports","targetReport","measureReport","feeds","data","then","drain","kill","report","console","group","output","Object","entries","map","key","toString","join","log","groupEnd","table","fromEntries","padding","row","JSON","stringify"],"mappings":"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"}
|
package/build/index.js
CHANGED
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { cpus } from 'node:os';\nimport { createExecutor, ExecutorOptions, ExecutorReport } from './executor.js';\nimport { MaybePromise, StepFn, SetupFn, TeardownFn, FeedFn, ReportType, ReportTypeList, DEFAULT_CYCLES } from './types.js';\n\ndeclare global {\n const benchmark: typeof Benchmark.create;\n}\n\nexport const DEFAULT_WORKERS = cpus().length;\n\nexport const AsyncFunction = (async () => {}).constructor;\n\nexport interface TargetReport<R extends ReportTypeList> {\n target: string;\n measures: MeasureReport<R>[];\n}\n\nexport interface MeasureReport<R extends ReportTypeList> {\n measure: string;\n feeds: FeedReport<R>[];\n}\n\nexport interface FeedReport<R extends ReportTypeList> {\n feed: string;\n data: ExecutorReport<R>;\n}\n\nexport const DEFAULT_REPORT_TYPES = ['ops'] as const;\nexport type DefaultReportTypes = (typeof DEFAULT_REPORT_TYPES)[number];\n\nexport class MeasureContext<TContext, TInput> {\n public pre?: StepFn<TContext, TInput>;\n public post?: StepFn<TContext, TInput>;\n\n constructor(\n public title: string,\n public run: StepFn<TContext, TInput>,\n ) {}\n}\n\nexport class Measure<TContext, TInput> {\n #ctx: MeasureContext<TContext, TInput>;\n\n constructor(ctx: MeasureContext<TContext, TInput>) {\n this.#ctx = ctx;\n }\n\n pre(fn: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n this.#ctx.pre = fn;\n return this;\n }\n post(fn: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n this.#ctx.post = fn;\n return this;\n }\n}\n\nexport class TargetContext<TContext, TInput> {\n public teardown?: TeardownFn<TContext>;\n public measures: MeasureContext<TContext, TInput>[] = [];\n\n constructor(\n readonly title: string,\n readonly setup?: SetupFn<MaybePromise<TContext>>,\n ) {}\n}\n\nexport class Target<TContext, TInput> {\n #ctx: TargetContext<TContext, TInput>;\n\n constructor(ctx: TargetContext<TContext, TInput>) {\n this.#ctx = ctx;\n }\n teardown(fn: TeardownFn<TContext>): Target<TContext, TInput> {\n this.#ctx.teardown = fn;\n\n return this;\n }\n measure(title: string, run: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n const measure = new MeasureContext(title, run);\n this.#ctx.measures.push(measure);\n\n return new Measure(measure);\n }\n}\n\nexport class FeedContext<TInput> {\n constructor(\n readonly title: string,\n readonly fn?: FeedFn<TInput>,\n ) {}\n}\n\nexport class Benchmark<TInput> {\n #targets: TargetContext<unknown, TInput>[] = [];\n #feeds: FeedContext<TInput>[] = [];\n #executed = false;\n\n static create(title: string): Benchmark<void>;\n static create<I>(title: string, fn: FeedFn<I>): Benchmark<I>;\n static create<I>(title: string, fn?: FeedFn<I> | undefined): Benchmark<I> {\n if (fn) {\n return new Benchmark(title, fn);\n } else {\n return new Benchmark(title);\n }\n }\n\n constructor(title: string);\n constructor(title: string, fn: FeedFn<TInput>);\n constructor(title: string, fn?: FeedFn<TInput> | undefined) {\n if (fn) {\n this.feed(title, fn);\n } else {\n this.feed(title);\n }\n }\n\n feed(title: string): Benchmark<TInput | void>;\n feed<I>(title: string, fn: FeedFn<I>): Benchmark<TInput | I>;\n feed<I>(title: string, fn?: FeedFn<I> | undefined): Benchmark<TInput | I> {\n const self = this as Benchmark<TInput | I>;\n self.#feeds.push(fn ? new FeedContext(title, fn) : new FeedContext(title));\n\n return self;\n }\n\n target<TContext>(title: string): Target<void, TInput>;\n target<TContext>(title: string, setup: SetupFn<Awaited<TContext>>): Target<TContext, TInput>;\n target<TContext>(title: string, setup?: SetupFn<Awaited<TContext>> | undefined): Target<TContext, TInput> {\n const target = new TargetContext<TContext, TInput>(title, setup);\n this.#targets.push(target as TargetContext<unknown, TInput>);\n\n return new Target<TContext, TInput>(target);\n }\n\n async execute<R extends readonly ReportType[] = typeof DEFAULT_REPORT_TYPES>({\n workers = DEFAULT_WORKERS,\n warmupCycles = 20,\n maxCycles = DEFAULT_CYCLES,\n minCycles = 50,\n absThreshold = 1_000,\n relThreshold = 0.02,\n reportTypes = DEFAULT_REPORT_TYPES as unknown as R,\n }: ExecutorOptions<R>): Promise<TargetReport<R>[]> {\n if (this.#executed) {\n throw new Error(\"Benchmark is executed and can't be reused\");\n }\n this.#executed = true;\n\n const executor = createExecutor<unknown, TInput, R>({\n workers,\n warmupCycles,\n maxCycles,\n minCycles,\n absThreshold,\n relThreshold,\n reportTypes,\n });\n\n const reports: TargetReport<R>[] = [];\n for (const target of this.#targets) {\n const targetReport: TargetReport<R> = { target: target.title, measures: [] };\n for (const measure of target.measures) {\n const measureReport: MeasureReport<R> = { measure: measure.title, feeds: [] };\n for (const feed of this.#feeds) {\n const data = await feed.fn?.();\n executor\n .push<ExecutorReport<R>>({\n setup: target.setup,\n teardown: target.teardown,\n pre: measure.pre,\n run: measure.run,\n post: measure.post,\n data,\n })\n .then((data) => {\n measureReport.feeds.push({\n feed: feed.title,\n data,\n });\n });\n }\n targetReport.measures.push(measureReport);\n }\n reports.push(targetReport);\n }\n await executor.drain();\n executor.kill();\n\n return reports;\n }\n}\n\nexport const printSimpleReports = <R extends ReportTypeList>(reports: TargetReport<R>[]) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.group('\\n', report.target, measure);\n for (const { feed, data } of feeds) {\n const output = Object.entries(data)\n .map(([key, report]) => `${key}: ${report.toString()}`)\n .join('; ');\n console.log(feed, output);\n }\n console.groupEnd();\n }\n }\n};\n\nexport const printTableReports = <R extends ReportTypeList>(reports: TargetReport<R>[]) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.log('\\n', report.target, measure);\n const table: Record<string, unknown> = {};\n for (const { feed, data } of feeds) {\n table[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n console.table(table);\n }\n }\n};\n\nexport const printJSONReports = <R extends ReportTypeList>(reports: TargetReport<R>[], padding?: number) => {\n const output = {} as Record<string, Record<string, Record<string, string>>>;\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n const row = {} as Record<string, Record<string, string>>;\n for (const { feed, data } of feeds) {\n row[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n output[`${report.target} ${measure}`] = row;\n }\n }\n console.log(JSON.stringify(output, null, padding));\n};\n"],"names":["cpus","createExecutor","DEFAULT_CYCLES","DEFAULT_WORKERS","length","AsyncFunction","constructor","DEFAULT_REPORT_TYPES","MeasureContext","pre","post","title","run","Measure","ctx","fn","TargetContext","teardown","measures","setup","Target","measure","push","FeedContext","Benchmark","create","feed","self","target","execute","workers","warmupCycles","maxCycles","minCycles","absThreshold","relThreshold","reportTypes","Error","executor","reports","targetReport","measureReport","feeds","data","then","drain","kill","printSimpleReports","report","console","group","output","Object","entries","map","key","toString","join","log","groupEnd","printTableReports","table","fromEntries","printJSONReports","padding","row","JSON","stringify"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { cpus } from 'node:os';\nimport { createExecutor, ExecutorOptions, ExecutorReport } from './executor.js';\nimport { MaybePromise, StepFn, SetupFn, TeardownFn, FeedFn, ReportType, ReportTypeList, DEFAULT_CYCLES } from './types.js';\n\ndeclare global {\n const benchmark: typeof Benchmark.create;\n}\n\nexport const DEFAULT_WORKERS = cpus().length;\n\nexport const AsyncFunction = (async () => {}).constructor;\n\nexport interface TargetReport<R extends ReportTypeList> {\n target: string;\n measures: MeasureReport<R>[];\n}\n\nexport interface MeasureReport<R extends ReportTypeList> {\n measure: string;\n feeds: FeedReport<R>[];\n}\n\nexport interface FeedReport<R extends ReportTypeList> {\n feed: string;\n data: ExecutorReport<R>;\n}\n\nexport const DEFAULT_REPORT_TYPES = ['ops'] as const;\nexport type DefaultReportTypes = (typeof DEFAULT_REPORT_TYPES)[number];\n\nexport class MeasureContext<TContext, TInput> {\n public pre?: StepFn<TContext, TInput>;\n public post?: StepFn<TContext, TInput>;\n\n constructor(\n public title: string,\n public run: StepFn<TContext, TInput>,\n ) {}\n}\n\nexport class Measure<TContext, TInput> {\n #ctx: MeasureContext<TContext, TInput>;\n\n constructor(ctx: MeasureContext<TContext, TInput>) {\n this.#ctx = ctx;\n }\n\n pre(fn: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n this.#ctx.pre = fn;\n return this;\n }\n post(fn: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n this.#ctx.post = fn;\n return this;\n }\n}\n\nexport class TargetContext<TContext, TInput> {\n public teardown?: TeardownFn<TContext>;\n public measures: MeasureContext<TContext, TInput>[] = [];\n\n constructor(\n readonly title: string,\n readonly setup?: SetupFn<MaybePromise<TContext>>,\n ) {}\n}\n\nexport class Target<TContext, TInput> {\n #ctx: TargetContext<TContext, TInput>;\n\n constructor(ctx: TargetContext<TContext, TInput>) {\n this.#ctx = ctx;\n }\n teardown(fn: TeardownFn<TContext>): Target<TContext, TInput> {\n this.#ctx.teardown = fn;\n\n return this;\n }\n measure(title: string, run: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n const measure = new MeasureContext(title, run);\n this.#ctx.measures.push(measure);\n\n return new Measure(measure);\n }\n}\n\nexport class FeedContext<TInput> {\n constructor(\n readonly title: string,\n readonly fn?: FeedFn<TInput>,\n ) {}\n}\n\nexport class Benchmark<TInput> {\n #targets: TargetContext<unknown, TInput>[] = [];\n #feeds: FeedContext<TInput>[] = [];\n #executed = false;\n\n static create(title: string): Benchmark<void>;\n static create<I>(title: string, fn: FeedFn<I>): Benchmark<I>;\n static create<I>(title: string, fn?: FeedFn<I> | undefined): Benchmark<I> {\n if (fn) {\n return new Benchmark(title, fn);\n } else {\n return new Benchmark(title);\n }\n }\n\n constructor(title: string);\n constructor(title: string, fn: FeedFn<TInput>);\n constructor(title: string, fn?: FeedFn<TInput> | undefined) {\n if (fn) {\n this.feed(title, fn);\n } else {\n this.feed(title);\n }\n }\n\n feed(title: string): Benchmark<TInput | void>;\n feed<I>(title: string, fn: FeedFn<I>): Benchmark<TInput | I>;\n feed<I>(title: string, fn?: FeedFn<I> | undefined): Benchmark<TInput | I> {\n const self = this as Benchmark<TInput | I>;\n self.#feeds.push(fn ? new FeedContext(title, fn) : new FeedContext(title));\n\n return self;\n }\n\n target<TContext>(title: string): Target<void, TInput>;\n target<TContext>(title: string, setup: SetupFn<Awaited<TContext>>): Target<TContext, TInput>;\n target<TContext>(title: string, setup?: SetupFn<Awaited<TContext>> | undefined): Target<TContext, TInput> {\n const target = new TargetContext<TContext, TInput>(title, setup);\n this.#targets.push(target as TargetContext<unknown, TInput>);\n\n return new Target<TContext, TInput>(target);\n }\n\n async execute<R extends readonly ReportType[] = typeof DEFAULT_REPORT_TYPES>({\n workers = DEFAULT_WORKERS,\n warmupCycles = 20,\n maxCycles = DEFAULT_CYCLES,\n minCycles = 50,\n absThreshold = 1_000,\n relThreshold = 0.02,\n reportTypes = DEFAULT_REPORT_TYPES as unknown as R,\n }: ExecutorOptions<R>): Promise<TargetReport<R>[]> {\n if (this.#executed) {\n throw new Error(\"Benchmark is executed and can't be reused\");\n }\n this.#executed = true;\n\n const executor = createExecutor<unknown, TInput, R>({\n workers,\n warmupCycles,\n maxCycles,\n minCycles,\n absThreshold,\n relThreshold,\n reportTypes,\n });\n\n const reports: TargetReport<R>[] = [];\n for (const target of this.#targets) {\n const targetReport: TargetReport<R> = { target: target.title, measures: [] };\n for (const measure of target.measures) {\n const measureReport: MeasureReport<R> = { measure: measure.title, feeds: [] };\n for (const feed of this.#feeds) {\n const data = await feed.fn?.();\n executor\n .push<ExecutorReport<R>>({\n setup: target.setup,\n teardown: target.teardown,\n pre: measure.pre,\n run: measure.run,\n post: measure.post,\n data,\n })\n .then((data) => {\n measureReport.feeds.push({\n feed: feed.title,\n data,\n });\n });\n }\n targetReport.measures.push(measureReport);\n }\n reports.push(targetReport);\n }\n await executor.drain();\n executor.kill();\n\n return reports;\n }\n}\n\nexport const printSimpleReports = <R extends ReportTypeList>(reports: TargetReport<R>[]) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.group('\\n', report.target, measure);\n for (const { feed, data } of feeds) {\n const output = Object.entries(data)\n .map(([key, report]) => `${key}: ${report.toString()}`)\n .join('; ');\n console.log(feed, output);\n }\n console.groupEnd();\n }\n }\n};\n\nexport const printTableReports = <R extends ReportTypeList>(reports: TargetReport<R>[]) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.log('\\n', report.target, measure);\n const table: Record<string, unknown> = {};\n for (const { feed, data } of feeds) {\n table[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n console.table(table);\n }\n }\n};\n\nexport const printJSONReports = <R extends ReportTypeList>(reports: TargetReport<R>[], padding?: number) => {\n const output = {} as Record<string, Record<string, Record<string, string>>>;\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n const row = {} as Record<string, Record<string, string>>;\n for (const { feed, data } of feeds) {\n row[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n output[`${report.target} ${measure}`] = row;\n }\n }\n console.log(JSON.stringify(output, null, padding));\n};\n"],"names":["cpus","createExecutor","DEFAULT_CYCLES","DEFAULT_WORKERS","length","AsyncFunction","DEFAULT_REPORT_TYPES","MeasureContext","pre","post","title","run","Measure","ctx","fn","TargetContext","teardown","measures","setup","Target","measure","push","FeedContext","Benchmark","create","feed","self","target","execute","workers","warmupCycles","maxCycles","minCycles","absThreshold","relThreshold","reportTypes","Error","executor","reports","targetReport","measureReport","feeds","data","then","drain","kill","printSimpleReports","report","console","group","output","Object","entries","map","key","toString","join","log","groupEnd","printTableReports","table","fromEntries","printJSONReports","padding","row","JSON","stringify"],"mappings":"AAAA,SAASA,IAAI,QAAQ,UAAU;AAC/B,SAASC,cAAc,QAAyC,gBAAgB;AAChF,SAAwFC,cAAc,QAAQ,aAAa;AAM3H,OAAO,MAAMC,kBAAkBH,OAAOI,MAAM,CAAC;AAE7C,OAAO,MAAMC,gBAAgB,AAAC,CAAA,WAAa,CAAA,EAAG,WAAW,CAAC;AAiB1D,OAAO,MAAMC,uBAAuB;IAAC;CAAM,CAAU;AAGrD,OAAO,MAAMC;;;IACJC,IAA+B;IAC/BC,KAAgC;IAEvC,YACE,AAAOC,KAAa,EACpB,AAAOC,GAA6B,CACpC;aAFOD,QAAAA;aACAC,MAAAA;IACN;AACL;AAEA,OAAO,MAAMC;IACX,CAAA,GAAI,CAAmC;IAEvC,YAAYC,GAAqC,CAAE;QACjD,IAAI,CAAC,CAAA,GAAI,GAAGA;IACd;IAEAL,IAAIM,EAA4B,EAA6B;QAC3D,IAAI,CAAC,CAAA,GAAI,CAACN,GAAG,GAAGM;QAChB,OAAO,IAAI;IACb;IACAL,KAAKK,EAA4B,EAA6B;QAC5D,IAAI,CAAC,CAAA,GAAI,CAACL,IAAI,GAAGK;QACjB,OAAO,IAAI;IACb;AACF;AAEA,OAAO,MAAMC;;;IACJC,SAAgC;IAChCC,WAA+C,EAAE,CAAC;IAEzD,YACE,AAASP,KAAa,EACtB,AAASQ,KAAuC,CAChD;aAFSR,QAAAA;aACAQ,QAAAA;IACR;AACL;AAEA,OAAO,MAAMC;IACX,CAAA,GAAI,CAAkC;IAEtC,YAAYN,GAAoC,CAAE;QAChD,IAAI,CAAC,CAAA,GAAI,GAAGA;IACd;IACAG,SAASF,EAAwB,EAA4B;QAC3D,IAAI,CAAC,CAAA,GAAI,CAACE,QAAQ,GAAGF;QAErB,OAAO,IAAI;IACb;IACAM,QAAQV,KAAa,EAAEC,GAA6B,EAA6B;QAC/E,MAAMS,UAAU,IAAIb,eAAeG,OAAOC;QAC1C,IAAI,CAAC,CAAA,GAAI,CAACM,QAAQ,CAACI,IAAI,CAACD;QAExB,OAAO,IAAIR,QAAQQ;IACrB;AACF;AAEA,OAAO,MAAME;;;IACX,YACE,AAASZ,KAAa,EACtB,AAASI,EAAmB,CAC5B;aAFSJ,QAAAA;aACAI,KAAAA;IACR;AACL;AAEA,OAAO,MAAMS;IACX,CAAA,OAAQ,GAAqC,EAAE,CAAC;IAChD,CAAA,KAAM,GAA0B,EAAE,CAAC;IACnC,CAAA,QAAS,GAAG,MAAM;IAIlB,OAAOC,OAAUd,KAAa,EAAEI,EAA0B,EAAgB;QACxE,IAAIA,IAAI;YACN,OAAO,IAAIS,UAAUb,OAAOI;QAC9B,OAAO;YACL,OAAO,IAAIS,UAAUb;QACvB;IACF;IAIA,YAAYA,KAAa,EAAEI,EAA+B,CAAE;QAC1D,IAAIA,IAAI;YACN,IAAI,CAACW,IAAI,CAACf,OAAOI;QACnB,OAAO;YACL,IAAI,CAACW,IAAI,CAACf;QACZ;IACF;IAIAe,KAAQf,KAAa,EAAEI,EAA0B,EAAyB;QACxE,MAAMY,OAAO,IAAI;QACjBA,KAAK,CAAA,KAAM,CAACL,IAAI,CAACP,KAAK,IAAIQ,YAAYZ,OAAOI,MAAM,IAAIQ,YAAYZ;QAEnE,OAAOgB;IACT;IAIAC,OAAiBjB,KAAa,EAAEQ,KAA8C,EAA4B;QACxG,MAAMS,SAAS,IAAIZ,cAAgCL,OAAOQ;QAC1D,IAAI,CAAC,CAAA,OAAQ,CAACG,IAAI,CAACM;QAEnB,OAAO,IAAIR,OAAyBQ;IACtC;IAEA,MAAMC,QAAuE,EAC3EC,UAAU1B,eAAe,EACzB2B,eAAe,EAAE,EACjBC,YAAY7B,cAAc,EAC1B8B,YAAY,EAAE,EACdC,eAAe,KAAK,EACpBC,eAAe,IAAI,EACnBC,cAAc7B,oBAAoC,EAC/B,EAA8B;QACjD,IAAI,IAAI,CAAC,CAAA,QAAS,EAAE;YAClB,MAAM,IAAI8B,MAAM;QAClB;QACA,IAAI,CAAC,CAAA,QAAS,GAAG;QAEjB,MAAMC,WAAWpC,eAAmC;YAClD4B;YACAC;YACAC;YACAC;YACAC;YACAC;YACAC;QACF;QAEA,MAAMG,UAA6B,EAAE;QACrC,KAAK,MAAMX,UAAU,IAAI,CAAC,CAAA,OAAQ,CAAE;YAClC,MAAMY,eAAgC;gBAAEZ,QAAQA,OAAOjB,KAAK;gBAAEO,UAAU,EAAE;YAAC;YAC3E,KAAK,MAAMG,WAAWO,OAAOV,QAAQ,CAAE;gBACrC,MAAMuB,gBAAkC;oBAAEpB,SAASA,QAAQV,KAAK;oBAAE+B,OAAO,EAAE;gBAAC;gBAC5E,KAAK,MAAMhB,QAAQ,IAAI,CAAC,CAAA,KAAM,CAAE;oBAC9B,MAAMiB,OAAO,MAAMjB,KAAKX,EAAE;oBAC1BuB,SACGhB,IAAI,CAAoB;wBACvBH,OAAOS,OAAOT,KAAK;wBACnBF,UAAUW,OAAOX,QAAQ;wBACzBR,KAAKY,QAAQZ,GAAG;wBAChBG,KAAKS,QAAQT,GAAG;wBAChBF,MAAMW,QAAQX,IAAI;wBAClBiC;oBACF,GACCC,IAAI,CAAC,CAACD;wBACLF,cAAcC,KAAK,CAACpB,IAAI,CAAC;4BACvBI,MAAMA,KAAKf,KAAK;4BAChBgC;wBACF;oBACF;gBACJ;gBACAH,aAAatB,QAAQ,CAACI,IAAI,CAACmB;YAC7B;YACAF,QAAQjB,IAAI,CAACkB;QACf;QACA,MAAMF,SAASO,KAAK;QACpBP,SAASQ,IAAI;QAEb,OAAOP;IACT;AACF;AAEA,OAAO,MAAMQ,qBAAqB,CAA2BR;IAC3D,KAAK,MAAMS,UAAUT,QAAS;QAC5B,KAAK,MAAM,EAAElB,OAAO,EAAEqB,KAAK,EAAE,IAAIM,OAAO9B,QAAQ,CAAE;YAChD+B,QAAQC,KAAK,CAAC,MAAMF,OAAOpB,MAAM,EAAEP;YACnC,KAAK,MAAM,EAAEK,IAAI,EAAEiB,IAAI,EAAE,IAAID,MAAO;gBAClC,MAAMS,SAASC,OAAOC,OAAO,CAACV,MAC3BW,GAAG,CAAC,CAAC,CAACC,KAAKP,OAAO,GAAK,GAAGO,IAAI,EAAE,EAAEP,OAAOQ,QAAQ,IAAI,EACrDC,IAAI,CAAC;gBACRR,QAAQS,GAAG,CAAChC,MAAMyB;YACpB;YACAF,QAAQU,QAAQ;QAClB;IACF;AACF,EAAE;AAEF,OAAO,MAAMC,oBAAoB,CAA2BrB;IAC1D,KAAK,MAAMS,UAAUT,QAAS;QAC5B,KAAK,MAAM,EAAElB,OAAO,EAAEqB,KAAK,EAAE,IAAIM,OAAO9B,QAAQ,CAAE;YAChD+B,QAAQS,GAAG,CAAC,MAAMV,OAAOpB,MAAM,EAAEP;YACjC,MAAMwC,QAAiC,CAAC;YACxC,KAAK,MAAM,EAAEnC,IAAI,EAAEiB,IAAI,EAAE,IAAID,MAAO;gBAClCmB,KAAK,CAACnC,KAAK,GAAG0B,OAAOU,WAAW,CAACV,OAAOC,OAAO,CAACV,MAAMW,GAAG,CAAC,CAAC,CAACC,KAAKP,OAAO,GAAK;wBAACO;wBAAKP,OAAOQ,QAAQ;qBAAG;YACvG;YACAP,QAAQY,KAAK,CAACA;QAChB;IACF;AACF,EAAE;AAEF,OAAO,MAAME,mBAAmB,CAA2BxB,SAA4ByB;IACrF,MAAMb,SAAS,CAAC;IAChB,KAAK,MAAMH,UAAUT,QAAS;QAC5B,KAAK,MAAM,EAAElB,OAAO,EAAEqB,KAAK,EAAE,IAAIM,OAAO9B,QAAQ,CAAE;YAChD,MAAM+C,MAAM,CAAC;YACb,KAAK,MAAM,EAAEvC,IAAI,EAAEiB,IAAI,EAAE,IAAID,MAAO;gBAClCuB,GAAG,CAACvC,KAAK,GAAG0B,OAAOU,WAAW,CAACV,OAAOC,OAAO,CAACV,MAAMW,GAAG,CAAC,CAAC,CAACC,KAAKP,OAAO,GAAK;wBAACO;wBAAKP,OAAOQ,QAAQ;qBAAG;YACrG;YACAL,MAAM,CAAC,GAAGH,OAAOpB,MAAM,CAAC,CAAC,EAAEP,SAAS,CAAC,GAAG4C;QAC1C;IACF;IACAhB,QAAQS,GAAG,CAACQ,KAAKC,SAAS,CAAChB,QAAQ,MAAMa;AAC3C,EAAE"}
|
package/build/reporter.cjs
CHANGED
|
@@ -5,14 +5,14 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
function _export(target, all) {
|
|
6
6
|
for(var name in all)Object.defineProperty(target, name, {
|
|
7
7
|
enumerable: true,
|
|
8
|
-
get: all
|
|
8
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
_export(exports, {
|
|
12
|
-
Report
|
|
12
|
+
get Report () {
|
|
13
13
|
return Report;
|
|
14
14
|
},
|
|
15
|
-
createReport
|
|
15
|
+
get createReport () {
|
|
16
16
|
return createReport;
|
|
17
17
|
}
|
|
18
18
|
});
|
package/build/reporter.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/reporter.ts"],"sourcesContent":["import { div, max, divs } from './utils.js';\nimport { ReportType } from './types.js';\n\nconst units = [\n { unit: 'ns', factor: 1 },\n { unit: 'µs', factor: 1e3 },\n { unit: 'ms', factor: 1e6 },\n { unit: 's', factor: 1e9 },\n { unit: 'm', factor: 60 * 1e9 },\n { unit: 'h', factor: 3600 * 1e9 },\n] as const;\n\nfunction smartFixed(n: number): string {\n return n.toLocaleString(undefined, {\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n useGrouping: true,\n });\n}\nexport class Report {\n constructor(\n public readonly type: ReportType,\n public readonly value: bigint,\n public readonly uncertainty: number = 0,\n public readonly scale: bigint = 1n,\n ) {}\n valueOf() {\n return Number(div(this.value, this.scale));\n }\n toString() {\n const uncertainty = this.uncertainty ? ` ± ${smartFixed(this.uncertainty)}%` : '';\n\n const value = this.valueOf();\n if (this.type === 'ops') {\n return `${smartFixed(value)} ops/s${uncertainty}`;\n }\n let display = value;\n let unit = 'ns';\n\n for (const { unit: u, factor } of units) {\n const candidate = value / factor;\n if (candidate < 1000) {\n display = candidate;\n unit = u;\n break;\n }\n }\n return `${smartFixed(display)} ${unit}${uncertainty}`;\n }\n}\n\nexport const createReport = (durations: BigUint64Array, type: ReportType): Report => {\n const n = durations.length;\n if (n === 0) {\n return new Report(type, 0n);\n }\n switch (type) {\n case 'min': {\n return new Report(type, durations[0]);\n }\n case 'max': {\n return new Report(type, durations[n - 1]);\n }\n case 'median': {\n const mid = Math.floor(n / 2);\n const med = n % 2 === 0 ? (durations[mid - 1] + durations[mid]) / 2n : durations[mid];\n return new Report(type, med);\n }\n\n case 'mode': {\n const freq = new Map<bigint, bigint>();\n let maxCount = 0n;\n let modeVal = durations[0];\n for (const d of durations) {\n const count = (freq.get(d) || 0n) + 1n;\n freq.set(d, count);\n if (count > maxCount) {\n maxCount = count;\n modeVal = d;\n }\n }\n let lower = modeVal;\n let upper = modeVal;\n const firstIdx = durations.indexOf(modeVal);\n const lastIdx = durations.lastIndexOf(modeVal);\n if (firstIdx > 0) lower = durations[firstIdx - 1];\n if (lastIdx < n - 1) upper = durations[lastIdx + 1];\n const gap = max(modeVal - lower, upper - modeVal);\n const uncertainty = modeVal > 0 ? Number(((gap / 2n) * 100n) / modeVal) : 0;\n return new Report(type, modeVal, uncertainty);\n }\n\n case 'ops': {\n let sum = 0n;\n for (const duration of durations) {\n sum += duration;\n }\n const avgNs = sum / BigInt(n);\n const nsPerSec = 1_000_000_000n;\n const raw = Number(nsPerSec) / Number(avgNs);\n const extra = raw < 1 ? Math.ceil(-Math.log10(raw)) : 0;\n\n const exp = raw > 100 ? 0 : 2 + extra;\n\n const scale = 10n ** BigInt(exp);\n\n const value = avgNs > 0n ? (nsPerSec * scale) / avgNs : 0n;\n const deviation = durations[n - 1] - durations[0];\n const uncertainty = avgNs > 0 ? Number(div(deviation * scale, 2n * avgNs)) : 0;\n return new Report(type, value, uncertainty, scale);\n }\n case 'mean': {\n let sum = 0n;\n for (const duration of durations) {\n sum += duration;\n }\n const value = divs(sum, BigInt(n), 1n);\n return new Report(type, value);\n }\n\n default: {\n const p = Number(type.slice(1));\n if (p === 0) {\n return new Report(type, durations[0]);\n }\n if (p === 100) {\n return new Report(type, durations[n - 1]);\n }\n const idx = Math.ceil((p / 100) * n) - 1;\n const value = durations[Math.min(Math.max(idx, 0), n - 1)];\n const prev = idx > 0 ? durations[idx - 1] : value;\n const next = idx < n - 1 ? durations[idx + 1] : value;\n const gap = max(value - prev, next - value);\n const uncertainty = value > 0 ? Number(div(divs(gap, 2n, 100_00n), value)) / 100 : 0;\n\n return new Report(type, value, uncertainty);\n }\n }\n};\n"],"names":["Report","createReport","units","unit","factor","smartFixed","n","toLocaleString","undefined","minimumFractionDigits","maximumFractionDigits","useGrouping","
|
|
1
|
+
{"version":3,"sources":["../src/reporter.ts"],"sourcesContent":["import { div, max, divs } from './utils.js';\nimport { ReportType } from './types.js';\n\nconst units = [\n { unit: 'ns', factor: 1 },\n { unit: 'µs', factor: 1e3 },\n { unit: 'ms', factor: 1e6 },\n { unit: 's', factor: 1e9 },\n { unit: 'm', factor: 60 * 1e9 },\n { unit: 'h', factor: 3600 * 1e9 },\n] as const;\n\nfunction smartFixed(n: number): string {\n return n.toLocaleString(undefined, {\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n useGrouping: true,\n });\n}\nexport class Report {\n constructor(\n public readonly type: ReportType,\n public readonly value: bigint,\n public readonly uncertainty: number = 0,\n public readonly scale: bigint = 1n,\n ) {}\n valueOf() {\n return Number(div(this.value, this.scale));\n }\n toString() {\n const uncertainty = this.uncertainty ? ` ± ${smartFixed(this.uncertainty)}%` : '';\n\n const value = this.valueOf();\n if (this.type === 'ops') {\n return `${smartFixed(value)} ops/s${uncertainty}`;\n }\n let display = value;\n let unit = 'ns';\n\n for (const { unit: u, factor } of units) {\n const candidate = value / factor;\n if (candidate < 1000) {\n display = candidate;\n unit = u;\n break;\n }\n }\n return `${smartFixed(display)} ${unit}${uncertainty}`;\n }\n}\n\nexport const createReport = (durations: BigUint64Array, type: ReportType): Report => {\n const n = durations.length;\n if (n === 0) {\n return new Report(type, 0n);\n }\n switch (type) {\n case 'min': {\n return new Report(type, durations[0]);\n }\n case 'max': {\n return new Report(type, durations[n - 1]);\n }\n case 'median': {\n const mid = Math.floor(n / 2);\n const med = n % 2 === 0 ? (durations[mid - 1] + durations[mid]) / 2n : durations[mid];\n return new Report(type, med);\n }\n\n case 'mode': {\n const freq = new Map<bigint, bigint>();\n let maxCount = 0n;\n let modeVal = durations[0];\n for (const d of durations) {\n const count = (freq.get(d) || 0n) + 1n;\n freq.set(d, count);\n if (count > maxCount) {\n maxCount = count;\n modeVal = d;\n }\n }\n let lower = modeVal;\n let upper = modeVal;\n const firstIdx = durations.indexOf(modeVal);\n const lastIdx = durations.lastIndexOf(modeVal);\n if (firstIdx > 0) lower = durations[firstIdx - 1];\n if (lastIdx < n - 1) upper = durations[lastIdx + 1];\n const gap = max(modeVal - lower, upper - modeVal);\n const uncertainty = modeVal > 0 ? Number(((gap / 2n) * 100n) / modeVal) : 0;\n return new Report(type, modeVal, uncertainty);\n }\n\n case 'ops': {\n let sum = 0n;\n for (const duration of durations) {\n sum += duration;\n }\n const avgNs = sum / BigInt(n);\n const nsPerSec = 1_000_000_000n;\n const raw = Number(nsPerSec) / Number(avgNs);\n const extra = raw < 1 ? Math.ceil(-Math.log10(raw)) : 0;\n\n const exp = raw > 100 ? 0 : 2 + extra;\n\n const scale = 10n ** BigInt(exp);\n\n const value = avgNs > 0n ? (nsPerSec * scale) / avgNs : 0n;\n const deviation = durations[n - 1] - durations[0];\n const uncertainty = avgNs > 0 ? Number(div(deviation * scale, 2n * avgNs)) : 0;\n return new Report(type, value, uncertainty, scale);\n }\n case 'mean': {\n let sum = 0n;\n for (const duration of durations) {\n sum += duration;\n }\n const value = divs(sum, BigInt(n), 1n);\n return new Report(type, value);\n }\n\n default: {\n const p = Number(type.slice(1));\n if (p === 0) {\n return new Report(type, durations[0]);\n }\n if (p === 100) {\n return new Report(type, durations[n - 1]);\n }\n const idx = Math.ceil((p / 100) * n) - 1;\n const value = durations[Math.min(Math.max(idx, 0), n - 1)];\n const prev = idx > 0 ? durations[idx - 1] : value;\n const next = idx < n - 1 ? durations[idx + 1] : value;\n const gap = max(value - prev, next - value);\n const uncertainty = value > 0 ? Number(div(divs(gap, 2n, 100_00n), value)) / 100 : 0;\n\n return new Report(type, value, uncertainty);\n }\n }\n};\n"],"names":["Report","createReport","units","unit","factor","smartFixed","n","toLocaleString","undefined","minimumFractionDigits","maximumFractionDigits","useGrouping","type","value","uncertainty","scale","valueOf","Number","div","toString","display","u","candidate","durations","length","mid","Math","floor","med","freq","Map","maxCount","modeVal","d","count","get","set","lower","upper","firstIdx","indexOf","lastIdx","lastIndexOf","gap","max","sum","duration","avgNs","BigInt","nsPerSec","raw","extra","ceil","log10","exp","deviation","divs","p","slice","idx","min","prev","next"],"mappings":"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"}
|
package/build/reporter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/reporter.ts"],"sourcesContent":["import { div, max, divs } from './utils.js';\nimport { ReportType } from './types.js';\n\nconst units = [\n { unit: 'ns', factor: 1 },\n { unit: 'µs', factor: 1e3 },\n { unit: 'ms', factor: 1e6 },\n { unit: 's', factor: 1e9 },\n { unit: 'm', factor: 60 * 1e9 },\n { unit: 'h', factor: 3600 * 1e9 },\n] as const;\n\nfunction smartFixed(n: number): string {\n return n.toLocaleString(undefined, {\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n useGrouping: true,\n });\n}\nexport class Report {\n constructor(\n public readonly type: ReportType,\n public readonly value: bigint,\n public readonly uncertainty: number = 0,\n public readonly scale: bigint = 1n,\n ) {}\n valueOf() {\n return Number(div(this.value, this.scale));\n }\n toString() {\n const uncertainty = this.uncertainty ? ` ± ${smartFixed(this.uncertainty)}%` : '';\n\n const value = this.valueOf();\n if (this.type === 'ops') {\n return `${smartFixed(value)} ops/s${uncertainty}`;\n }\n let display = value;\n let unit = 'ns';\n\n for (const { unit: u, factor } of units) {\n const candidate = value / factor;\n if (candidate < 1000) {\n display = candidate;\n unit = u;\n break;\n }\n }\n return `${smartFixed(display)} ${unit}${uncertainty}`;\n }\n}\n\nexport const createReport = (durations: BigUint64Array, type: ReportType): Report => {\n const n = durations.length;\n if (n === 0) {\n return new Report(type, 0n);\n }\n switch (type) {\n case 'min': {\n return new Report(type, durations[0]);\n }\n case 'max': {\n return new Report(type, durations[n - 1]);\n }\n case 'median': {\n const mid = Math.floor(n / 2);\n const med = n % 2 === 0 ? (durations[mid - 1] + durations[mid]) / 2n : durations[mid];\n return new Report(type, med);\n }\n\n case 'mode': {\n const freq = new Map<bigint, bigint>();\n let maxCount = 0n;\n let modeVal = durations[0];\n for (const d of durations) {\n const count = (freq.get(d) || 0n) + 1n;\n freq.set(d, count);\n if (count > maxCount) {\n maxCount = count;\n modeVal = d;\n }\n }\n let lower = modeVal;\n let upper = modeVal;\n const firstIdx = durations.indexOf(modeVal);\n const lastIdx = durations.lastIndexOf(modeVal);\n if (firstIdx > 0) lower = durations[firstIdx - 1];\n if (lastIdx < n - 1) upper = durations[lastIdx + 1];\n const gap = max(modeVal - lower, upper - modeVal);\n const uncertainty = modeVal > 0 ? Number(((gap / 2n) * 100n) / modeVal) : 0;\n return new Report(type, modeVal, uncertainty);\n }\n\n case 'ops': {\n let sum = 0n;\n for (const duration of durations) {\n sum += duration;\n }\n const avgNs = sum / BigInt(n);\n const nsPerSec = 1_000_000_000n;\n const raw = Number(nsPerSec) / Number(avgNs);\n const extra = raw < 1 ? Math.ceil(-Math.log10(raw)) : 0;\n\n const exp = raw > 100 ? 0 : 2 + extra;\n\n const scale = 10n ** BigInt(exp);\n\n const value = avgNs > 0n ? (nsPerSec * scale) / avgNs : 0n;\n const deviation = durations[n - 1] - durations[0];\n const uncertainty = avgNs > 0 ? Number(div(deviation * scale, 2n * avgNs)) : 0;\n return new Report(type, value, uncertainty, scale);\n }\n case 'mean': {\n let sum = 0n;\n for (const duration of durations) {\n sum += duration;\n }\n const value = divs(sum, BigInt(n), 1n);\n return new Report(type, value);\n }\n\n default: {\n const p = Number(type.slice(1));\n if (p === 0) {\n return new Report(type, durations[0]);\n }\n if (p === 100) {\n return new Report(type, durations[n - 1]);\n }\n const idx = Math.ceil((p / 100) * n) - 1;\n const value = durations[Math.min(Math.max(idx, 0), n - 1)];\n const prev = idx > 0 ? durations[idx - 1] : value;\n const next = idx < n - 1 ? durations[idx + 1] : value;\n const gap = max(value - prev, next - value);\n const uncertainty = value > 0 ? Number(div(divs(gap, 2n, 100_00n), value)) / 100 : 0;\n\n return new Report(type, value, uncertainty);\n }\n }\n};\n"],"names":["div","max","divs","units","unit","factor","smartFixed","n","toLocaleString","undefined","minimumFractionDigits","maximumFractionDigits","useGrouping","Report","
|
|
1
|
+
{"version":3,"sources":["../src/reporter.ts"],"sourcesContent":["import { div, max, divs } from './utils.js';\nimport { ReportType } from './types.js';\n\nconst units = [\n { unit: 'ns', factor: 1 },\n { unit: 'µs', factor: 1e3 },\n { unit: 'ms', factor: 1e6 },\n { unit: 's', factor: 1e9 },\n { unit: 'm', factor: 60 * 1e9 },\n { unit: 'h', factor: 3600 * 1e9 },\n] as const;\n\nfunction smartFixed(n: number): string {\n return n.toLocaleString(undefined, {\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n useGrouping: true,\n });\n}\nexport class Report {\n constructor(\n public readonly type: ReportType,\n public readonly value: bigint,\n public readonly uncertainty: number = 0,\n public readonly scale: bigint = 1n,\n ) {}\n valueOf() {\n return Number(div(this.value, this.scale));\n }\n toString() {\n const uncertainty = this.uncertainty ? ` ± ${smartFixed(this.uncertainty)}%` : '';\n\n const value = this.valueOf();\n if (this.type === 'ops') {\n return `${smartFixed(value)} ops/s${uncertainty}`;\n }\n let display = value;\n let unit = 'ns';\n\n for (const { unit: u, factor } of units) {\n const candidate = value / factor;\n if (candidate < 1000) {\n display = candidate;\n unit = u;\n break;\n }\n }\n return `${smartFixed(display)} ${unit}${uncertainty}`;\n }\n}\n\nexport const createReport = (durations: BigUint64Array, type: ReportType): Report => {\n const n = durations.length;\n if (n === 0) {\n return new Report(type, 0n);\n }\n switch (type) {\n case 'min': {\n return new Report(type, durations[0]);\n }\n case 'max': {\n return new Report(type, durations[n - 1]);\n }\n case 'median': {\n const mid = Math.floor(n / 2);\n const med = n % 2 === 0 ? (durations[mid - 1] + durations[mid]) / 2n : durations[mid];\n return new Report(type, med);\n }\n\n case 'mode': {\n const freq = new Map<bigint, bigint>();\n let maxCount = 0n;\n let modeVal = durations[0];\n for (const d of durations) {\n const count = (freq.get(d) || 0n) + 1n;\n freq.set(d, count);\n if (count > maxCount) {\n maxCount = count;\n modeVal = d;\n }\n }\n let lower = modeVal;\n let upper = modeVal;\n const firstIdx = durations.indexOf(modeVal);\n const lastIdx = durations.lastIndexOf(modeVal);\n if (firstIdx > 0) lower = durations[firstIdx - 1];\n if (lastIdx < n - 1) upper = durations[lastIdx + 1];\n const gap = max(modeVal - lower, upper - modeVal);\n const uncertainty = modeVal > 0 ? Number(((gap / 2n) * 100n) / modeVal) : 0;\n return new Report(type, modeVal, uncertainty);\n }\n\n case 'ops': {\n let sum = 0n;\n for (const duration of durations) {\n sum += duration;\n }\n const avgNs = sum / BigInt(n);\n const nsPerSec = 1_000_000_000n;\n const raw = Number(nsPerSec) / Number(avgNs);\n const extra = raw < 1 ? Math.ceil(-Math.log10(raw)) : 0;\n\n const exp = raw > 100 ? 0 : 2 + extra;\n\n const scale = 10n ** BigInt(exp);\n\n const value = avgNs > 0n ? (nsPerSec * scale) / avgNs : 0n;\n const deviation = durations[n - 1] - durations[0];\n const uncertainty = avgNs > 0 ? Number(div(deviation * scale, 2n * avgNs)) : 0;\n return new Report(type, value, uncertainty, scale);\n }\n case 'mean': {\n let sum = 0n;\n for (const duration of durations) {\n sum += duration;\n }\n const value = divs(sum, BigInt(n), 1n);\n return new Report(type, value);\n }\n\n default: {\n const p = Number(type.slice(1));\n if (p === 0) {\n return new Report(type, durations[0]);\n }\n if (p === 100) {\n return new Report(type, durations[n - 1]);\n }\n const idx = Math.ceil((p / 100) * n) - 1;\n const value = durations[Math.min(Math.max(idx, 0), n - 1)];\n const prev = idx > 0 ? durations[idx - 1] : value;\n const next = idx < n - 1 ? durations[idx + 1] : value;\n const gap = max(value - prev, next - value);\n const uncertainty = value > 0 ? Number(div(divs(gap, 2n, 100_00n), value)) / 100 : 0;\n\n return new Report(type, value, uncertainty);\n }\n }\n};\n"],"names":["div","max","divs","units","unit","factor","smartFixed","n","toLocaleString","undefined","minimumFractionDigits","maximumFractionDigits","useGrouping","Report","type","value","uncertainty","scale","valueOf","Number","toString","display","u","candidate","createReport","durations","length","mid","Math","floor","med","freq","Map","maxCount","modeVal","d","count","get","set","lower","upper","firstIdx","indexOf","lastIdx","lastIndexOf","gap","sum","duration","avgNs","BigInt","nsPerSec","raw","extra","ceil","log10","exp","deviation","p","slice","idx","min","prev","next"],"mappings":"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"}
|
package/build/types.cjs
CHANGED
|
@@ -5,23 +5,23 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
function _export(target, all) {
|
|
6
6
|
for(var name in all)Object.defineProperty(target, name, {
|
|
7
7
|
enumerable: true,
|
|
8
|
-
get: all
|
|
8
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
_export(exports, {
|
|
12
|
-
CONTROL_SLOTS
|
|
12
|
+
get CONTROL_SLOTS () {
|
|
13
13
|
return CONTROL_SLOTS;
|
|
14
14
|
},
|
|
15
|
-
Control
|
|
15
|
+
get Control () {
|
|
16
16
|
return Control;
|
|
17
17
|
},
|
|
18
|
-
DEFAULT_CYCLES
|
|
18
|
+
get DEFAULT_CYCLES () {
|
|
19
19
|
return DEFAULT_CYCLES;
|
|
20
20
|
},
|
|
21
|
-
REPORT_TYPES
|
|
21
|
+
get REPORT_TYPES () {
|
|
22
22
|
return REPORT_TYPES;
|
|
23
23
|
},
|
|
24
|
-
Z95
|
|
24
|
+
get Z95 () {
|
|
25
25
|
return Z95;
|
|
26
26
|
}
|
|
27
27
|
});
|
package/build/types.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["export type MaybePromise<T> = Promise<T> | PromiseLike<T> | T;\n\nexport interface SetupFn<TContext> {\n (): MaybePromise<TContext>;\n}\n\nexport interface TeardownFn<TContext> {\n (ctx: TContext): MaybePromise<void>;\n}\n\nexport interface StepFn<TContext, TInput> {\n (ctx: TContext, input: TInput): MaybePromise<void>;\n}\n\nexport interface FeedFn<TInput> {\n (): MaybePromise<TInput>;\n}\n\ntype _Sequence<To extends number, R extends unknown[]> = R['length'] extends To ? R[number] : _Sequence<To, [R['length'], ...R]>;\nexport type Sequence<To extends number> = number extends To ? number : _Sequence<To, []>;\nexport type Between<From extends number, To extends number> = Exclude<Sequence<To>, Sequence<From>>;\n\nexport type ReportType = 'ops' | 'min' | 'max' | 'mean' | 'median' | 'mode' | `p${Between<1, 100>}`;\nexport type ReportTypeList = readonly ReportType[];\nexport const REPORT_TYPES: ReportTypeList = Array.from({ length: 99 }, (_, idx) => `p${idx + 1}` as ReportType).concat(['ops', 'mean', 'min', 'max', 'median', 'mode']);\n\nexport interface ReportOptions<R extends ReportTypeList> {\n reportTypes: R;\n}\n\nexport interface BenchmarkOptions {\n warmupCycles?: number;\n minCycles?: number;\n absThreshold?: number; // ns\n relThreshold?: number; // %\n}\n\nexport interface RunOptions<TContext, TInput> {\n setup?: SetupFn<TContext>;\n teardown?: TeardownFn<TContext>;\n pre?: StepFn<TContext, TInput>;\n run: StepFn<TContext, TInput>;\n post?: StepFn<TContext, TInput>;\n data?: TInput;\n}\n\nexport interface WorkerOptions extends Required<BenchmarkOptions> {\n setupCode?: string;\n teardownCode?: string;\n preCode?: string;\n runCode: string;\n postCode?: string;\n data?: unknown;\n\n durationsSAB: SharedArrayBuffer;\n controlSAB: SharedArrayBuffer;\n}\n\nexport interface Options<TContext, TInput> extends RunOptions<TContext, TInput>, BenchmarkOptions {\n durationsSAB: SharedArrayBuffer;\n controlSAB: SharedArrayBuffer;\n}\n\nexport enum Control {\n INDEX,\n PROGRESS,\n COMPLETE,\n}\n\nexport const CONTROL_SLOTS = Object.values(Control).length / 2;\nexport const DEFAULT_CYCLES = 1_000;\nexport const Z95 = 1.96;\n"],"names":["CONTROL_SLOTS","Control","DEFAULT_CYCLES","REPORT_TYPES","Z95","Array","from","length","_","idx","concat","Object","values"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["export type MaybePromise<T> = Promise<T> | PromiseLike<T> | T;\n\nexport interface SetupFn<TContext> {\n (): MaybePromise<TContext>;\n}\n\nexport interface TeardownFn<TContext> {\n (ctx: TContext): MaybePromise<void>;\n}\n\nexport interface StepFn<TContext, TInput> {\n (ctx: TContext, input: TInput): MaybePromise<void>;\n}\n\nexport interface FeedFn<TInput> {\n (): MaybePromise<TInput>;\n}\n\ntype _Sequence<To extends number, R extends unknown[]> = R['length'] extends To ? R[number] : _Sequence<To, [R['length'], ...R]>;\nexport type Sequence<To extends number> = number extends To ? number : _Sequence<To, []>;\nexport type Between<From extends number, To extends number> = Exclude<Sequence<To>, Sequence<From>>;\n\nexport type ReportType = 'ops' | 'min' | 'max' | 'mean' | 'median' | 'mode' | `p${Between<1, 100>}`;\nexport type ReportTypeList = readonly ReportType[];\nexport const REPORT_TYPES: ReportTypeList = Array.from({ length: 99 }, (_, idx) => `p${idx + 1}` as ReportType).concat(['ops', 'mean', 'min', 'max', 'median', 'mode']);\n\nexport interface ReportOptions<R extends ReportTypeList> {\n reportTypes: R;\n}\n\nexport interface BenchmarkOptions {\n warmupCycles?: number;\n minCycles?: number;\n absThreshold?: number; // ns\n relThreshold?: number; // %\n}\n\nexport interface RunOptions<TContext, TInput> {\n setup?: SetupFn<TContext>;\n teardown?: TeardownFn<TContext>;\n pre?: StepFn<TContext, TInput>;\n run: StepFn<TContext, TInput>;\n post?: StepFn<TContext, TInput>;\n data?: TInput;\n}\n\nexport interface WorkerOptions extends Required<BenchmarkOptions> {\n setupCode?: string;\n teardownCode?: string;\n preCode?: string;\n runCode: string;\n postCode?: string;\n data?: unknown;\n\n durationsSAB: SharedArrayBuffer;\n controlSAB: SharedArrayBuffer;\n}\n\nexport interface Options<TContext, TInput> extends RunOptions<TContext, TInput>, BenchmarkOptions {\n durationsSAB: SharedArrayBuffer;\n controlSAB: SharedArrayBuffer;\n}\n\nexport enum Control {\n INDEX,\n PROGRESS,\n COMPLETE,\n}\n\nexport const CONTROL_SLOTS = Object.values(Control).length / 2;\nexport const DEFAULT_CYCLES = 1_000;\nexport const Z95 = 1.96;\n"],"names":["CONTROL_SLOTS","Control","DEFAULT_CYCLES","REPORT_TYPES","Z95","Array","from","length","_","idx","concat","Object","values"],"mappings":";;;;;;;;;;;QAqEaA;eAAAA;;QANDC;eAAAA;;QAOCC;eAAAA;;QA9CAC;eAAAA;;QA+CAC;eAAAA;;;AA/CN,MAAMD,eAA+BE,MAAMC,IAAI,CAAC;IAAEC,QAAQ;AAAG,GAAG,CAACC,GAAGC,MAAQ,CAAC,CAAC,EAAEA,MAAM,GAAG,EAAgBC,MAAM,CAAC;IAAC;IAAO;IAAQ;IAAO;IAAO;IAAU;CAAO;AAuC/J,IAAA,AAAKT,iCAAAA;;;;WAAAA;;AAML,MAAMD,gBAAgBW,OAAOC,MAAM,CAACX,SAASM,MAAM,GAAG;AACtD,MAAML,iBAAiB;AACvB,MAAME,MAAM"}
|
package/build/utils.cjs
CHANGED
|
@@ -5,29 +5,29 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
function _export(target, all) {
|
|
6
6
|
for(var name in all)Object.defineProperty(target, name, {
|
|
7
7
|
enumerable: true,
|
|
8
|
-
get: all
|
|
8
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
_export(exports, {
|
|
12
|
-
ScaledBigInt
|
|
12
|
+
get ScaledBigInt () {
|
|
13
13
|
return ScaledBigInt;
|
|
14
14
|
},
|
|
15
|
-
abs
|
|
15
|
+
get abs () {
|
|
16
16
|
return abs;
|
|
17
17
|
},
|
|
18
|
-
cmp
|
|
18
|
+
get cmp () {
|
|
19
19
|
return cmp;
|
|
20
20
|
},
|
|
21
|
-
div
|
|
21
|
+
get div () {
|
|
22
22
|
return div;
|
|
23
23
|
},
|
|
24
|
-
divMod
|
|
24
|
+
get divMod () {
|
|
25
25
|
return divMod;
|
|
26
26
|
},
|
|
27
|
-
divs
|
|
27
|
+
get divs () {
|
|
28
28
|
return divs;
|
|
29
29
|
},
|
|
30
|
-
max
|
|
30
|
+
get max () {
|
|
31
31
|
return max;
|
|
32
32
|
}
|
|
33
33
|
});
|
|
@@ -90,7 +90,7 @@ class ScaledBigInt {
|
|
|
90
90
|
this.value *= value;
|
|
91
91
|
}
|
|
92
92
|
unscale() {
|
|
93
|
-
return this.value / this.
|
|
93
|
+
return this.value / this.scale;
|
|
94
94
|
}
|
|
95
95
|
number() {
|
|
96
96
|
return Number(div(this.value, this.scale));
|
package/build/utils.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils.ts"],"sourcesContent":["export const abs = (value: bigint) => {\n if (value < 0n) {\n return -value;\n }\n return value;\n};\nexport const cmp = (a: bigint | number, b: bigint | number): number => {\n if (a > b) {\n return 1;\n }\n if (a < b) {\n return -1;\n }\n return 0;\n};\n\nexport const max = (a: bigint, b: bigint) => {\n if (a > b) {\n return a;\n }\n return b;\n};\n\nexport const divMod = (a: bigint, b: bigint) => {\n return { quotient: a / b, remainder: a % b };\n};\n\nexport function div(a: bigint, b: bigint, decimals: number = 2): string {\n if (b === 0n) throw new RangeError('Division by zero');\n const scale = 10n ** BigInt(decimals);\n const scaled = (a * scale) / b;\n const intPart = scaled / scale;\n const fracPart = scaled % scale;\n return `${intPart}.${fracPart.toString().padStart(decimals, '0')}`;\n}\n\nexport function divs(a: bigint, b: bigint, scale: bigint): bigint {\n if (b === 0n) throw new RangeError('Division by zero');\n return (a * scale) / b;\n}\n\nexport class ScaledBigInt {\n constructor(\n public value: bigint,\n public scale: bigint,\n ) {}\n add(value: bigint) {\n this.value += value * this.scale;\n }\n sub(value: bigint) {\n this.value -= value * this.scale;\n }\n div(value: bigint) {\n this.value /= value;\n }\n mul(value: bigint) {\n this.value *= value;\n }\n unscale() {\n return this.value / this.
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts"],"sourcesContent":["export const abs = (value: bigint) => {\n if (value < 0n) {\n return -value;\n }\n return value;\n};\nexport const cmp = (a: bigint | number, b: bigint | number): number => {\n if (a > b) {\n return 1;\n }\n if (a < b) {\n return -1;\n }\n return 0;\n};\n\nexport const max = (a: bigint, b: bigint) => {\n if (a > b) {\n return a;\n }\n return b;\n};\n\nexport const divMod = (a: bigint, b: bigint) => {\n return { quotient: a / b, remainder: a % b };\n};\n\nexport function div(a: bigint, b: bigint, decimals: number = 2): string {\n if (b === 0n) throw new RangeError('Division by zero');\n const scale = 10n ** BigInt(decimals);\n const scaled = (a * scale) / b;\n const intPart = scaled / scale;\n const fracPart = scaled % scale;\n return `${intPart}.${fracPart.toString().padStart(decimals, '0')}`;\n}\n\nexport function divs(a: bigint, b: bigint, scale: bigint): bigint {\n if (b === 0n) throw new RangeError('Division by zero');\n return (a * scale) / b;\n}\n\nexport class ScaledBigInt {\n constructor(\n public value: bigint,\n public scale: bigint,\n ) {}\n add(value: bigint) {\n this.value += value * this.scale;\n }\n sub(value: bigint) {\n this.value -= value * this.scale;\n }\n div(value: bigint) {\n this.value /= value;\n }\n mul(value: bigint) {\n this.value *= value;\n }\n unscale() {\n return this.value / this.scale;\n }\n number() {\n return Number(div(this.value, this.scale));\n }\n}\n"],"names":["ScaledBigInt","abs","cmp","div","divMod","divs","max","value","a","b","quotient","remainder","decimals","RangeError","scale","BigInt","scaled","intPart","fracPart","toString","padStart","add","sub","mul","unscale","number","Number"],"mappings":";;;;;;;;;;;QAyCaA;eAAAA;;QAzCAC;eAAAA;;QAMAC;eAAAA;;QAqBGC;eAAAA;;QAJHC;eAAAA;;QAaGC;eAAAA;;QApBHC;eAAAA;;;AAhBN,MAAML,MAAM,CAACM;IAClB,IAAIA,QAAQ,EAAE,EAAE;QACd,OAAO,CAACA;IACV;IACA,OAAOA;AACT;AACO,MAAML,MAAM,CAACM,GAAoBC;IACtC,IAAID,IAAIC,GAAG;QACT,OAAO;IACT;IACA,IAAID,IAAIC,GAAG;QACT,OAAO,CAAC;IACV;IACA,OAAO;AACT;AAEO,MAAMH,MAAM,CAACE,GAAWC;IAC7B,IAAID,IAAIC,GAAG;QACT,OAAOD;IACT;IACA,OAAOC;AACT;AAEO,MAAML,SAAS,CAACI,GAAWC;IAChC,OAAO;QAAEC,UAAUF,IAAIC;QAAGE,WAAWH,IAAIC;IAAE;AAC7C;AAEO,SAASN,IAAIK,CAAS,EAAEC,CAAS,EAAEG,WAAmB,CAAC;IAC5D,IAAIH,MAAM,EAAE,EAAE,MAAM,IAAII,WAAW;IACnC,MAAMC,QAAQ,GAAG,IAAIC,OAAOH;IAC5B,MAAMI,SAAS,AAACR,IAAIM,QAASL;IAC7B,MAAMQ,UAAUD,SAASF;IACzB,MAAMI,WAAWF,SAASF;IAC1B,OAAO,GAAGG,QAAQ,CAAC,EAAEC,SAASC,QAAQ,GAAGC,QAAQ,CAACR,UAAU,MAAM;AACpE;AAEO,SAASP,KAAKG,CAAS,EAAEC,CAAS,EAAEK,KAAa;IACtD,IAAIL,MAAM,EAAE,EAAE,MAAM,IAAII,WAAW;IACnC,OAAO,AAACL,IAAIM,QAASL;AACvB;AAEO,MAAMT;;;IACX,YACE,AAAOO,KAAa,EACpB,AAAOO,KAAa,CACpB;aAFOP,QAAAA;aACAO,QAAAA;IACN;IACHO,IAAId,KAAa,EAAE;QACjB,IAAI,CAACA,KAAK,IAAIA,QAAQ,IAAI,CAACO,KAAK;IAClC;IACAQ,IAAIf,KAAa,EAAE;QACjB,IAAI,CAACA,KAAK,IAAIA,QAAQ,IAAI,CAACO,KAAK;IAClC;IACAX,IAAII,KAAa,EAAE;QACjB,IAAI,CAACA,KAAK,IAAIA;IAChB;IACAgB,IAAIhB,KAAa,EAAE;QACjB,IAAI,CAACA,KAAK,IAAIA;IAChB;IACAiB,UAAU;QACR,OAAO,IAAI,CAACjB,KAAK,GAAG,IAAI,CAACO,KAAK;IAChC;IACAW,SAAS;QACP,OAAOC,OAAOvB,IAAI,IAAI,CAACI,KAAK,EAAE,IAAI,CAACO,KAAK;IAC1C;AACF"}
|
package/build/utils.js
CHANGED
package/build/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils.ts"],"sourcesContent":["export const abs = (value: bigint) => {\n if (value < 0n) {\n return -value;\n }\n return value;\n};\nexport const cmp = (a: bigint | number, b: bigint | number): number => {\n if (a > b) {\n return 1;\n }\n if (a < b) {\n return -1;\n }\n return 0;\n};\n\nexport const max = (a: bigint, b: bigint) => {\n if (a > b) {\n return a;\n }\n return b;\n};\n\nexport const divMod = (a: bigint, b: bigint) => {\n return { quotient: a / b, remainder: a % b };\n};\n\nexport function div(a: bigint, b: bigint, decimals: number = 2): string {\n if (b === 0n) throw new RangeError('Division by zero');\n const scale = 10n ** BigInt(decimals);\n const scaled = (a * scale) / b;\n const intPart = scaled / scale;\n const fracPart = scaled % scale;\n return `${intPart}.${fracPart.toString().padStart(decimals, '0')}`;\n}\n\nexport function divs(a: bigint, b: bigint, scale: bigint): bigint {\n if (b === 0n) throw new RangeError('Division by zero');\n return (a * scale) / b;\n}\n\nexport class ScaledBigInt {\n constructor(\n public value: bigint,\n public scale: bigint,\n ) {}\n add(value: bigint) {\n this.value += value * this.scale;\n }\n sub(value: bigint) {\n this.value -= value * this.scale;\n }\n div(value: bigint) {\n this.value /= value;\n }\n mul(value: bigint) {\n this.value *= value;\n }\n unscale() {\n return this.value / this.
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts"],"sourcesContent":["export const abs = (value: bigint) => {\n if (value < 0n) {\n return -value;\n }\n return value;\n};\nexport const cmp = (a: bigint | number, b: bigint | number): number => {\n if (a > b) {\n return 1;\n }\n if (a < b) {\n return -1;\n }\n return 0;\n};\n\nexport const max = (a: bigint, b: bigint) => {\n if (a > b) {\n return a;\n }\n return b;\n};\n\nexport const divMod = (a: bigint, b: bigint) => {\n return { quotient: a / b, remainder: a % b };\n};\n\nexport function div(a: bigint, b: bigint, decimals: number = 2): string {\n if (b === 0n) throw new RangeError('Division by zero');\n const scale = 10n ** BigInt(decimals);\n const scaled = (a * scale) / b;\n const intPart = scaled / scale;\n const fracPart = scaled % scale;\n return `${intPart}.${fracPart.toString().padStart(decimals, '0')}`;\n}\n\nexport function divs(a: bigint, b: bigint, scale: bigint): bigint {\n if (b === 0n) throw new RangeError('Division by zero');\n return (a * scale) / b;\n}\n\nexport class ScaledBigInt {\n constructor(\n public value: bigint,\n public scale: bigint,\n ) {}\n add(value: bigint) {\n this.value += value * this.scale;\n }\n sub(value: bigint) {\n this.value -= value * this.scale;\n }\n div(value: bigint) {\n this.value /= value;\n }\n mul(value: bigint) {\n this.value *= value;\n }\n unscale() {\n return this.value / this.scale;\n }\n number() {\n return Number(div(this.value, this.scale));\n }\n}\n"],"names":["abs","value","cmp","a","b","max","divMod","quotient","remainder","div","decimals","RangeError","scale","BigInt","scaled","intPart","fracPart","toString","padStart","divs","ScaledBigInt","add","sub","mul","unscale","number","Number"],"mappings":"AAAA,OAAO,MAAMA,MAAM,CAACC;IAClB,IAAIA,QAAQ,EAAE,EAAE;QACd,OAAO,CAACA;IACV;IACA,OAAOA;AACT,EAAE;AACF,OAAO,MAAMC,MAAM,CAACC,GAAoBC;IACtC,IAAID,IAAIC,GAAG;QACT,OAAO;IACT;IACA,IAAID,IAAIC,GAAG;QACT,OAAO,CAAC;IACV;IACA,OAAO;AACT,EAAE;AAEF,OAAO,MAAMC,MAAM,CAACF,GAAWC;IAC7B,IAAID,IAAIC,GAAG;QACT,OAAOD;IACT;IACA,OAAOC;AACT,EAAE;AAEF,OAAO,MAAME,SAAS,CAACH,GAAWC;IAChC,OAAO;QAAEG,UAAUJ,IAAIC;QAAGI,WAAWL,IAAIC;IAAE;AAC7C,EAAE;AAEF,OAAO,SAASK,IAAIN,CAAS,EAAEC,CAAS,EAAEM,WAAmB,CAAC;IAC5D,IAAIN,MAAM,EAAE,EAAE,MAAM,IAAIO,WAAW;IACnC,MAAMC,QAAQ,GAAG,IAAIC,OAAOH;IAC5B,MAAMI,SAAS,AAACX,IAAIS,QAASR;IAC7B,MAAMW,UAAUD,SAASF;IACzB,MAAMI,WAAWF,SAASF;IAC1B,OAAO,GAAGG,QAAQ,CAAC,EAAEC,SAASC,QAAQ,GAAGC,QAAQ,CAACR,UAAU,MAAM;AACpE;AAEA,OAAO,SAASS,KAAKhB,CAAS,EAAEC,CAAS,EAAEQ,KAAa;IACtD,IAAIR,MAAM,EAAE,EAAE,MAAM,IAAIO,WAAW;IACnC,OAAO,AAACR,IAAIS,QAASR;AACvB;AAEA,OAAO,MAAMgB;;;IACX,YACE,AAAOnB,KAAa,EACpB,AAAOW,KAAa,CACpB;aAFOX,QAAAA;aACAW,QAAAA;IACN;IACHS,IAAIpB,KAAa,EAAE;QACjB,IAAI,CAACA,KAAK,IAAIA,QAAQ,IAAI,CAACW,KAAK;IAClC;IACAU,IAAIrB,KAAa,EAAE;QACjB,IAAI,CAACA,KAAK,IAAIA,QAAQ,IAAI,CAACW,KAAK;IAClC;IACAH,IAAIR,KAAa,EAAE;QACjB,IAAI,CAACA,KAAK,IAAIA;IAChB;IACAsB,IAAItB,KAAa,EAAE;QACjB,IAAI,CAACA,KAAK,IAAIA;IAChB;IACAuB,UAAU;QACR,OAAO,IAAI,CAACvB,KAAK,GAAG,IAAI,CAACW,KAAK;IAChC;IACAa,SAAS;QACP,OAAOC,OAAOjB,IAAI,IAAI,CAACR,KAAK,EAAE,IAAI,CAACW,KAAK;IAC1C;AACF"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Benchmark, printSimpleReports } from '../build/index.js';
|
|
2
|
+
|
|
3
|
+
const benchmark = new Benchmark('1K array of objects', () => Array.from({ length: 1_000 }, (_, idx) => ({ [idx]: idx })));
|
|
4
|
+
|
|
5
|
+
benchmark.target('reduce destructure').measure('data', (_, input) => {
|
|
6
|
+
input.reduce((acc, obj) => {
|
|
7
|
+
return { ...acc, ...obj };
|
|
8
|
+
}, {});
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
benchmark.target('reduce assign').measure('data', (_, input) => {
|
|
12
|
+
input.reduce((acc, obj) => {
|
|
13
|
+
Object.assign(acc, obj);
|
|
14
|
+
return acc;
|
|
15
|
+
}, {});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
benchmark.target('forEach assign').measure('data', (_, input) => {
|
|
19
|
+
const result = {};
|
|
20
|
+
input.forEach((obj) => {
|
|
21
|
+
Object.assign(result, obj);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
benchmark.target('for assign').measure('data', (_, input) => {
|
|
26
|
+
const result = {};
|
|
27
|
+
for (let i = 0; i < input.length; i++) {
|
|
28
|
+
Object.assign(result, input[i]);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
benchmark.target('assign').measure('data', (_, input) => {
|
|
33
|
+
Object.assign({}, ...input);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const reports = await benchmark.execute({
|
|
37
|
+
reportTypes: ['ops'],
|
|
38
|
+
maxCycles: 10_000,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
printSimpleReports(reports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "overtake",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "NodeJS performance benchmark",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -8,12 +8,6 @@
|
|
|
8
8
|
"bin": {
|
|
9
9
|
"overtake": "bin/overtake.js"
|
|
10
10
|
},
|
|
11
|
-
"scripts": {
|
|
12
|
-
"build": "rm -rf build && inop src build -i __tests__ -i *.tmp.ts && tsc --declaration --emitDeclarationOnly",
|
|
13
|
-
"start": "./bin/overtake.js",
|
|
14
|
-
"test": "jest --detectOpenHandles --passWithNoTests",
|
|
15
|
-
"prepare": "husky"
|
|
16
|
-
},
|
|
17
11
|
"engines": {
|
|
18
12
|
"node": ">=22"
|
|
19
13
|
},
|
|
@@ -34,23 +28,27 @@
|
|
|
34
28
|
},
|
|
35
29
|
"homepage": "https://github.com/3axap4eHko/overtake#readme",
|
|
36
30
|
"devDependencies": {
|
|
37
|
-
"@jest/globals": "^
|
|
38
|
-
"@swc/jest": "^0.2.
|
|
39
|
-
"@types/async": "^3.2.
|
|
40
|
-
"@types/jest": "^
|
|
41
|
-
"@types/node": "^
|
|
31
|
+
"@jest/globals": "^30.0.5",
|
|
32
|
+
"@swc/jest": "^0.2.39",
|
|
33
|
+
"@types/async": "^3.2.25",
|
|
34
|
+
"@types/jest": "^30.0.0",
|
|
35
|
+
"@types/node": "^24.2.0",
|
|
42
36
|
"husky": "^9.1.7",
|
|
43
37
|
"inop": "^0.7.8",
|
|
44
|
-
"jest": "^
|
|
45
|
-
"prettier": "^3.
|
|
46
|
-
"pretty-quick": "^4.
|
|
47
|
-
"typescript": "^5.
|
|
38
|
+
"jest": "^30.0.5",
|
|
39
|
+
"prettier": "^3.6.2",
|
|
40
|
+
"pretty-quick": "^4.2.2",
|
|
41
|
+
"typescript": "^5.9.2"
|
|
48
42
|
},
|
|
49
43
|
"dependencies": {
|
|
50
|
-
"@swc/core": "^1.
|
|
44
|
+
"@swc/core": "^1.13.3",
|
|
51
45
|
"async": "^3.2.6",
|
|
52
|
-
"commander": "^
|
|
53
|
-
"glob": "^11.0.
|
|
46
|
+
"commander": "^14.0.0",
|
|
47
|
+
"glob": "^11.0.3"
|
|
54
48
|
},
|
|
55
|
-
"
|
|
56
|
-
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "rm -rf build && inop src build -i __tests__ -i *.tmp.ts && tsc --declaration --emitDeclarationOnly",
|
|
51
|
+
"start": "./bin/overtake.js",
|
|
52
|
+
"test": "jest --detectOpenHandles --passWithNoTests"
|
|
53
|
+
}
|
|
54
|
+
}
|