nano-benchmark 1.0.14 → 1.0.16
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 +35 -0
- package/bin/nano-bench.js +13 -2
- package/llms-full.txt +56 -0
- package/llms.txt +1 -1
- package/package.json +18 -9
- package/src/bench/runner.js +143 -67
package/README.md
CHANGED
|
@@ -93,6 +93,37 @@ npx nano-watch bench-strings-concat.js backticks
|
|
|
93
93
|
|
|
94
94
|
See [wiki](https://github.com/uhop/nano-bench/wiki) for more details.
|
|
95
95
|
|
|
96
|
+
## User Timing API integration
|
|
97
|
+
|
|
98
|
+
Pass `-o` / `--observe` to `nano-bench` to emit
|
|
99
|
+
[User Timing](https://developer.mozilla.org/en-US/docs/Web/API/Performance_API/User_timing)
|
|
100
|
+
marks at calibration and sampling phase boundaries. Marks are written to the
|
|
101
|
+
standard performance timeline and are observable via `PerformanceObserver` or
|
|
102
|
+
visible in DevTools / `node --inspect` traces — useful for correlating
|
|
103
|
+
benchmark variability with GC pauses, V8 optimization events, etc.
|
|
104
|
+
|
|
105
|
+
Mark / measure names follow `nano-bench/<function-name>/<phase>`, where phase is
|
|
106
|
+
`find-level` (calibration) or `series` / `series-par` (sample collection).
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
import {PerformanceObserver} from 'node:perf_hooks';
|
|
110
|
+
|
|
111
|
+
const obs = new PerformanceObserver(list => {
|
|
112
|
+
for (const e of list.getEntries()) {
|
|
113
|
+
console.log(e.name, e.duration.toFixed(2), 'ms');
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
obs.observe({entryTypes: ['measure']});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Marks have a small fixed cost per phase (no per-sample overhead), so leaving
|
|
120
|
+
`--observe` on does not affect measurement accuracy. Default is off.
|
|
121
|
+
|
|
122
|
+
Library users can opt in directly: `findLevel` / `benchmarkSeries` /
|
|
123
|
+
`benchmarkSeriesPar` / `measure` / `measurePar` all accept an `observe` option
|
|
124
|
+
(`boolean | string`) — `false` / unset for no marks, `true` for the default
|
|
125
|
+
label, or a string for a custom label.
|
|
126
|
+
|
|
96
127
|
## AI agents and contributing
|
|
97
128
|
|
|
98
129
|
AI agents and AI-assisted developers: read [AGENTS.md](./AGENTS.md) first for project rules
|
|
@@ -111,6 +142,8 @@ BSD 3-Clause License
|
|
|
111
142
|
|
|
112
143
|
## Release history
|
|
113
144
|
|
|
145
|
+
- 1.0.16: _Added User Timing API integration: `--observe` flag._
|
|
146
|
+
- 1.0.15: _Updated dependencies._
|
|
114
147
|
- 1.0.14: _Fixed Kruskal-Wallis post-hoc (Conover-Iman) pairwise comparison bug: corrected rank variance computation and critical value distribution. Added regression test._
|
|
115
148
|
- 1.0.13: _Improved CLI help texts and documentation for brevity and clarity._
|
|
116
149
|
- 1.0.12: _Added AI coding skills for writing benchmark files (write-bench, write-watch), shipped via npm. Added findLevel() tests. Expanded test suite._
|
|
@@ -126,3 +159,5 @@ BSD 3-Clause License
|
|
|
126
159
|
- 1.0.2: _Added the `--self` option._
|
|
127
160
|
- 1.0.1: _Added "self" argument to utilities so it can be used with Deno, Bun, etc._
|
|
128
161
|
- 1.0.0: _Initial release._
|
|
162
|
+
|
|
163
|
+
The full release notes are in the wiki: [Release notes](https://github.com/uhop/nano-bench/wiki/Release-notes).
|
package/bin/nano-bench.js
CHANGED
|
@@ -62,6 +62,10 @@ program
|
|
|
62
62
|
.option('-s, --samples <samples>', 'number of samples', toInt, 100)
|
|
63
63
|
.option('-p, --parallel', 'collect samples in parallel')
|
|
64
64
|
.option('-b, --bootstrap <bootstrap>', 'number of bootstrap samples', toInt, 1000)
|
|
65
|
+
.option(
|
|
66
|
+
'-o, --observe',
|
|
67
|
+
'emit User Timing marks at phase boundaries (PerformanceObserver/DevTools)'
|
|
68
|
+
)
|
|
65
69
|
.option('--self', 'print the file name to stdout and exit')
|
|
66
70
|
.showHelpAfterError('(add --help to see available options)');
|
|
67
71
|
|
|
@@ -246,7 +250,11 @@ while (iterations.length < names.length) {
|
|
|
246
250
|
|
|
247
251
|
const batchSize = await findLevel(
|
|
248
252
|
fn,
|
|
249
|
-
{
|
|
253
|
+
{
|
|
254
|
+
threshold: options.ms,
|
|
255
|
+
startFrom: options.minIterations,
|
|
256
|
+
observe: options.observe ? names[index] : undefined
|
|
257
|
+
},
|
|
250
258
|
async (name, data) => {
|
|
251
259
|
if (name === 'finding-level-next') {
|
|
252
260
|
iterations[index] = data.n;
|
|
@@ -264,7 +272,10 @@ while (iterations.length < names.length) {
|
|
|
264
272
|
|
|
265
273
|
for (let i = 0; i < iterations.length; ++i) {
|
|
266
274
|
const batchSize = iterations[i],
|
|
267
|
-
samples = await benchSeries(fns[names[i]], batchSize, {
|
|
275
|
+
samples = await benchSeries(fns[names[i]], batchSize, {
|
|
276
|
+
nSeries: options.samples,
|
|
277
|
+
observe: options.observe ? names[i] : undefined
|
|
278
|
+
});
|
|
268
279
|
normalizeSamples(samples, batchSize);
|
|
269
280
|
results.push(samples);
|
|
270
281
|
stats.push(getStats(samples));
|
package/llms-full.txt
CHANGED
|
@@ -38,6 +38,7 @@ nano-bench [options] <file>
|
|
|
38
38
|
- `-b, --bootstrap <bootstrap>` — number of bootstrap resamples for CI estimation (default: 1000).
|
|
39
39
|
- `-a, --alpha <alpha>` — significance level for confidence interval and tests (default: 0.05 = 95% CI).
|
|
40
40
|
- `-p, --parallel` — collect samples in parallel (useful for async benchmarks).
|
|
41
|
+
- `-o, --observe` — emit User Timing marks at calibration and sampling phase boundaries, observable via `PerformanceObserver` or DevTools / `node --inspect` traces. Mark names follow `nano-bench/<function-name>/<phase>`. Default: off.
|
|
41
42
|
- `-e, --export <name>` — name of the export to use from the file (default: `"default"`).
|
|
42
43
|
- `--self` — print the script's file path to stdout and exit (for Deno/Bun usage).
|
|
43
44
|
|
|
@@ -206,3 +207,58 @@ Used by nano-watch for indefinite monitoring with constant memory:
|
|
|
206
207
|
|
|
207
208
|
- **StatCounter** — Welford's online algorithm for streaming mean, variance (M2), skewness (M3), and kurtosis (M4). Numerically stable single-pass computation.
|
|
208
209
|
- **MedianCounter** — approximate streaming median using a hierarchical median-of-three structure. Provides O(1) memory approximate median without storing all values.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## User Timing API integration
|
|
214
|
+
|
|
215
|
+
The `nano-bench` CLI accepts `-o` / `--observe`. When set, calibration and sampling phases emit `performance.mark` and `performance.measure` entries to the standard performance timeline. Observers can subscribe via `PerformanceObserver`; the entries are also visible in DevTools / `node --inspect` traces.
|
|
216
|
+
|
|
217
|
+
Mark and measure names follow the convention `nano-bench/<label>/<phase>`:
|
|
218
|
+
|
|
219
|
+
- Start mark: `nano-bench/<label>/<phase>:start`
|
|
220
|
+
- Measure: `nano-bench/<label>/<phase>` (with the start mark as its start)
|
|
221
|
+
|
|
222
|
+
Phases are:
|
|
223
|
+
|
|
224
|
+
- `find-level` — calibration (auto-discovery of batch size).
|
|
225
|
+
- `series` — sequential sample collection.
|
|
226
|
+
- `series-par` — parallel sample collection (when `--parallel` is set).
|
|
227
|
+
|
|
228
|
+
The CLI uses each function's exported name as the label, so multiple benchmarks in one run produce distinct entries (e.g., `nano-bench/strings/find-level`, `nano-bench/backticks/series`, etc.).
|
|
229
|
+
|
|
230
|
+
### Consumer example
|
|
231
|
+
|
|
232
|
+
```js
|
|
233
|
+
import {PerformanceObserver} from 'node:perf_hooks';
|
|
234
|
+
|
|
235
|
+
const obs = new PerformanceObserver(list => {
|
|
236
|
+
for (const e of list.getEntries()) {
|
|
237
|
+
console.log(`${e.name}: ${e.duration.toFixed(2)} ms`);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
obs.observe({entryTypes: ['measure']});
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Library API
|
|
244
|
+
|
|
245
|
+
The orchestrating runner functions accept an `observe` option (`boolean | string`):
|
|
246
|
+
|
|
247
|
+
- `false` / `undefined` — no instrumentation (default).
|
|
248
|
+
- `true` — emit marks with the default label `"default"`.
|
|
249
|
+
- string — emit marks with the given label.
|
|
250
|
+
|
|
251
|
+
The option is supported by `findLevel`, `benchmarkSeries`, `benchmarkSeriesPar`, `measure`, and `measurePar`. `measure` / `measurePar` thread it through to the inner `findLevel` and `benchmarkSeries` / `benchmarkSeriesPar` calls so a single `observe` argument produces both calibration and sample-collection entries.
|
|
252
|
+
|
|
253
|
+
```js
|
|
254
|
+
import {measure} from 'nano-benchmark/bench/runner.js';
|
|
255
|
+
|
|
256
|
+
const fn = n => { let s = 0; for (let i = 0; i < n; ++i) s += i; };
|
|
257
|
+
const stats = await measure(fn, {nSeries: 50, observe: 'sum-loop'});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Cost
|
|
261
|
+
|
|
262
|
+
Marks have a small fixed cost per phase (one `performance.mark` + one `performance.measure` per phase boundary, not per sample). Per-sample timing remains pure `performance.now()` deltas, so observe-mode does not measurably affect benchmark accuracy. The default is off purely to keep the perf timeline buffer empty for users who don't need the integration.
|
|
263
|
+
|
|
264
|
+
The `nano-watch` CLI deliberately does **not** expose `--observe` because its sample loop is unbounded by default; library users who want to instrument continuous monitoring should manage their own buffer (e.g., `performance.clearMarks()` periodically) via the library API.
|
package/llms.txt
CHANGED
|
@@ -30,7 +30,7 @@ npx nano-bench benchmark.js
|
|
|
30
30
|
npx nano-bench -s 200 -b 2000 -a 0.01 benchmark.js
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
Options: `--ms` (measurement time, default 50), `--iterations` (overrides --ms), `--samples` (default 100), `--bootstrap` (default 1000), `--alpha` (significance level, default 0.05), `--parallel`, `--export` (default "default"), `--self`.
|
|
33
|
+
Options: `--ms` (measurement time, default 50), `--iterations` (overrides --ms), `--samples` (default 100), `--bootstrap` (default 1000), `--alpha` (significance level, default 0.05), `--parallel`, `--observe` (emit User Timing marks at phase boundaries), `--export` (default "default"), `--self`.
|
|
34
34
|
|
|
35
35
|
### nano-watch
|
|
36
36
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nano-benchmark",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "CLI micro-benchmarking with nonparametric statistics and significance testing.",
|
|
3
|
+
"version": "1.0.16",
|
|
4
|
+
"description": "CLI micro-benchmarking for Node, Deno, and Bun with nonparametric statistics and significance testing.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"test:seq:bun": "bun run `tape6-seq --self` --flags FO",
|
|
24
24
|
"test:seq:deno": "deno run -A `tape6-seq --self` --flags FO",
|
|
25
25
|
"lint": "prettier --check .",
|
|
26
|
-
"lint:fix": "prettier --write ."
|
|
26
|
+
"lint:fix": "prettier --write .",
|
|
27
|
+
"js-check": "tsc --project tsconfig.check.json"
|
|
27
28
|
},
|
|
28
29
|
"repository": {
|
|
29
30
|
"type": "git",
|
|
@@ -33,14 +34,20 @@
|
|
|
33
34
|
"benchmark",
|
|
34
35
|
"micro-benchmark",
|
|
35
36
|
"performance",
|
|
36
|
-
"profiling",
|
|
37
37
|
"statistics",
|
|
38
|
+
"nonparametric",
|
|
38
39
|
"significance",
|
|
40
|
+
"confidence-interval",
|
|
39
41
|
"bootstrap",
|
|
40
42
|
"mann-whitney",
|
|
41
43
|
"kruskal-wallis",
|
|
42
44
|
"cli",
|
|
43
|
-
"
|
|
45
|
+
"watch",
|
|
46
|
+
"compare",
|
|
47
|
+
"cross-runtime",
|
|
48
|
+
"nodejs",
|
|
49
|
+
"deno",
|
|
50
|
+
"bun"
|
|
44
51
|
],
|
|
45
52
|
"author": "Eugene Lazutkin <eugene.lazutkin@gmail.com> (https://www.lazutkin.com/)",
|
|
46
53
|
"license": "BSD-3-Clause",
|
|
@@ -60,13 +67,15 @@
|
|
|
60
67
|
"llms-full.txt"
|
|
61
68
|
],
|
|
62
69
|
"devDependencies": {
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"tape-six
|
|
70
|
+
"@types/node": "^25.6.0",
|
|
71
|
+
"prettier": "^3.8.3",
|
|
72
|
+
"tape-six": "^1.9.0",
|
|
73
|
+
"tape-six-proc": "^1.2.9",
|
|
74
|
+
"typescript": "^6.0.3"
|
|
66
75
|
},
|
|
67
76
|
"dependencies": {
|
|
68
77
|
"commander": "^14.0.3",
|
|
69
|
-
"console-toolkit": "^1.
|
|
78
|
+
"console-toolkit": "^1.3.0"
|
|
70
79
|
},
|
|
71
80
|
"tape6": {
|
|
72
81
|
"tests": [
|
package/src/bench/runner.js
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import {performance} from 'node:perf_hooks';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {boolean | string} Observe
|
|
5
|
+
* false / undefined — no instrumentation; true — emit marks with label "default";
|
|
6
|
+
* string — emit marks with the given label.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const makeObserver = (observe, defaultLabel) => {
|
|
10
|
+
if (!observe) return null;
|
|
11
|
+
const label = typeof observe === 'string' ? observe : defaultLabel;
|
|
12
|
+
const prefix = `nano-bench/${label}`;
|
|
13
|
+
return {
|
|
14
|
+
mark: phase => performance.mark(`${prefix}/${phase}:start`),
|
|
15
|
+
measure: phase => performance.measure(`${prefix}/${phase}`, `${prefix}/${phase}:start`)
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
3
19
|
export const nextLevel = n => {
|
|
4
20
|
if (n < 1) return 1;
|
|
5
21
|
let exp = 0;
|
|
@@ -20,33 +36,45 @@ export const nextLevel = n => {
|
|
|
20
36
|
return n;
|
|
21
37
|
};
|
|
22
38
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
/**
|
|
40
|
+
* @param {{threshold?: number, startFrom?: number, timeout?: number, observe?: Observe}} [opts]
|
|
41
|
+
* @param {Function} [report]
|
|
42
|
+
*/
|
|
43
|
+
export const findLevel = async (fn, opts = {}, report) => {
|
|
44
|
+
const {threshold = 20, startFrom = 1, timeout = 5, observe} = opts;
|
|
45
|
+
const obs = makeObserver(observe, 'default');
|
|
46
|
+
obs?.mark('find-level');
|
|
47
|
+
try {
|
|
48
|
+
return await new Promise((resolve, reject) => {
|
|
49
|
+
const bench = async n => {
|
|
50
|
+
report && (await report('finding-level', {n}));
|
|
51
|
+
try {
|
|
52
|
+
const start = performance.now(),
|
|
53
|
+
result = fn(n),
|
|
54
|
+
finish = performance.now();
|
|
55
|
+
if (result && typeof result.then == 'function') {
|
|
56
|
+
// thenable
|
|
57
|
+
result.then(async () => {
|
|
58
|
+
const finish = performance.now();
|
|
59
|
+
if (finish - start >= threshold) return resolve(n);
|
|
60
|
+
report && (await report('finding-level-next', {n, time: finish - start}));
|
|
61
|
+
setTimeout(bench, timeout, nextLevel(n));
|
|
62
|
+
}, reject);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (finish - start >= threshold) return resolve(n);
|
|
66
|
+
report && (await report('finding-level-next', {n, time: finish - start}));
|
|
67
|
+
setTimeout(bench, timeout, nextLevel(n));
|
|
68
|
+
} catch (error) {
|
|
69
|
+
reject(error);
|
|
40
70
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
bench(startFrom);
|
|
49
|
-
});
|
|
71
|
+
};
|
|
72
|
+
bench(startFrom);
|
|
73
|
+
});
|
|
74
|
+
} finally {
|
|
75
|
+
obs?.measure('find-level');
|
|
76
|
+
}
|
|
77
|
+
};
|
|
50
78
|
|
|
51
79
|
export const benchmark = (fn, n) =>
|
|
52
80
|
new Promise((resolve, reject) => {
|
|
@@ -68,42 +96,72 @@ export const benchmark = (fn, n) =>
|
|
|
68
96
|
}
|
|
69
97
|
});
|
|
70
98
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
99
|
+
/**
|
|
100
|
+
* @param {{nSeries?: number, timeout?: number, DataArray?: ArrayConstructor, observe?: Observe}} [opts]
|
|
101
|
+
*/
|
|
102
|
+
export const benchmarkSeries = async (fn, n, opts = {}) => {
|
|
103
|
+
const {nSeries = 100, timeout = 5, DataArray = Array, observe} = opts;
|
|
104
|
+
const obs = makeObserver(observe, 'default');
|
|
105
|
+
obs?.mark('series');
|
|
106
|
+
try {
|
|
107
|
+
const data = new DataArray(nSeries);
|
|
108
|
+
|
|
109
|
+
const bench = async (nSeries, resolve, reject) => {
|
|
110
|
+
--nSeries;
|
|
111
|
+
try {
|
|
112
|
+
data[nSeries] = await benchmark(fn, n);
|
|
113
|
+
if (nSeries) {
|
|
114
|
+
setTimeout(bench, timeout, nSeries, resolve, reject);
|
|
115
|
+
} else {
|
|
116
|
+
resolve();
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
reject(error);
|
|
86
120
|
}
|
|
87
|
-
}
|
|
88
|
-
reject(error);
|
|
89
|
-
}
|
|
90
|
-
};
|
|
121
|
+
};
|
|
91
122
|
|
|
92
|
-
|
|
123
|
+
await new Promise((resolve, reject) => bench(nSeries, resolve, reject));
|
|
93
124
|
|
|
94
|
-
|
|
125
|
+
return data;
|
|
126
|
+
} finally {
|
|
127
|
+
obs?.measure('series');
|
|
128
|
+
}
|
|
95
129
|
};
|
|
96
130
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
131
|
+
/**
|
|
132
|
+
* @param {{nSeries?: number, DataArray?: ArrayConstructor, observe?: Observe}} [opts]
|
|
133
|
+
*/
|
|
134
|
+
export const benchmarkSeriesPar = async (fn, n, opts = {}) => {
|
|
135
|
+
let {nSeries = 100} = opts;
|
|
136
|
+
const {DataArray = Array, observe} = opts;
|
|
137
|
+
const obs = makeObserver(observe, 'default');
|
|
138
|
+
obs?.mark('series-par');
|
|
139
|
+
try {
|
|
140
|
+
const benchmarks = [];
|
|
141
|
+
for (; nSeries > 0; --nSeries) benchmarks.push(benchmark(fn, n));
|
|
142
|
+
const results = await Promise.all(benchmarks);
|
|
143
|
+
return DataArray === Array ? results : DataArray.from(results);
|
|
144
|
+
} finally {
|
|
145
|
+
obs?.measure('series-par');
|
|
146
|
+
}
|
|
102
147
|
};
|
|
103
148
|
|
|
149
|
+
/**
|
|
150
|
+
* @typedef {object} StatsInit
|
|
151
|
+
* @property {number[]} data
|
|
152
|
+
* @property {number} reps
|
|
153
|
+
* @property {number} [time]
|
|
154
|
+
* @property {boolean} [sorted]
|
|
155
|
+
*/
|
|
156
|
+
|
|
104
157
|
export class Stats {
|
|
158
|
+
/** @param {StatsInit} object */
|
|
105
159
|
constructor(object) {
|
|
106
|
-
|
|
160
|
+
/** @type {number[]} */
|
|
161
|
+
this.data = object.data;
|
|
162
|
+
this.reps = object.reps;
|
|
163
|
+
this.time = object.time;
|
|
164
|
+
this.sorted = object.sorted ?? false;
|
|
107
165
|
}
|
|
108
166
|
|
|
109
167
|
static sortNumbersAsc = (a, b) => a - b;
|
|
@@ -134,34 +192,52 @@ export class Stats {
|
|
|
134
192
|
}
|
|
135
193
|
}
|
|
136
194
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
) => {
|
|
195
|
+
/**
|
|
196
|
+
* @param {{nSeries?: number, threshold?: number, startFrom?: number, timeout?: number, DataArray?: ArrayConstructor, observe?: Observe}} [opts]
|
|
197
|
+
* @param {Function} [report]
|
|
198
|
+
*/
|
|
199
|
+
export const measure = async (fn, opts = {}, report) => {
|
|
200
|
+
const {
|
|
201
|
+
nSeries = 100,
|
|
202
|
+
threshold = 20,
|
|
203
|
+
startFrom = 1,
|
|
204
|
+
timeout = 5,
|
|
205
|
+
DataArray = Array,
|
|
206
|
+
observe
|
|
207
|
+
} = opts;
|
|
142
208
|
report?.('finding-reps');
|
|
143
|
-
const reps =
|
|
209
|
+
const reps =
|
|
210
|
+
startFrom < 0 ? -startFrom : await findLevel(fn, {threshold, startFrom, timeout, observe});
|
|
144
211
|
report?.('found-reps', {reps});
|
|
145
212
|
report?.('starting-benchmarks', {nSeries, reps});
|
|
146
213
|
const start = performance.now(),
|
|
147
|
-
data = await benchmarkSeries(fn, reps, {nSeries, timeout, DataArray}),
|
|
214
|
+
data = await benchmarkSeries(fn, reps, {nSeries, timeout, DataArray, observe}),
|
|
148
215
|
finish = performance.now(),
|
|
149
216
|
result = {data, reps, time: finish - start};
|
|
150
217
|
report?.('finished-benchmarks', {...result, nSeries});
|
|
151
218
|
return new Stats(result);
|
|
152
219
|
};
|
|
153
220
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
) => {
|
|
221
|
+
/**
|
|
222
|
+
* @param {{nSeries?: number, threshold?: number, startFrom?: number, timeout?: number, DataArray?: ArrayConstructor, observe?: Observe}} [opts]
|
|
223
|
+
* @param {Function} [report]
|
|
224
|
+
*/
|
|
225
|
+
export const measurePar = async (fn, opts = {}, report) => {
|
|
226
|
+
const {
|
|
227
|
+
nSeries = 100,
|
|
228
|
+
threshold = 20,
|
|
229
|
+
startFrom = 1,
|
|
230
|
+
timeout = 5,
|
|
231
|
+
DataArray = Array,
|
|
232
|
+
observe
|
|
233
|
+
} = opts;
|
|
159
234
|
report?.('finding-reps');
|
|
160
|
-
const reps =
|
|
235
|
+
const reps =
|
|
236
|
+
startFrom < 0 ? -startFrom : await findLevel(fn, {threshold, startFrom, timeout, observe});
|
|
161
237
|
report?.('found-reps', {reps});
|
|
162
238
|
report?.('starting-benchmarks', {nSeries, reps});
|
|
163
239
|
const start = performance.now(),
|
|
164
|
-
data = await benchmarkSeriesPar(fn, reps, {nSeries, DataArray}),
|
|
240
|
+
data = await benchmarkSeriesPar(fn, reps, {nSeries, DataArray, observe}),
|
|
165
241
|
finish = performance.now(),
|
|
166
242
|
result = {data, reps, time: finish - start};
|
|
167
243
|
report?.('finished-benchmarks', {...result, nSeries});
|