@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 CHANGED
@@ -34,7 +34,7 @@
34
34
 
35
35
  ## About
36
36
 
37
- Benchmarking & profiling utilities w/ various statistics & formatters (CSV, JSON, Markdown etc.).
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.06 KB
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<void>;
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
- benchResult
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: () => void, opts?: Partial<BenchmarkOpts>) => BenchmarkResult;
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 _opts = { ...DEFAULT_OPTS, ...opts };
14
- let { iter, size, extSize, warmup, output, format } = _opts;
15
- output && outputString(format.start(_opts));
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, _opts));
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: _opts.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.6.53",
4
- "description": "Benchmarking & profiling utilities w/ various statistics & formatters (CSV, JSON, Markdown etc.)",
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.26",
45
- "@thi.ng/timestamp": "^1.1.45"
44
+ "@thi.ng/api": "^8.12.27",
45
+ "@thi.ng/timestamp": "^1.1.46"
46
46
  },
47
47
  "devDependencies": {
48
- "@types/node": "^25.6.2",
49
- "esbuild": "^0.28.0",
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": "c47c56420bcae2e0477d252a497be44f08ca185a"
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
- this.start(id);
251
- this.end(id);
252
- }, ~~(iter / 10));
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 { benchmark, DEFAULT_OPTS, outputString } from "./benchmark.js";
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
- timedResult
26
+ timedAsync,
27
+ timedResult,
28
+ timedResultAsync
16
29
  };