@thi.ng/bench 3.6.53 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/api.d.ts +4 -1
- package/bench.d.ts +15 -0
- package/bench.js +22 -2
- package/benchmark.d.ts +17 -1
- package/benchmark.js +23 -6
- package/package.json +8 -7
- package/profiler.d.ts +15 -0
- package/profiler.js +34 -4
- package/suite.d.ts +8 -1
- package/suite.js +24 -2
- package/timed.d.ts +13 -0
- package/timed.js +14 -1
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
## About
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
Async/sync benchmarking & profiling utilities with various statistics & formatters (CSV, JSON, Markdown etc.).
|
|
38
38
|
|
|
39
39
|
> [!IMPORTANT]
|
|
40
40
|
> As of 2024-12-27, all timestamp-related functions have been extracted/migrated
|
|
@@ -78,7 +78,7 @@ For Node.js REPL:
|
|
|
78
78
|
const bench = await import("@thi.ng/bench");
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
Package sizes (brotli'd, pre-treeshake): ESM: 2.
|
|
81
|
+
Package sizes (brotli'd, pre-treeshake): ESM: 2.22 KB
|
|
82
82
|
|
|
83
83
|
## Dependencies
|
|
84
84
|
|
|
@@ -114,14 +114,14 @@ directory are using this package:
|
|
|
114
114
|
|
|
115
115
|
```ts id:test-functions
|
|
116
116
|
// functions to benchmark...
|
|
117
|
-
const fib = (n: number) =>
|
|
117
|
+
const fib = (n: number): number =>
|
|
118
118
|
n > 2
|
|
119
119
|
? fib(n - 1) + fib(n - 2)
|
|
120
120
|
: n > 0
|
|
121
121
|
? 1
|
|
122
122
|
: 0;
|
|
123
123
|
|
|
124
|
-
const fib2 = (n: number) => {
|
|
124
|
+
const fib2 = (n: number): number => {
|
|
125
125
|
const res = [0, 1];
|
|
126
126
|
for(let i = 2; i <= n; i++) {
|
|
127
127
|
res[i] = res[i - 1] + res[i - 2];
|
package/api.d.ts
CHANGED
|
@@ -146,13 +146,16 @@ export interface Benchmark {
|
|
|
146
146
|
* Benchmark function. Will be called `size` times per `iter`ation (see
|
|
147
147
|
* {@link BenchmarkOpts}).
|
|
148
148
|
*/
|
|
149
|
-
fn: Fn0<
|
|
149
|
+
fn: Fn0<any>;
|
|
150
150
|
/**
|
|
151
151
|
* Optional & partial benchmark specific option overrides (merged with opts
|
|
152
152
|
* given to suite)
|
|
153
153
|
*/
|
|
154
154
|
opts?: Partial<OptsWithoutTitle>;
|
|
155
155
|
}
|
|
156
|
+
export interface AsyncBenchmark extends Omit<Benchmark, "fn"> {
|
|
157
|
+
fn: Fn0<Promise<any>>;
|
|
158
|
+
}
|
|
156
159
|
export declare const setPrecision: (prec: number) => number;
|
|
157
160
|
export declare const FLOAT: (x: number) => string;
|
|
158
161
|
export declare const EMPTY: () => string;
|
package/bench.d.ts
CHANGED
|
@@ -8,6 +8,14 @@ import type { TimingResult } from "./api.js";
|
|
|
8
8
|
* @param n - number of iterations
|
|
9
9
|
*/
|
|
10
10
|
export declare const bench: <T>(fn: () => T, n?: number, prefix?: string) => T;
|
|
11
|
+
/**
|
|
12
|
+
* Async version of {@link bench}.
|
|
13
|
+
*
|
|
14
|
+
* @param fn
|
|
15
|
+
* @param n
|
|
16
|
+
* @param prefix
|
|
17
|
+
*/
|
|
18
|
+
export declare const benchAsync: <T>(fn: () => Promise<T>, n?: number, prefix?: string) => Promise<T>;
|
|
11
19
|
/**
|
|
12
20
|
* Similar to {@link bench}, but produces no output and instead returns
|
|
13
21
|
* tuple of `fn`'s last result and the grand total time measurement.
|
|
@@ -16,4 +24,11 @@ export declare const bench: <T>(fn: () => T, n?: number, prefix?: string) => T;
|
|
|
16
24
|
* @param n - number of iterations
|
|
17
25
|
*/
|
|
18
26
|
export declare const benchResult: <T>(fn: () => T, n?: number) => TimingResult<T>;
|
|
27
|
+
/**
|
|
28
|
+
* Async version of {@link benchResult}.
|
|
29
|
+
*
|
|
30
|
+
* @param fn
|
|
31
|
+
* @param n
|
|
32
|
+
*/
|
|
33
|
+
export declare const benchResultAsync: <T>(fn: () => Promise<T>, n?: number) => Promise<TimingResult<T>>;
|
|
19
34
|
//# sourceMappingURL=bench.d.ts.map
|
package/bench.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { timed, timedResult } from "./timed.js";
|
|
1
|
+
import { timed, timedAsync, timedResult, timedResultAsync } from "./timed.js";
|
|
2
2
|
const bench = (fn, n = 1e6, prefix = "") => {
|
|
3
3
|
let res;
|
|
4
4
|
return timed(() => {
|
|
@@ -8,6 +8,15 @@ const bench = (fn, n = 1e6, prefix = "") => {
|
|
|
8
8
|
return res;
|
|
9
9
|
}, prefix);
|
|
10
10
|
};
|
|
11
|
+
const benchAsync = (fn, n = 1e6, prefix = "") => {
|
|
12
|
+
let res;
|
|
13
|
+
return timedAsync(async () => {
|
|
14
|
+
while (n-- > 0) {
|
|
15
|
+
res = await fn();
|
|
16
|
+
}
|
|
17
|
+
return res;
|
|
18
|
+
}, prefix);
|
|
19
|
+
};
|
|
11
20
|
const benchResult = (fn, n = 1e6) => {
|
|
12
21
|
let res;
|
|
13
22
|
return timedResult(() => {
|
|
@@ -17,7 +26,18 @@ const benchResult = (fn, n = 1e6) => {
|
|
|
17
26
|
return res;
|
|
18
27
|
});
|
|
19
28
|
};
|
|
29
|
+
const benchResultAsync = (fn, n = 1e6) => {
|
|
30
|
+
let res;
|
|
31
|
+
return timedResultAsync(async () => {
|
|
32
|
+
while (n-- > 0) {
|
|
33
|
+
res = await fn();
|
|
34
|
+
}
|
|
35
|
+
return res;
|
|
36
|
+
});
|
|
37
|
+
};
|
|
20
38
|
export {
|
|
21
39
|
bench,
|
|
22
|
-
|
|
40
|
+
benchAsync,
|
|
41
|
+
benchResult,
|
|
42
|
+
benchResultAsync
|
|
23
43
|
};
|
package/benchmark.d.ts
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import type { BenchmarkOpts, BenchmarkResult } from "./api.js";
|
|
2
2
|
export declare const DEFAULT_OPTS: BenchmarkOpts;
|
|
3
|
-
export declare const benchmark: (fn: () =>
|
|
3
|
+
export declare const benchmark: (fn: () => any, opts?: Partial<BenchmarkOpts>) => BenchmarkResult;
|
|
4
|
+
/**
|
|
5
|
+
* Async version of {@link benchmark}.
|
|
6
|
+
*
|
|
7
|
+
* @param fn
|
|
8
|
+
* @param opts
|
|
9
|
+
*/
|
|
10
|
+
export declare const benchmarkAsync: (fn: () => Promise<any>, opts?: Partial<BenchmarkOpts>) => Promise<BenchmarkResult>;
|
|
11
|
+
/**
|
|
12
|
+
* Helper for {@link benchmark}, {@link benchmarkAsync}. Takes an array of
|
|
13
|
+
* benchmark samples and computes result statistics using provided options. If
|
|
14
|
+
* `opts.output` is true, also outputs formatted result.
|
|
15
|
+
*
|
|
16
|
+
* @param samples
|
|
17
|
+
* @param opts
|
|
18
|
+
*/
|
|
19
|
+
export declare const benchmarkResult: (samples: number[], { iter, size, extSize, format, output, title }: BenchmarkOpts) => BenchmarkResult;
|
|
4
20
|
/**
|
|
5
21
|
* Only outputs non-empty strings to console.
|
|
6
22
|
*
|
package/benchmark.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { benchResult } from "./bench.js";
|
|
1
|
+
import { benchResult, benchResultAsync } from "./bench.js";
|
|
2
2
|
import { FORMAT_DEFAULT } from "./format/default.js";
|
|
3
3
|
const DEFAULT_OPTS = {
|
|
4
4
|
title: "benchmark",
|
|
@@ -10,15 +10,30 @@ const DEFAULT_OPTS = {
|
|
|
10
10
|
format: FORMAT_DEFAULT
|
|
11
11
|
};
|
|
12
12
|
const benchmark = (fn, opts) => {
|
|
13
|
-
const
|
|
14
|
-
let { iter, size,
|
|
15
|
-
output && outputString(format.start(
|
|
13
|
+
const $opts = { ...DEFAULT_OPTS, ...opts };
|
|
14
|
+
let { iter, size, warmup, output, format } = $opts;
|
|
15
|
+
output && outputString(format.start($opts));
|
|
16
16
|
const t = benchResult(fn, warmup * size)[1];
|
|
17
|
-
output && outputString(format.warmup(t,
|
|
17
|
+
output && outputString(format.warmup(t, $opts));
|
|
18
18
|
const samples = [];
|
|
19
19
|
for (let i = iter; i-- > 0; ) {
|
|
20
20
|
samples.push(benchResult(fn, size)[1]);
|
|
21
21
|
}
|
|
22
|
+
return benchmarkResult(samples, $opts);
|
|
23
|
+
};
|
|
24
|
+
const benchmarkAsync = async (fn, opts) => {
|
|
25
|
+
const $opts = { ...DEFAULT_OPTS, ...opts };
|
|
26
|
+
let { iter, size, warmup, output, format } = $opts;
|
|
27
|
+
output && outputString(format.start($opts));
|
|
28
|
+
const t = (await benchResultAsync(fn, warmup * size))[1];
|
|
29
|
+
output && outputString(format.warmup(t, $opts));
|
|
30
|
+
const samples = [];
|
|
31
|
+
for (let i = iter; i-- > 0; ) {
|
|
32
|
+
samples.push((await benchResultAsync(fn, size))[1]);
|
|
33
|
+
}
|
|
34
|
+
return benchmarkResult(samples, $opts);
|
|
35
|
+
};
|
|
36
|
+
const benchmarkResult = (samples, { iter, size, extSize, format, output, title }) => {
|
|
22
37
|
samples.sort((a, b) => a - b);
|
|
23
38
|
const total = samples.reduce((acc, x) => acc + x, 0);
|
|
24
39
|
const freq = iter * size * extSize * 1e3 / total;
|
|
@@ -32,7 +47,7 @@ const benchmark = (fn, opts) => {
|
|
|
32
47
|
samples.reduce((acc, x) => acc + (mean - x) ** 2, 0) / iter
|
|
33
48
|
) / mean * 100;
|
|
34
49
|
const res = {
|
|
35
|
-
title
|
|
50
|
+
title,
|
|
36
51
|
iter,
|
|
37
52
|
size,
|
|
38
53
|
total,
|
|
@@ -52,5 +67,7 @@ const outputString = (str) => str !== "" && console.log(str);
|
|
|
52
67
|
export {
|
|
53
68
|
DEFAULT_OPTS,
|
|
54
69
|
benchmark,
|
|
70
|
+
benchmarkAsync,
|
|
71
|
+
benchmarkResult,
|
|
55
72
|
outputString
|
|
56
73
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/bench",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.7.0",
|
|
4
|
+
"description": "Async/sync benchmarking & profiling utilities with various statistics & formatters (CSV, JSON, Markdown etc.)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
7
7
|
"typings": "./index.d.ts",
|
|
@@ -41,17 +41,18 @@
|
|
|
41
41
|
"tool:tangle": "../../node_modules/.bin/tangle src/**/*.ts"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@thi.ng/api": "^8.12.
|
|
45
|
-
"@thi.ng/timestamp": "^1.1.
|
|
44
|
+
"@thi.ng/api": "^8.12.27",
|
|
45
|
+
"@thi.ng/timestamp": "^1.1.46"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@types/node": "^25.
|
|
49
|
-
"esbuild": "^0.28.
|
|
48
|
+
"@types/node": "^25.9.4",
|
|
49
|
+
"esbuild": "^0.28.1",
|
|
50
50
|
"tools": "workspace:^",
|
|
51
51
|
"typedoc": "^0.28.19",
|
|
52
52
|
"typescript": "^6.0.3"
|
|
53
53
|
},
|
|
54
54
|
"keywords": [
|
|
55
|
+
"async",
|
|
55
56
|
"benchmark",
|
|
56
57
|
"bigint",
|
|
57
58
|
"csv",
|
|
@@ -123,5 +124,5 @@
|
|
|
123
124
|
"tag": "benchmark",
|
|
124
125
|
"year": 2018
|
|
125
126
|
},
|
|
126
|
-
"gitHead": "
|
|
127
|
+
"gitHead": "4fc0df9a97bec92742f288aa059c04e9ff24cf41"
|
|
127
128
|
}
|
package/profiler.d.ts
CHANGED
|
@@ -164,6 +164,14 @@ export declare class Profiler implements IDeref<IObjectOf<ProfileResult>>, IEnab
|
|
|
164
164
|
* @param fn
|
|
165
165
|
*/
|
|
166
166
|
profile<T>(id: string, fn: FnAny<T>, ...args: any[]): T;
|
|
167
|
+
/**
|
|
168
|
+
* Async version of {@link Profiler.profile}.
|
|
169
|
+
*
|
|
170
|
+
* @param id
|
|
171
|
+
* @param fn
|
|
172
|
+
* @param args
|
|
173
|
+
*/
|
|
174
|
+
profileAsync<T>(id: string, fn: FnAny<Promise<T>>, ...args: any[]): Promise<T>;
|
|
167
175
|
/**
|
|
168
176
|
* Higher-order version of {@link Profiler.profile}. Takes a profile `id`
|
|
169
177
|
* and vararg function `fn`. Returns new function which when called, calls
|
|
@@ -202,6 +210,13 @@ export declare class Profiler implements IDeref<IObjectOf<ProfileResult>>, IEnab
|
|
|
202
210
|
* @param fn
|
|
203
211
|
*/
|
|
204
212
|
wrap<T>(id: string, fn: FnAny<T>): (...args: any[]) => T;
|
|
213
|
+
/**
|
|
214
|
+
* Async version of {@link Profiler.wrap}.
|
|
215
|
+
*
|
|
216
|
+
* @param id
|
|
217
|
+
* @param fn
|
|
218
|
+
*/
|
|
219
|
+
wrapAsync<T>(id: string, fn: FnAny<Promise<T>>): (...args: any[]) => Promise<T>;
|
|
205
220
|
/**
|
|
206
221
|
* Estimates the internal overhead of the {@link Profiler.start} and
|
|
207
222
|
* {@link Profiler.end} methods by performing given number of `iter`ations
|
package/profiler.js
CHANGED
|
@@ -185,6 +185,19 @@ class Profiler {
|
|
|
185
185
|
this.end(id);
|
|
186
186
|
return res;
|
|
187
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Async version of {@link Profiler.profile}.
|
|
190
|
+
*
|
|
191
|
+
* @param id
|
|
192
|
+
* @param fn
|
|
193
|
+
* @param args
|
|
194
|
+
*/
|
|
195
|
+
async profileAsync(id, fn, ...args) {
|
|
196
|
+
this.start(id);
|
|
197
|
+
const res = await fn.apply(null, args);
|
|
198
|
+
this.end(id);
|
|
199
|
+
return res;
|
|
200
|
+
}
|
|
188
201
|
/**
|
|
189
202
|
* Higher-order version of {@link Profiler.profile}. Takes a profile `id`
|
|
190
203
|
* and vararg function `fn`. Returns new function which when called, calls
|
|
@@ -230,6 +243,20 @@ class Profiler {
|
|
|
230
243
|
return res;
|
|
231
244
|
};
|
|
232
245
|
}
|
|
246
|
+
/**
|
|
247
|
+
* Async version of {@link Profiler.wrap}.
|
|
248
|
+
*
|
|
249
|
+
* @param id
|
|
250
|
+
* @param fn
|
|
251
|
+
*/
|
|
252
|
+
wrapAsync(id, fn) {
|
|
253
|
+
return async (...args) => {
|
|
254
|
+
this.start(id);
|
|
255
|
+
const res = await fn.apply(null, args);
|
|
256
|
+
this.end(id);
|
|
257
|
+
return res;
|
|
258
|
+
};
|
|
259
|
+
}
|
|
233
260
|
/**
|
|
234
261
|
* Estimates the internal overhead of the {@link Profiler.start} and
|
|
235
262
|
* {@link Profiler.end} methods by performing given number of `iter`ations
|
|
@@ -246,10 +273,13 @@ class Profiler {
|
|
|
246
273
|
let total = 0;
|
|
247
274
|
for (let i = 0; i < 10; i++) {
|
|
248
275
|
const id = `prof-${i}`;
|
|
249
|
-
const [_, taken] = benchResult(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
276
|
+
const [_, taken] = benchResult(
|
|
277
|
+
() => {
|
|
278
|
+
this.start(id);
|
|
279
|
+
this.end(id);
|
|
280
|
+
},
|
|
281
|
+
~~(iter / 10)
|
|
282
|
+
);
|
|
253
283
|
total += taken;
|
|
254
284
|
}
|
|
255
285
|
this._overhead = total / iter;
|
package/suite.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
import type { Benchmark, BenchmarkResult, BenchmarkSuiteOpts } from "./api.js";
|
|
1
|
+
import type { AsyncBenchmark, Benchmark, BenchmarkResult, BenchmarkSuiteOpts } from "./api.js";
|
|
2
2
|
export declare const suite: (cases: Benchmark[], opts?: Partial<BenchmarkSuiteOpts>) => BenchmarkResult[];
|
|
3
|
+
/**
|
|
4
|
+
* Async version of {@link suite}.
|
|
5
|
+
*
|
|
6
|
+
* @param cases
|
|
7
|
+
* @param opts
|
|
8
|
+
*/
|
|
9
|
+
export declare const suiteAsync: (cases: AsyncBenchmark[], opts?: Partial<BenchmarkSuiteOpts>) => Promise<BenchmarkResult[]>;
|
|
3
10
|
//# sourceMappingURL=suite.d.ts.map
|
package/suite.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
benchmark,
|
|
3
|
+
benchmarkAsync,
|
|
4
|
+
DEFAULT_OPTS,
|
|
5
|
+
outputString
|
|
6
|
+
} from "./benchmark.js";
|
|
2
7
|
const suite = (cases, opts) => {
|
|
3
8
|
const _opts = {
|
|
4
9
|
...DEFAULT_OPTS,
|
|
@@ -13,6 +18,23 @@ const suite = (cases, opts) => {
|
|
|
13
18
|
_opts.output && outputString(_opts.format.suffix());
|
|
14
19
|
return results;
|
|
15
20
|
};
|
|
21
|
+
const suiteAsync = async (cases, opts) => {
|
|
22
|
+
const _opts = {
|
|
23
|
+
...DEFAULT_OPTS,
|
|
24
|
+
...opts
|
|
25
|
+
};
|
|
26
|
+
_opts.output && outputString(_opts.format.prefix());
|
|
27
|
+
const results = [];
|
|
28
|
+
for (const c of cases) {
|
|
29
|
+
results.push(
|
|
30
|
+
await benchmarkAsync(c.fn, { ..._opts, ...c.opts, title: c.title })
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
_opts.output && outputString(_opts.format.total(results));
|
|
34
|
+
_opts.output && outputString(_opts.format.suffix());
|
|
35
|
+
return results;
|
|
36
|
+
};
|
|
16
37
|
export {
|
|
17
|
-
suite
|
|
38
|
+
suite,
|
|
39
|
+
suiteAsync
|
|
18
40
|
};
|
package/timed.d.ts
CHANGED
|
@@ -8,6 +8,13 @@ import type { TimingResult } from "./api.js";
|
|
|
8
8
|
* @param prefix - log prefix
|
|
9
9
|
*/
|
|
10
10
|
export declare const timed: <T>(fn: () => T, prefix?: string) => T;
|
|
11
|
+
/**
|
|
12
|
+
* Async version of {@link timed}.
|
|
13
|
+
*
|
|
14
|
+
* @param fn
|
|
15
|
+
* @param prefix
|
|
16
|
+
*/
|
|
17
|
+
export declare const timedAsync: <T>(fn: () => Promise<T>, prefix?: string) => Promise<T>;
|
|
11
18
|
/**
|
|
12
19
|
* Similar to {@link timed}, but produces no output and instead returns
|
|
13
20
|
* tuple of `fn`'s result and the time measurement (in milliseconds).
|
|
@@ -15,4 +22,10 @@ export declare const timed: <T>(fn: () => T, prefix?: string) => T;
|
|
|
15
22
|
* @param fn - function to time
|
|
16
23
|
*/
|
|
17
24
|
export declare const timedResult: <T>(fn: () => T) => TimingResult<T>;
|
|
25
|
+
/**
|
|
26
|
+
* Async version of {@link timedResult}.
|
|
27
|
+
*
|
|
28
|
+
* @param fn
|
|
29
|
+
*/
|
|
30
|
+
export declare const timedResultAsync: <T>(fn: () => Promise<T>) => Promise<TimingResult<T>>;
|
|
18
31
|
//# sourceMappingURL=timed.d.ts.map
|
package/timed.js
CHANGED
|
@@ -4,13 +4,26 @@ const timed = (fn, prefix = "") => {
|
|
|
4
4
|
console.log(`${prefix} ${t.toFixed(2)}ms`);
|
|
5
5
|
return res;
|
|
6
6
|
};
|
|
7
|
+
const timedAsync = async (fn, prefix = "") => {
|
|
8
|
+
const [res, t] = await timedResultAsync(fn);
|
|
9
|
+
console.log(`${prefix} ${t.toFixed(2)}ms`);
|
|
10
|
+
return res;
|
|
11
|
+
};
|
|
7
12
|
const timedResult = (fn) => {
|
|
8
13
|
const t0 = now();
|
|
9
14
|
const res = fn();
|
|
10
15
|
const t1 = now();
|
|
11
16
|
return [res, timeDiff(t0, t1)];
|
|
12
17
|
};
|
|
18
|
+
const timedResultAsync = async (fn) => {
|
|
19
|
+
const t0 = now();
|
|
20
|
+
const res = await fn();
|
|
21
|
+
const t1 = now();
|
|
22
|
+
return [res, timeDiff(t0, t1)];
|
|
23
|
+
};
|
|
13
24
|
export {
|
|
14
25
|
timed,
|
|
15
|
-
|
|
26
|
+
timedAsync,
|
|
27
|
+
timedResult,
|
|
28
|
+
timedResultAsync
|
|
16
29
|
};
|