@fuzdev/fuz_util 0.44.0 ā 0.45.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/LICENSE +1 -1
- package/dist/benchmark.d.ts +43 -1
- package/dist/benchmark.d.ts.map +1 -1
- package/dist/benchmark.js +51 -3
- package/dist/benchmark_baseline.d.ts +2 -2
- package/dist/benchmark_baseline.js +3 -3
- package/dist/benchmark_format.d.ts +39 -12
- package/dist/benchmark_format.d.ts.map +1 -1
- package/dist/benchmark_format.js +108 -30
- package/dist/benchmark_stats.d.ts +2 -2
- package/dist/benchmark_stats.d.ts.map +1 -1
- package/dist/benchmark_stats.js +4 -4
- package/dist/benchmark_types.d.ts +6 -0
- package/dist/benchmark_types.d.ts.map +1 -1
- package/dist/package_json.d.ts +0 -1
- package/dist/package_json.d.ts.map +1 -1
- package/dist/package_json.js +0 -6
- package/dist/process.d.ts +4 -0
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +14 -0
- package/package.json +1 -2
- package/src/lib/benchmark.ts +53 -2
- package/src/lib/benchmark_baseline.ts +3 -3
- package/src/lib/benchmark_format.ts +126 -30
- package/src/lib/benchmark_stats.ts +4 -4
- package/src/lib/benchmark_types.ts +7 -0
- package/src/lib/package_json.ts +0 -7
- package/src/lib/process.ts +14 -0
package/LICENSE
CHANGED
package/dist/benchmark.d.ts
CHANGED
|
@@ -135,9 +135,24 @@ export declare class Benchmark {
|
|
|
135
135
|
table(options?: BenchmarkFormatTableOptions): string;
|
|
136
136
|
/**
|
|
137
137
|
* Format results as a Markdown table.
|
|
138
|
+
* @param options - Formatting options (groups for organized output with optional baselines)
|
|
138
139
|
* @returns Formatted markdown string
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* // Standard table
|
|
144
|
+
* console.log(bench.markdown());
|
|
145
|
+
*
|
|
146
|
+
* // Grouped by category with custom baseline
|
|
147
|
+
* console.log(bench.markdown({
|
|
148
|
+
* groups: [
|
|
149
|
+
* { name: 'Format', filter: (r) => r.name.startsWith('format/'), baseline: 'format/prettier' },
|
|
150
|
+
* { name: 'Parse', filter: (r) => r.name.startsWith('parse/') },
|
|
151
|
+
* ]
|
|
152
|
+
* }));
|
|
153
|
+
* ```
|
|
139
154
|
*/
|
|
140
|
-
markdown(): string;
|
|
155
|
+
markdown(options?: BenchmarkFormatTableOptions): string;
|
|
141
156
|
/**
|
|
142
157
|
* Format results as JSON.
|
|
143
158
|
* @param options - Formatting options (pretty, include_timings)
|
|
@@ -150,6 +165,33 @@ export declare class Benchmark {
|
|
|
150
165
|
* @returns Array of benchmark results
|
|
151
166
|
*/
|
|
152
167
|
results(): Array<BenchmarkResult>;
|
|
168
|
+
/**
|
|
169
|
+
* Check if the benchmark has been run and has results.
|
|
170
|
+
* @returns True if results are available
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* if (bench.has_results) {
|
|
175
|
+
* console.log(bench.table());
|
|
176
|
+
* }
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
get has_results(): boolean;
|
|
180
|
+
/**
|
|
181
|
+
* Get results as a map for convenient lookup by task name.
|
|
182
|
+
* Returns a new Map each call to prevent external mutation.
|
|
183
|
+
* @returns Map of task name to benchmark result
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```ts
|
|
187
|
+
* const results_map = bench.results_by_name();
|
|
188
|
+
* const slugify_result = results_map.get('slugify');
|
|
189
|
+
* if (slugify_result) {
|
|
190
|
+
* console.log(`slugify: ${slugify_result.stats.ops_per_second} ops/sec`);
|
|
191
|
+
* }
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
results_by_name(): Map<string, BenchmarkResult>;
|
|
153
195
|
/**
|
|
154
196
|
* Reset the benchmark results.
|
|
155
197
|
* Keeps tasks intact so benchmarks can be rerun.
|
package/dist/benchmark.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"benchmark.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/benchmark.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,OAAO,
|
|
1
|
+
{"version":3,"file":"benchmark.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/benchmark.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,OAAO,EAON,KAAK,0BAA0B,EAC/B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EACX,eAAe,EACf,aAAa,EACb,eAAe,EACf,2BAA2B,EAC3B,MAAM,sBAAsB,CAAC;AAgD9B;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gBAAgB,GAC5B,IAAI,MAAM,OAAO,EACjB,YAAY,MAAM,EAClB,aAAa,OAAO,KAClB,OAAO,CAAC,OAAO,CAyBjB,CAAC;AAEF;;GAEG;AACH,qBAAa,SAAS;;gBAMT,MAAM,GAAE,eAAoB;IAcxC;;;;;;;;;;;;;;;;;;OAkBG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,GAAG,IAAI;IAC1C,GAAG,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAkB9B;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAS1B;;;;;;;;;;;;;OAaG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IASxB;;;;;;;;;;;;;;OAcG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IASxB;;;OAGG;IACG,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAqH5C;;;;;;;;;;;;;;;;;;OAkBG;IACH,KAAK,CAAC,OAAO,CAAC,EAAE,2BAA2B,GAAG,MAAM;IAMpD;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,2BAA2B,GAAG,MAAM;IAMvD;;;;OAIG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,0BAA0B,GAAG,MAAM;IAIlD;;;;OAIG;IACH,OAAO,IAAI,KAAK,CAAC,eAAe,CAAC;IAIjC;;;;;;;;;;OAUG;IACH,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;;;;;;;;;;;;OAaG;IACH,eAAe,IAAI,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC;IAI/C;;;;OAIG;IACH,KAAK,IAAI,IAAI;IAKb;;;;OAIG;IACH,KAAK,IAAI,IAAI;IAMb;;;;;;;;;;;OAWG;IACH,OAAO,IAAI,MAAM;CA+BjB"}
|
package/dist/benchmark.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
import { is_promise, wait } from './async.js';
|
|
22
22
|
import { BenchmarkStats } from './benchmark_stats.js';
|
|
23
23
|
import { timer_default, time_unit_detect_best, time_format } from './time.js';
|
|
24
|
-
import { benchmark_format_table, benchmark_format_table_grouped, benchmark_format_markdown, benchmark_format_json, benchmark_format_number, } from './benchmark_format.js';
|
|
24
|
+
import { benchmark_format_table, benchmark_format_table_grouped, benchmark_format_markdown, benchmark_format_markdown_grouped, benchmark_format_json, benchmark_format_number, } from './benchmark_format.js';
|
|
25
25
|
// Default configuration values
|
|
26
26
|
const DEFAULT_DURATION_MS = 1000;
|
|
27
27
|
const DEFAULT_WARMUP_ITERATIONS = 10;
|
|
@@ -328,10 +328,27 @@ export class Benchmark {
|
|
|
328
328
|
}
|
|
329
329
|
/**
|
|
330
330
|
* Format results as a Markdown table.
|
|
331
|
+
* @param options - Formatting options (groups for organized output with optional baselines)
|
|
331
332
|
* @returns Formatted markdown string
|
|
333
|
+
*
|
|
334
|
+
* @example
|
|
335
|
+
* ```ts
|
|
336
|
+
* // Standard table
|
|
337
|
+
* console.log(bench.markdown());
|
|
338
|
+
*
|
|
339
|
+
* // Grouped by category with custom baseline
|
|
340
|
+
* console.log(bench.markdown({
|
|
341
|
+
* groups: [
|
|
342
|
+
* { name: 'Format', filter: (r) => r.name.startsWith('format/'), baseline: 'format/prettier' },
|
|
343
|
+
* { name: 'Parse', filter: (r) => r.name.startsWith('parse/') },
|
|
344
|
+
* ]
|
|
345
|
+
* }));
|
|
346
|
+
* ```
|
|
332
347
|
*/
|
|
333
|
-
markdown() {
|
|
334
|
-
return
|
|
348
|
+
markdown(options) {
|
|
349
|
+
return options?.groups
|
|
350
|
+
? benchmark_format_markdown_grouped(this.#results, options.groups)
|
|
351
|
+
: benchmark_format_markdown(this.#results);
|
|
335
352
|
}
|
|
336
353
|
/**
|
|
337
354
|
* Format results as JSON.
|
|
@@ -349,6 +366,37 @@ export class Benchmark {
|
|
|
349
366
|
results() {
|
|
350
367
|
return [...this.#results];
|
|
351
368
|
}
|
|
369
|
+
/**
|
|
370
|
+
* Check if the benchmark has been run and has results.
|
|
371
|
+
* @returns True if results are available
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* ```ts
|
|
375
|
+
* if (bench.has_results) {
|
|
376
|
+
* console.log(bench.table());
|
|
377
|
+
* }
|
|
378
|
+
* ```
|
|
379
|
+
*/
|
|
380
|
+
get has_results() {
|
|
381
|
+
return this.#results.length > 0;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Get results as a map for convenient lookup by task name.
|
|
385
|
+
* Returns a new Map each call to prevent external mutation.
|
|
386
|
+
* @returns Map of task name to benchmark result
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* ```ts
|
|
390
|
+
* const results_map = bench.results_by_name();
|
|
391
|
+
* const slugify_result = results_map.get('slugify');
|
|
392
|
+
* if (slugify_result) {
|
|
393
|
+
* console.log(`slugify: ${slugify_result.stats.ops_per_second} ops/sec`);
|
|
394
|
+
* }
|
|
395
|
+
* ```
|
|
396
|
+
*/
|
|
397
|
+
results_by_name() {
|
|
398
|
+
return new Map(this.#results.map((r) => [r.name, r]));
|
|
399
|
+
}
|
|
352
400
|
/**
|
|
353
401
|
* Reset the benchmark results.
|
|
354
402
|
* Keeps tasks intact so benchmarks can be rerun.
|
|
@@ -11,7 +11,7 @@ import { type BenchmarkComparison } from './benchmark_stats.js';
|
|
|
11
11
|
export declare const BenchmarkBaselineEntry: z.ZodObject<{
|
|
12
12
|
name: z.ZodString;
|
|
13
13
|
mean_ns: z.ZodNumber;
|
|
14
|
-
|
|
14
|
+
p50_ns: z.ZodNumber;
|
|
15
15
|
std_dev_ns: z.ZodNumber;
|
|
16
16
|
min_ns: z.ZodNumber;
|
|
17
17
|
max_ns: z.ZodNumber;
|
|
@@ -35,7 +35,7 @@ export declare const BenchmarkBaseline: z.ZodObject<{
|
|
|
35
35
|
entries: z.ZodArray<z.ZodObject<{
|
|
36
36
|
name: z.ZodString;
|
|
37
37
|
mean_ns: z.ZodNumber;
|
|
38
|
-
|
|
38
|
+
p50_ns: z.ZodNumber;
|
|
39
39
|
std_dev_ns: z.ZodNumber;
|
|
40
40
|
min_ns: z.ZodNumber;
|
|
41
41
|
max_ns: z.ZodNumber;
|
|
@@ -10,14 +10,14 @@ import { git_info_get } from './git.js';
|
|
|
10
10
|
import { benchmark_stats_compare, } from './benchmark_stats.js';
|
|
11
11
|
import { stats_confidence_interval_from_summary } from './stats.js';
|
|
12
12
|
// Version for forward compatibility - increment when schema changes
|
|
13
|
-
const BASELINE_VERSION =
|
|
13
|
+
const BASELINE_VERSION = 2;
|
|
14
14
|
/**
|
|
15
15
|
* Schema for a single benchmark entry in the baseline.
|
|
16
16
|
*/
|
|
17
17
|
export const BenchmarkBaselineEntry = z.object({
|
|
18
18
|
name: z.string(),
|
|
19
19
|
mean_ns: z.number(),
|
|
20
|
-
|
|
20
|
+
p50_ns: z.number(),
|
|
21
21
|
std_dev_ns: z.number(),
|
|
22
22
|
min_ns: z.number(),
|
|
23
23
|
max_ns: z.number(),
|
|
@@ -48,7 +48,7 @@ const results_to_entries = (results) => {
|
|
|
48
48
|
return results.map((r) => ({
|
|
49
49
|
name: r.name,
|
|
50
50
|
mean_ns: r.stats.mean_ns,
|
|
51
|
-
|
|
51
|
+
p50_ns: r.stats.p50_ns,
|
|
52
52
|
std_dev_ns: r.stats.std_dev_ns,
|
|
53
53
|
min_ns: r.stats.min_ns,
|
|
54
54
|
max_ns: r.stats.max_ns,
|
|
@@ -3,36 +3,63 @@ import type { BenchmarkResult, BenchmarkGroup } from './benchmark_types.js';
|
|
|
3
3
|
* Format results as an ASCII table with percentiles, min/max, and relative performance.
|
|
4
4
|
* All times use the same unit for easy comparison.
|
|
5
5
|
* @param results - Array of benchmark results
|
|
6
|
+
* @param baseline - Optional task name to use as baseline for comparison (defaults to fastest)
|
|
6
7
|
* @returns Formatted table string with enhanced metrics
|
|
7
8
|
*
|
|
8
9
|
* @example
|
|
9
10
|
* ```ts
|
|
10
11
|
* console.log(benchmark_format_table(results));
|
|
11
|
-
* //
|
|
12
|
-
* // ā Task Name ā ops/sec ā
|
|
13
|
-
* //
|
|
14
|
-
* // ā slugify v2 ā 1,237,144 ā
|
|
15
|
-
* // ā slugify ā 261,619 ā
|
|
16
|
-
* //
|
|
12
|
+
* // āāāāāāāāāāāāāāā¬āāāāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā
|
|
13
|
+
* // ā Task Name ā ops/sec ā p50 (μs) ā p75 (μs) ā p90 (μs) ā p95 (μs) ā p99 (μs) ā min (μs) ā max (μs) ā vs Best ā
|
|
14
|
+
* // āāāāāāāāāāāāāāā¼āāāāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¤
|
|
15
|
+
* // ā slugify v2 ā 1,237,144 ā 0.81 ā 0.85 ā 0.89 ā 0.95 ā 1.20 ā 0.72 ā 2.45 ā baseline ā
|
|
16
|
+
* // ā slugify ā 261,619 ā 3.82 ā 3.95 ā 4.12 ā 4.35 ā 5.10 ā 3.21 ā 12.45 ā 4.73x ā
|
|
17
|
+
* // āāāāāāāāāāāāāāā“āāāāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā
|
|
17
18
|
* ```
|
|
18
19
|
*/
|
|
19
|
-
export declare const benchmark_format_table: (results: Array<BenchmarkResult
|
|
20
|
+
export declare const benchmark_format_table: (results: Array<BenchmarkResult>, baseline?: string) => string;
|
|
20
21
|
/**
|
|
21
22
|
* Format results as a Markdown table with key metrics.
|
|
22
23
|
* All times use the same unit for easy comparison.
|
|
23
24
|
* @param results - Array of benchmark results
|
|
25
|
+
* @param baseline - Optional task name to use as baseline for comparison (defaults to fastest)
|
|
24
26
|
* @returns Formatted markdown table string
|
|
25
27
|
*
|
|
26
28
|
* @example
|
|
27
29
|
* ```ts
|
|
28
30
|
* console.log(benchmark_format_markdown(results));
|
|
29
|
-
* // | Task Name | ops/sec |
|
|
30
|
-
* //
|
|
31
|
-
* // | slugify v2 | 1,237,144 | 0.81
|
|
32
|
-
* // | slugify | 261,619 | 3.82
|
|
31
|
+
* // | Task Name | ops/sec | p50 (μs) | p75 (μs) | p90 (μs) | p95 (μs) | p99 (μs) | min (μs) | max (μs) | vs Best |
|
|
32
|
+
* // |------------|------------|----------|----------|----------|----------|----------|----------|----------|----------|
|
|
33
|
+
* // | slugify v2 | 1,237,144 | 0.81 | 0.85 | 0.89 | 0.95 | 1.20 | 0.72 | 2.45 | baseline |
|
|
34
|
+
* // | slugify | 261,619 | 3.82 | 3.95 | 4.12 | 4.35 | 5.10 | 3.21 | 12.45 | 4.73x |
|
|
33
35
|
* ```
|
|
34
36
|
*/
|
|
35
|
-
export declare const benchmark_format_markdown: (results: Array<BenchmarkResult
|
|
37
|
+
export declare const benchmark_format_markdown: (results: Array<BenchmarkResult>, baseline?: string) => string;
|
|
38
|
+
/**
|
|
39
|
+
* Format results as grouped Markdown tables with headers between groups.
|
|
40
|
+
* @param results - Array of benchmark results
|
|
41
|
+
* @param groups - Array of group definitions
|
|
42
|
+
* @returns Formatted markdown string with group headers and tables
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* const groups = [
|
|
47
|
+
* { name: 'Fast Paths', filter: (r) => r.name.includes('fast'), baseline: 'fast/reference' },
|
|
48
|
+
* { name: 'Slow Paths', filter: (r) => r.name.includes('slow') },
|
|
49
|
+
* ];
|
|
50
|
+
* console.log(benchmark_format_markdown_grouped(results, groups));
|
|
51
|
+
* // ### Fast Paths
|
|
52
|
+
* // | Task Name | ops/sec | ... | vs fast/reference |
|
|
53
|
+
* // |-----------|---------|-----|-------------------|
|
|
54
|
+
* // | ... | ... | ... | ... |
|
|
55
|
+
* //
|
|
56
|
+
* // ### Slow Paths
|
|
57
|
+
* // | Task Name | ops/sec | ... | vs Best |
|
|
58
|
+
* // |-----------|---------|-----|---------|
|
|
59
|
+
* // | ... | ... | ... | ... |
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare const benchmark_format_markdown_grouped: (results: Array<BenchmarkResult>, groups: Array<BenchmarkGroup>) => string;
|
|
36
63
|
export interface BenchmarkFormatJsonOptions {
|
|
37
64
|
/** Whether to pretty-print (default: true) */
|
|
38
65
|
pretty?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"benchmark_format.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/benchmark_format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,eAAe,EAAE,cAAc,EAAC,MAAM,sBAAsB,CAAC;AAK1E
|
|
1
|
+
{"version":3,"file":"benchmark_format.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/benchmark_format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,eAAe,EAAE,cAAc,EAAC,MAAM,sBAAsB,CAAC;AAK1E;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,sBAAsB,GAClC,SAAS,KAAK,CAAC,eAAe,CAAC,EAC/B,WAAW,MAAM,KACf,MAiGF,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,yBAAyB,GACrC,SAAS,KAAK,CAAC,eAAe,CAAC,EAC/B,WAAW,MAAM,KACf,MA4FF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,iCAAiC,GAC7C,SAAS,KAAK,CAAC,eAAe,CAAC,EAC/B,QAAQ,KAAK,CAAC,cAAc,CAAC,KAC3B,MA2BF,CAAC;AAEF,MAAM,WAAW,0BAA0B;IAC1C,8CAA8C;IAC9C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,0EAA0E;IAC1E,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,GACjC,SAAS,KAAK,CAAC,eAAe,CAAC,EAC/B,UAAU,0BAA0B,KAClC,MA6BF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,8BAA8B,GAC1C,SAAS,KAAK,CAAC,eAAe,CAAC,EAC/B,QAAQ,KAAK,CAAC,cAAc,CAAC,KAC3B,MA2BF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,0CAAgB,CAAC"}
|
package/dist/benchmark_format.js
CHANGED
|
@@ -5,46 +5,61 @@ import { format_number } from './maths.js';
|
|
|
5
5
|
* Format results as an ASCII table with percentiles, min/max, and relative performance.
|
|
6
6
|
* All times use the same unit for easy comparison.
|
|
7
7
|
* @param results - Array of benchmark results
|
|
8
|
+
* @param baseline - Optional task name to use as baseline for comparison (defaults to fastest)
|
|
8
9
|
* @returns Formatted table string with enhanced metrics
|
|
9
10
|
*
|
|
10
11
|
* @example
|
|
11
12
|
* ```ts
|
|
12
13
|
* console.log(benchmark_format_table(results));
|
|
13
|
-
* //
|
|
14
|
-
* // ā Task Name ā ops/sec ā
|
|
15
|
-
* //
|
|
16
|
-
* // ā slugify v2 ā 1,237,144 ā
|
|
17
|
-
* // ā slugify ā 261,619 ā
|
|
18
|
-
* //
|
|
14
|
+
* // āāāāāāāāāāāāāāā¬āāāāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā
|
|
15
|
+
* // ā Task Name ā ops/sec ā p50 (μs) ā p75 (μs) ā p90 (μs) ā p95 (μs) ā p99 (μs) ā min (μs) ā max (μs) ā vs Best ā
|
|
16
|
+
* // āāāāāāāāāāāāāāā¼āāāāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¤
|
|
17
|
+
* // ā slugify v2 ā 1,237,144 ā 0.81 ā 0.85 ā 0.89 ā 0.95 ā 1.20 ā 0.72 ā 2.45 ā baseline ā
|
|
18
|
+
* // ā slugify ā 261,619 ā 3.82 ā 3.95 ā 4.12 ā 4.35 ā 5.10 ā 3.21 ā 12.45 ā 4.73x ā
|
|
19
|
+
* // āāāāāāāāāāāāāāā“āāāāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā
|
|
19
20
|
* ```
|
|
20
21
|
*/
|
|
21
|
-
export const benchmark_format_table = (results) => {
|
|
22
|
+
export const benchmark_format_table = (results, baseline) => {
|
|
22
23
|
if (results.length === 0)
|
|
23
24
|
return '(no results)';
|
|
24
25
|
// Detect best unit for all results
|
|
25
26
|
const mean_times = results.map((r) => r.stats.mean_ns);
|
|
26
27
|
const unit = time_unit_detect_best(mean_times);
|
|
27
28
|
const unit_str = TIME_UNIT_DISPLAY[unit];
|
|
28
|
-
// Find
|
|
29
|
-
|
|
29
|
+
// Find baseline for relative comparison
|
|
30
|
+
let baseline_ops;
|
|
31
|
+
let vs_column_header;
|
|
32
|
+
if (baseline !== undefined) {
|
|
33
|
+
const baseline_result = results.find((r) => r.name === baseline);
|
|
34
|
+
if (!baseline_result) {
|
|
35
|
+
const available = results.map((r) => r.name).join(', ');
|
|
36
|
+
throw new Error(`Baseline task "${baseline}" not found in results. Available tasks: ${available}`);
|
|
37
|
+
}
|
|
38
|
+
baseline_ops = baseline_result.stats.ops_per_second;
|
|
39
|
+
vs_column_header = `vs ${baseline}`;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
baseline_ops = Math.max(...results.map((r) => r.stats.ops_per_second));
|
|
43
|
+
vs_column_header = 'vs Best';
|
|
44
|
+
}
|
|
30
45
|
const rows = [];
|
|
31
46
|
// Header with unit
|
|
32
47
|
rows.push([
|
|
33
48
|
'Task Name',
|
|
34
49
|
'ops/sec',
|
|
35
|
-
`
|
|
50
|
+
`p50 (${unit_str})`,
|
|
36
51
|
`p75 (${unit_str})`,
|
|
37
52
|
`p90 (${unit_str})`,
|
|
38
53
|
`p95 (${unit_str})`,
|
|
39
54
|
`p99 (${unit_str})`,
|
|
40
55
|
`min (${unit_str})`,
|
|
41
56
|
`max (${unit_str})`,
|
|
42
|
-
|
|
57
|
+
vs_column_header,
|
|
43
58
|
]);
|
|
44
59
|
// Data rows - all use same unit
|
|
45
60
|
results.forEach((r) => {
|
|
46
61
|
const ops_sec = benchmark_format_number(r.stats.ops_per_second, 2);
|
|
47
|
-
const
|
|
62
|
+
const p50 = time_format(r.stats.p50_ns, unit, 2).replace(unit_str, '').trim();
|
|
48
63
|
const p75 = time_format(r.stats.p75_ns, unit, 2).replace(unit_str, '').trim();
|
|
49
64
|
const p90 = time_format(r.stats.p90_ns, unit, 2).replace(unit_str, '').trim();
|
|
50
65
|
const p95 = time_format(r.stats.p95_ns, unit, 2).replace(unit_str, '').trim();
|
|
@@ -52,9 +67,9 @@ export const benchmark_format_table = (results) => {
|
|
|
52
67
|
const min = time_format(r.stats.min_ns, unit, 2).replace(unit_str, '').trim();
|
|
53
68
|
const max = time_format(r.stats.max_ns, unit, 2).replace(unit_str, '').trim();
|
|
54
69
|
// Calculate relative performance
|
|
55
|
-
const ratio =
|
|
56
|
-
const
|
|
57
|
-
rows.push([r.name, ops_sec,
|
|
70
|
+
const ratio = baseline_ops / r.stats.ops_per_second;
|
|
71
|
+
const vs_baseline = ratio === 1.0 ? 'baseline' : `${ratio.toFixed(2)}x`;
|
|
72
|
+
rows.push([r.name, ops_sec, p50, p75, p90, p95, p99, min, max, vs_baseline]);
|
|
58
73
|
});
|
|
59
74
|
// Calculate column widths (using display width for proper emoji handling)
|
|
60
75
|
const widths = rows[0].map((_, col_i) => {
|
|
@@ -91,44 +106,59 @@ export const benchmark_format_table = (results) => {
|
|
|
91
106
|
* Format results as a Markdown table with key metrics.
|
|
92
107
|
* All times use the same unit for easy comparison.
|
|
93
108
|
* @param results - Array of benchmark results
|
|
109
|
+
* @param baseline - Optional task name to use as baseline for comparison (defaults to fastest)
|
|
94
110
|
* @returns Formatted markdown table string
|
|
95
111
|
*
|
|
96
112
|
* @example
|
|
97
113
|
* ```ts
|
|
98
114
|
* console.log(benchmark_format_markdown(results));
|
|
99
|
-
* // | Task Name | ops/sec |
|
|
100
|
-
* //
|
|
101
|
-
* // | slugify v2 | 1,237,144 | 0.81
|
|
102
|
-
* // | slugify | 261,619 | 3.82
|
|
115
|
+
* // | Task Name | ops/sec | p50 (μs) | p75 (μs) | p90 (μs) | p95 (μs) | p99 (μs) | min (μs) | max (μs) | vs Best |
|
|
116
|
+
* // |------------|------------|----------|----------|----------|----------|----------|----------|----------|----------|
|
|
117
|
+
* // | slugify v2 | 1,237,144 | 0.81 | 0.85 | 0.89 | 0.95 | 1.20 | 0.72 | 2.45 | baseline |
|
|
118
|
+
* // | slugify | 261,619 | 3.82 | 3.95 | 4.12 | 4.35 | 5.10 | 3.21 | 12.45 | 4.73x |
|
|
103
119
|
* ```
|
|
104
120
|
*/
|
|
105
|
-
export const benchmark_format_markdown = (results) => {
|
|
121
|
+
export const benchmark_format_markdown = (results, baseline) => {
|
|
106
122
|
if (results.length === 0)
|
|
107
123
|
return '(no results)';
|
|
108
124
|
// Detect best unit for all results
|
|
109
125
|
const mean_times = results.map((r) => r.stats.mean_ns);
|
|
110
126
|
const unit = time_unit_detect_best(mean_times);
|
|
111
127
|
const unit_str = TIME_UNIT_DISPLAY[unit];
|
|
112
|
-
// Find
|
|
113
|
-
|
|
128
|
+
// Find baseline for relative comparison
|
|
129
|
+
let baseline_ops;
|
|
130
|
+
let vs_column_header;
|
|
131
|
+
if (baseline !== undefined) {
|
|
132
|
+
const baseline_result = results.find((r) => r.name === baseline);
|
|
133
|
+
if (!baseline_result) {
|
|
134
|
+
const available = results.map((r) => r.name).join(', ');
|
|
135
|
+
throw new Error(`Baseline task "${baseline}" not found in results. Available tasks: ${available}`);
|
|
136
|
+
}
|
|
137
|
+
baseline_ops = baseline_result.stats.ops_per_second;
|
|
138
|
+
vs_column_header = `vs ${baseline}`;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
baseline_ops = Math.max(...results.map((r) => r.stats.ops_per_second));
|
|
142
|
+
vs_column_header = 'vs Best';
|
|
143
|
+
}
|
|
114
144
|
const rows = [];
|
|
115
145
|
// Header with unit
|
|
116
146
|
rows.push([
|
|
117
147
|
'Task Name',
|
|
118
148
|
'ops/sec',
|
|
119
|
-
`
|
|
149
|
+
`p50 (${unit_str})`,
|
|
120
150
|
`p75 (${unit_str})`,
|
|
121
151
|
`p90 (${unit_str})`,
|
|
122
152
|
`p95 (${unit_str})`,
|
|
123
153
|
`p99 (${unit_str})`,
|
|
124
154
|
`min (${unit_str})`,
|
|
125
155
|
`max (${unit_str})`,
|
|
126
|
-
|
|
156
|
+
vs_column_header,
|
|
127
157
|
]);
|
|
128
158
|
// Data rows - all use same unit
|
|
129
159
|
results.forEach((r) => {
|
|
130
160
|
const ops_sec = benchmark_format_number(r.stats.ops_per_second, 2);
|
|
131
|
-
const
|
|
161
|
+
const p50 = time_format(r.stats.p50_ns, unit, 2).replace(unit_str, '').trim();
|
|
132
162
|
const p75 = time_format(r.stats.p75_ns, unit, 2).replace(unit_str, '').trim();
|
|
133
163
|
const p90 = time_format(r.stats.p90_ns, unit, 2).replace(unit_str, '').trim();
|
|
134
164
|
const p95 = time_format(r.stats.p95_ns, unit, 2).replace(unit_str, '').trim();
|
|
@@ -136,9 +166,9 @@ export const benchmark_format_markdown = (results) => {
|
|
|
136
166
|
const min = time_format(r.stats.min_ns, unit, 2).replace(unit_str, '').trim();
|
|
137
167
|
const max = time_format(r.stats.max_ns, unit, 2).replace(unit_str, '').trim();
|
|
138
168
|
// Calculate relative performance
|
|
139
|
-
const ratio =
|
|
140
|
-
const
|
|
141
|
-
rows.push([r.name, ops_sec,
|
|
169
|
+
const ratio = baseline_ops / r.stats.ops_per_second;
|
|
170
|
+
const vs_baseline = ratio === 1.0 ? 'baseline' : `${ratio.toFixed(2)}x`;
|
|
171
|
+
rows.push([r.name, ops_sec, p50, p75, p90, p95, p99, min, max, vs_baseline]);
|
|
142
172
|
});
|
|
143
173
|
// Calculate column widths
|
|
144
174
|
const widths = rows[0].map((_, col_i) => {
|
|
@@ -168,6 +198,54 @@ export const benchmark_format_markdown = (results) => {
|
|
|
168
198
|
}
|
|
169
199
|
return lines.join('\n');
|
|
170
200
|
};
|
|
201
|
+
/**
|
|
202
|
+
* Format results as grouped Markdown tables with headers between groups.
|
|
203
|
+
* @param results - Array of benchmark results
|
|
204
|
+
* @param groups - Array of group definitions
|
|
205
|
+
* @returns Formatted markdown string with group headers and tables
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```ts
|
|
209
|
+
* const groups = [
|
|
210
|
+
* { name: 'Fast Paths', filter: (r) => r.name.includes('fast'), baseline: 'fast/reference' },
|
|
211
|
+
* { name: 'Slow Paths', filter: (r) => r.name.includes('slow') },
|
|
212
|
+
* ];
|
|
213
|
+
* console.log(benchmark_format_markdown_grouped(results, groups));
|
|
214
|
+
* // ### Fast Paths
|
|
215
|
+
* // | Task Name | ops/sec | ... | vs fast/reference |
|
|
216
|
+
* // |-----------|---------|-----|-------------------|
|
|
217
|
+
* // | ... | ... | ... | ... |
|
|
218
|
+
* //
|
|
219
|
+
* // ### Slow Paths
|
|
220
|
+
* // | Task Name | ops/sec | ... | vs Best |
|
|
221
|
+
* // |-----------|---------|-----|---------|
|
|
222
|
+
* // | ... | ... | ... | ... |
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
export const benchmark_format_markdown_grouped = (results, groups) => {
|
|
226
|
+
if (results.length === 0)
|
|
227
|
+
return '(no results)';
|
|
228
|
+
const sections = [];
|
|
229
|
+
for (const group of groups) {
|
|
230
|
+
const group_results = results.filter(group.filter);
|
|
231
|
+
if (group_results.length === 0)
|
|
232
|
+
continue;
|
|
233
|
+
// Add group header and table
|
|
234
|
+
const header = group.description
|
|
235
|
+
? `### ${group.name}\n\n${group.description}\n`
|
|
236
|
+
: `### ${group.name}\n`;
|
|
237
|
+
sections.push(header);
|
|
238
|
+
sections.push(benchmark_format_markdown(group_results, group.baseline));
|
|
239
|
+
}
|
|
240
|
+
// Handle ungrouped results (those that don't match any group)
|
|
241
|
+
const grouped_names = new Set(groups.flatMap((g) => results.filter(g.filter).map((r) => r.name)));
|
|
242
|
+
const ungrouped = results.filter((r) => !grouped_names.has(r.name));
|
|
243
|
+
if (ungrouped.length > 0) {
|
|
244
|
+
sections.push('### Other\n');
|
|
245
|
+
sections.push(benchmark_format_markdown(ungrouped));
|
|
246
|
+
}
|
|
247
|
+
return sections.join('\n');
|
|
248
|
+
};
|
|
171
249
|
/**
|
|
172
250
|
* Format results as JSON.
|
|
173
251
|
* @param results - Array of benchmark results
|
|
@@ -191,7 +269,7 @@ export const benchmark_format_json = (results, options) => {
|
|
|
191
269
|
total_time_ms: r.total_time_ms,
|
|
192
270
|
ops_per_second: r.stats.ops_per_second,
|
|
193
271
|
mean_ns: r.stats.mean_ns,
|
|
194
|
-
|
|
272
|
+
p50_ns: r.stats.p50_ns,
|
|
195
273
|
std_dev_ns: r.stats.std_dev_ns,
|
|
196
274
|
min_ns: r.stats.min_ns,
|
|
197
275
|
max_ns: r.stats.max_ns,
|
|
@@ -248,7 +326,7 @@ export const benchmark_format_table_grouped = (results, groups) => {
|
|
|
248
326
|
? `\nš¦ ${group.name}\n ${group.description}`
|
|
249
327
|
: `\nš¦ ${group.name}`;
|
|
250
328
|
sections.push(header);
|
|
251
|
-
sections.push(benchmark_format_table(group_results));
|
|
329
|
+
sections.push(benchmark_format_table(group_results, group.baseline));
|
|
252
330
|
}
|
|
253
331
|
// Handle ungrouped results (those that don't match any group)
|
|
254
332
|
const grouped_names = new Set(groups.flatMap((g) => results.filter(g.filter).map((r) => r.name)));
|
|
@@ -53,8 +53,8 @@ export interface BenchmarkCompareOptions {
|
|
|
53
53
|
export declare class BenchmarkStats {
|
|
54
54
|
/** Mean (average) time in nanoseconds */
|
|
55
55
|
readonly mean_ns: number;
|
|
56
|
-
/**
|
|
57
|
-
readonly
|
|
56
|
+
/** 50th percentile (median) time in nanoseconds */
|
|
57
|
+
readonly p50_ns: number;
|
|
58
58
|
/** Standard deviation in nanoseconds */
|
|
59
59
|
readonly std_dev_ns: number;
|
|
60
60
|
/** Minimum time in nanoseconds */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"benchmark_stats.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/benchmark_stats.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAgBH;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,sBAAsB,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,mFAAmF;IACnF,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,OAAO,CAAC;IAC5B,kEAAkE;IAClE,aAAa,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,WAAW,EAAE,OAAO,CAAC;IACrB,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,gBAAgB,EAAE,eAAe,CAAC;IAClC,mDAAmD;IACnD,UAAU,EAAE,OAAO,CAAC;IACpB,sDAAsD;IACtD,cAAc,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACvC,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,qBAAa,cAAc;IAC1B,yCAAyC;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,
|
|
1
|
+
{"version":3,"file":"benchmark_stats.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/benchmark_stats.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAgBH;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,sBAAsB,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,mFAAmF;IACnF,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,OAAO,CAAC;IAC5B,kEAAkE;IAClE,aAAa,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,WAAW,EAAE,OAAO,CAAC;IACrB,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,gBAAgB,EAAE,eAAe,CAAC;IAClC,mDAAmD;IACnD,UAAU,EAAE,OAAO,CAAC;IACpB,sDAAsD;IACtD,cAAc,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACvC,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,qBAAa,cAAc;IAC1B,yCAAyC;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,kCAAkC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kCAAkC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,QAAQ,CAAC,sBAAsB,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClD,sDAAsD;IACtD,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACpC,yCAAyC;IACzC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,8CAA8C;IAC9C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,0DAA0D;IAC1D,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,mDAAmD;IACnD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,sEAAsE;IACtE,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;gBAEvB,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC;IAiErC;;OAEG;IACH,QAAQ,IAAI,MAAM;CAGlB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,uBAAuB,GACnC,GAAG,wBAAwB,EAC3B,GAAG,wBAAwB,EAC3B,UAAU,uBAAuB,KAC/B,mBA6GF,CAAC"}
|
package/dist/benchmark_stats.js
CHANGED
|
@@ -13,8 +13,8 @@ import { stats_mean, stats_median, stats_std_dev, stats_percentile, stats_cv, st
|
|
|
13
13
|
export class BenchmarkStats {
|
|
14
14
|
/** Mean (average) time in nanoseconds */
|
|
15
15
|
mean_ns;
|
|
16
|
-
/**
|
|
17
|
-
|
|
16
|
+
/** 50th percentile (median) time in nanoseconds */
|
|
17
|
+
p50_ns;
|
|
18
18
|
/** Standard deviation in nanoseconds */
|
|
19
19
|
std_dev_ns;
|
|
20
20
|
/** Minimum time in nanoseconds */
|
|
@@ -62,7 +62,7 @@ export class BenchmarkStats {
|
|
|
62
62
|
// If no valid timings, return empty stats
|
|
63
63
|
if (valid_timings.length === 0) {
|
|
64
64
|
this.mean_ns = NaN;
|
|
65
|
-
this.
|
|
65
|
+
this.p50_ns = NaN;
|
|
66
66
|
this.std_dev_ns = NaN;
|
|
67
67
|
this.min_ns = NaN;
|
|
68
68
|
this.max_ns = NaN;
|
|
@@ -86,7 +86,7 @@ export class BenchmarkStats {
|
|
|
86
86
|
this.sample_size = cleaned.length;
|
|
87
87
|
// Calculate statistics on cleaned data
|
|
88
88
|
this.mean_ns = stats_mean(cleaned);
|
|
89
|
-
this.
|
|
89
|
+
this.p50_ns = stats_median(sorted_cleaned);
|
|
90
90
|
this.std_dev_ns = stats_std_dev(cleaned, this.mean_ns);
|
|
91
91
|
const { min, max } = stats_min_max(sorted_cleaned);
|
|
92
92
|
this.min_ns = min;
|
|
@@ -170,5 +170,11 @@ export interface BenchmarkGroup {
|
|
|
170
170
|
description?: string;
|
|
171
171
|
/** Filter function to determine which results belong to this group */
|
|
172
172
|
filter: (result: BenchmarkResult) => boolean;
|
|
173
|
+
/**
|
|
174
|
+
* Task name to use as baseline for the "vs" column.
|
|
175
|
+
* When specified, ratios are computed against this task instead of the fastest.
|
|
176
|
+
* If the baseline task is not found in the group, falls back to "vs Best" with a warning.
|
|
177
|
+
*/
|
|
178
|
+
baseline?: string;
|
|
173
179
|
}
|
|
174
180
|
//# sourceMappingURL=benchmark_types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"benchmark_types.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/benchmark_types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAC,KAAK,EAAC,MAAM,WAAW,CAAC;AAErC;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;IAEd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAsCG;IACH,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IAEjF;;;;;;;;;;;;;;;;OAgBG;IACH,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnF;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IAEb,wEAAwE;IACxE,EAAE,EAAE,MAAM,OAAO,CAAC;IAElB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtC;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAC;IAEb,4CAA4C;IAC5C,KAAK,EAAE,cAAc,CAAC;IAEtB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IAEnB,uEAAuE;IACvE,aAAa,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC3C;;OAEG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IAEb,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,sEAAsE;IACtE,MAAM,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"benchmark_types.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/benchmark_types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAC,KAAK,EAAC,MAAM,WAAW,CAAC;AAErC;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;IAEd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAsCG;IACH,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IAEjF;;;;;;;;;;;;;;;;OAgBG;IACH,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnF;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IAEb,wEAAwE;IACxE,EAAE,EAAE,MAAM,OAAO,CAAC;IAElB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtC;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAC;IAEb,4CAA4C;IAC5C,KAAK,EAAE,cAAc,CAAC;IAEtB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IAEnB,uEAAuE;IACvE,aAAa,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC3C;;OAEG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IAEb,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,sEAAsE;IACtE,MAAM,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,OAAO,CAAC;IAE7C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB"}
|
package/dist/package_json.d.ts
CHANGED
|
@@ -33,7 +33,6 @@ export declare const PackageJson: z.ZodObject<{
|
|
|
33
33
|
name: z.ZodString;
|
|
34
34
|
version: z.ZodString;
|
|
35
35
|
private: z.ZodOptional<z.ZodBoolean>;
|
|
36
|
-
public: z.ZodOptional<z.ZodBoolean>;
|
|
37
36
|
description: z.ZodOptional<z.ZodString>;
|
|
38
37
|
motto: z.ZodOptional<z.ZodString>;
|
|
39
38
|
glyph: z.ZodOptional<z.ZodString>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package_json.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/package_json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAMtB,eAAO,MAAM,qBAAqB;;;;mBAOhC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE1E,eAAO,MAAM,iBAAiB;;;;mBAO5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,eAAO,MAAM,kBAAkB;;;mBAM7B,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAapE,eAAO,MAAM,WAAW,yEAAsB,CAAC;AAC/C,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,kJAI7B,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE;;GAEG;AACH,eAAO,MAAM,WAAW
|
|
1
|
+
{"version":3,"file":"package_json.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/package_json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAMtB,eAAO,MAAM,qBAAqB;;;;mBAOhC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE1E,eAAO,MAAM,iBAAiB;;;;mBAO5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,eAAO,MAAM,kBAAkB;;;mBAM7B,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAapE,eAAO,MAAM,WAAW,yEAAsB,CAAC;AAC/C,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,kJAI7B,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE;;GAEG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6DtB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC"}
|
package/dist/package_json.js
CHANGED
|
@@ -54,12 +54,6 @@ export const PackageJson = z.looseObject({
|
|
|
54
54
|
.boolean()
|
|
55
55
|
.meta({ description: 'disallow publishing to the configured registry' })
|
|
56
56
|
.optional(),
|
|
57
|
-
public: z
|
|
58
|
-
.boolean()
|
|
59
|
-
.meta({
|
|
60
|
-
description: 'a Gro extension that enables publishing `.well-known/package.json` and `.well-known/src`',
|
|
61
|
-
})
|
|
62
|
-
.optional(),
|
|
63
57
|
description: z.string().optional(),
|
|
64
58
|
motto: z
|
|
65
59
|
.string()
|
package/dist/process.d.ts
CHANGED
|
@@ -74,4 +74,8 @@ export interface RestartableProcess {
|
|
|
74
74
|
* handling many concurrent `restart` calls gracefully.
|
|
75
75
|
*/
|
|
76
76
|
export declare const spawn_restartable_process: (command: string, args?: ReadonlyArray<string>, options?: SpawnOptions) => RestartableProcess;
|
|
77
|
+
/**
|
|
78
|
+
* Check if a PID is still running.
|
|
79
|
+
*/
|
|
80
|
+
export declare const process_is_pid_running: (pid: number) => boolean;
|
|
77
81
|
//# sourceMappingURL=process.d.ts.map
|
package/dist/process.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/process.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,MAAM,oBAAoB,CAAC;AAK5B,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AAIxC,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,OAAO;IACvB,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IAC9B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAID,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAEnD;;;;GAIG;AACH,eAAO,MAAM,KAAK,GAAI,GAAG,MAAM,UAAU,CAAC,OAAO,aAAa,CAAC,KAAG,OAAO,CAAC,WAAW,CACvD,CAAC;AAE/B,MAAM,WAAW,UAAU;IAC1B,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,GACrB,SAAS,MAAM,EACf,OAAM,aAAa,CAAC,MAAM,CAAM,EAChC,UAAU,YAAY,KACpB,OAAO,CAAC,UAAU,CAYpB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GACzB,SAAS,MAAM,EACf,OAAM,aAAa,CAAC,MAAM,CAAM,EAChC,UAAU,YAAY,KACpB,cAUF,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,OAAO,YAAY,KAAG,MACwC,CAAC;AAEnG;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,GAAG,CAAC,YAAY,CAAa,CAAC;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,YAAY,KAAG,CAAC,MAAM,IAAI,CAWtE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,OAAO,GAAI,OAAO,YAAY,KAAG,OAAO,CAAC,WAAW,CAShE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,WAAW,QAAO,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CACQ,CAAC;AAElE;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,GACzC,iBAAiB,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,uBAAuB,KAAK,MAAM,GAAG,IAAI,EACtF,iBAAiB,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,uBAAuB,KAAK,MAAM,GAAG,IAAI,EACtF,eAAc,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,uBAAuB,KAAK,IACtD,KACd,IAYF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,WAAW,KAAG,MAKxD,CAAC;AAGF,MAAM,WAAW,kBAAkB;IAClC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;;GAGG;AACH,eAAO,MAAM,yBAAyB,GACrC,SAAS,MAAM,EACf,OAAM,aAAa,CAAC,MAAM,CAAM,EAChC,UAAU,YAAY,KACpB,kBAuBF,CAAC"}
|
|
1
|
+
{"version":3,"file":"process.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/process.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,MAAM,oBAAoB,CAAC;AAK5B,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AAIxC,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,OAAO;IACvB,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IAC9B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAID,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAEnD;;;;GAIG;AACH,eAAO,MAAM,KAAK,GAAI,GAAG,MAAM,UAAU,CAAC,OAAO,aAAa,CAAC,KAAG,OAAO,CAAC,WAAW,CACvD,CAAC;AAE/B,MAAM,WAAW,UAAU;IAC1B,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,GACrB,SAAS,MAAM,EACf,OAAM,aAAa,CAAC,MAAM,CAAM,EAChC,UAAU,YAAY,KACpB,OAAO,CAAC,UAAU,CAYpB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GACzB,SAAS,MAAM,EACf,OAAM,aAAa,CAAC,MAAM,CAAM,EAChC,UAAU,YAAY,KACpB,cAUF,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,OAAO,YAAY,KAAG,MACwC,CAAC;AAEnG;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,GAAG,CAAC,YAAY,CAAa,CAAC;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,YAAY,KAAG,CAAC,MAAM,IAAI,CAWtE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,OAAO,GAAI,OAAO,YAAY,KAAG,OAAO,CAAC,WAAW,CAShE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,WAAW,QAAO,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CACQ,CAAC;AAElE;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,GACzC,iBAAiB,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,uBAAuB,KAAK,MAAM,GAAG,IAAI,EACtF,iBAAiB,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,uBAAuB,KAAK,MAAM,GAAG,IAAI,EACtF,eAAc,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,uBAAuB,KAAK,IACtD,KACd,IAYF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,WAAW,KAAG,MAKxD,CAAC;AAGF,MAAM,WAAW,kBAAkB;IAClC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;;GAGG;AACH,eAAO,MAAM,yBAAyB,GACrC,SAAS,MAAM,EACf,OAAM,aAAa,CAAC,MAAM,CAAM,EAChC,UAAU,YAAY,KACpB,kBAuBF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,sBAAsB,GAAI,KAAK,MAAM,KAAG,OASpD,CAAC"}
|
package/dist/process.js
CHANGED
|
@@ -146,3 +146,17 @@ export const spawn_restartable_process = (command, args = [], options) => {
|
|
|
146
146
|
void restart();
|
|
147
147
|
return { restart, kill };
|
|
148
148
|
};
|
|
149
|
+
/**
|
|
150
|
+
* Check if a PID is still running.
|
|
151
|
+
*/
|
|
152
|
+
export const process_is_pid_running = (pid) => {
|
|
153
|
+
try {
|
|
154
|
+
// Sending signal 0 doesn't actually send a signal, just checks if process exists
|
|
155
|
+
process.kill(pid, 0);
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
// ESRCH = no such process, EPERM = exists but no permission
|
|
160
|
+
return err.code === 'EPERM';
|
|
161
|
+
}
|
|
162
|
+
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fuzdev/fuz_util",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
4
4
|
"description": "utility belt for JS",
|
|
5
5
|
"glyph": "š¦",
|
|
6
6
|
"logo": "logo.svg",
|
|
7
7
|
"logo_alt": "a green sauropod wearing a brown utility belt",
|
|
8
8
|
"motto": "ancient not extinct",
|
|
9
|
-
"public": true,
|
|
10
9
|
"license": "MIT",
|
|
11
10
|
"homepage": "https://util.fuz.dev/",
|
|
12
11
|
"author": {
|
package/src/lib/benchmark.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
benchmark_format_table,
|
|
27
27
|
benchmark_format_table_grouped,
|
|
28
28
|
benchmark_format_markdown,
|
|
29
|
+
benchmark_format_markdown_grouped,
|
|
29
30
|
benchmark_format_json,
|
|
30
31
|
benchmark_format_number,
|
|
31
32
|
type BenchmarkFormatJsonOptions,
|
|
@@ -407,10 +408,27 @@ export class Benchmark {
|
|
|
407
408
|
|
|
408
409
|
/**
|
|
409
410
|
* Format results as a Markdown table.
|
|
411
|
+
* @param options - Formatting options (groups for organized output with optional baselines)
|
|
410
412
|
* @returns Formatted markdown string
|
|
413
|
+
*
|
|
414
|
+
* @example
|
|
415
|
+
* ```ts
|
|
416
|
+
* // Standard table
|
|
417
|
+
* console.log(bench.markdown());
|
|
418
|
+
*
|
|
419
|
+
* // Grouped by category with custom baseline
|
|
420
|
+
* console.log(bench.markdown({
|
|
421
|
+
* groups: [
|
|
422
|
+
* { name: 'Format', filter: (r) => r.name.startsWith('format/'), baseline: 'format/prettier' },
|
|
423
|
+
* { name: 'Parse', filter: (r) => r.name.startsWith('parse/') },
|
|
424
|
+
* ]
|
|
425
|
+
* }));
|
|
426
|
+
* ```
|
|
411
427
|
*/
|
|
412
|
-
markdown(): string {
|
|
413
|
-
return
|
|
428
|
+
markdown(options?: BenchmarkFormatTableOptions): string {
|
|
429
|
+
return options?.groups
|
|
430
|
+
? benchmark_format_markdown_grouped(this.#results, options.groups)
|
|
431
|
+
: benchmark_format_markdown(this.#results);
|
|
414
432
|
}
|
|
415
433
|
|
|
416
434
|
/**
|
|
@@ -431,6 +449,39 @@ export class Benchmark {
|
|
|
431
449
|
return [...this.#results];
|
|
432
450
|
}
|
|
433
451
|
|
|
452
|
+
/**
|
|
453
|
+
* Check if the benchmark has been run and has results.
|
|
454
|
+
* @returns True if results are available
|
|
455
|
+
*
|
|
456
|
+
* @example
|
|
457
|
+
* ```ts
|
|
458
|
+
* if (bench.has_results) {
|
|
459
|
+
* console.log(bench.table());
|
|
460
|
+
* }
|
|
461
|
+
* ```
|
|
462
|
+
*/
|
|
463
|
+
get has_results(): boolean {
|
|
464
|
+
return this.#results.length > 0;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Get results as a map for convenient lookup by task name.
|
|
469
|
+
* Returns a new Map each call to prevent external mutation.
|
|
470
|
+
* @returns Map of task name to benchmark result
|
|
471
|
+
*
|
|
472
|
+
* @example
|
|
473
|
+
* ```ts
|
|
474
|
+
* const results_map = bench.results_by_name();
|
|
475
|
+
* const slugify_result = results_map.get('slugify');
|
|
476
|
+
* if (slugify_result) {
|
|
477
|
+
* console.log(`slugify: ${slugify_result.stats.ops_per_second} ops/sec`);
|
|
478
|
+
* }
|
|
479
|
+
* ```
|
|
480
|
+
*/
|
|
481
|
+
results_by_name(): Map<string, BenchmarkResult> {
|
|
482
|
+
return new Map(this.#results.map((r) => [r.name, r]));
|
|
483
|
+
}
|
|
484
|
+
|
|
434
485
|
/**
|
|
435
486
|
* Reset the benchmark results.
|
|
436
487
|
* Keeps tasks intact so benchmarks can be rerun.
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
import {stats_confidence_interval_from_summary} from './stats.js';
|
|
19
19
|
|
|
20
20
|
// Version for forward compatibility - increment when schema changes
|
|
21
|
-
const BASELINE_VERSION =
|
|
21
|
+
const BASELINE_VERSION = 2;
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Schema for a single benchmark entry in the baseline.
|
|
@@ -26,7 +26,7 @@ const BASELINE_VERSION = 1;
|
|
|
26
26
|
export const BenchmarkBaselineEntry = z.object({
|
|
27
27
|
name: z.string(),
|
|
28
28
|
mean_ns: z.number(),
|
|
29
|
-
|
|
29
|
+
p50_ns: z.number(),
|
|
30
30
|
std_dev_ns: z.number(),
|
|
31
31
|
min_ns: z.number(),
|
|
32
32
|
max_ns: z.number(),
|
|
@@ -137,7 +137,7 @@ const results_to_entries = (results: Array<BenchmarkResult>): Array<BenchmarkBas
|
|
|
137
137
|
return results.map((r) => ({
|
|
138
138
|
name: r.name,
|
|
139
139
|
mean_ns: r.stats.mean_ns,
|
|
140
|
-
|
|
140
|
+
p50_ns: r.stats.p50_ns,
|
|
141
141
|
std_dev_ns: r.stats.std_dev_ns,
|
|
142
142
|
min_ns: r.stats.min_ns,
|
|
143
143
|
max_ns: r.stats.max_ns,
|
|
@@ -7,20 +7,24 @@ import {format_number} from './maths.js';
|
|
|
7
7
|
* Format results as an ASCII table with percentiles, min/max, and relative performance.
|
|
8
8
|
* All times use the same unit for easy comparison.
|
|
9
9
|
* @param results - Array of benchmark results
|
|
10
|
+
* @param baseline - Optional task name to use as baseline for comparison (defaults to fastest)
|
|
10
11
|
* @returns Formatted table string with enhanced metrics
|
|
11
12
|
*
|
|
12
13
|
* @example
|
|
13
14
|
* ```ts
|
|
14
15
|
* console.log(benchmark_format_table(results));
|
|
15
|
-
* //
|
|
16
|
-
* // ā Task Name ā ops/sec ā
|
|
17
|
-
* //
|
|
18
|
-
* // ā slugify v2 ā 1,237,144 ā
|
|
19
|
-
* // ā slugify ā 261,619 ā
|
|
20
|
-
* //
|
|
16
|
+
* // āāāāāāāāāāāāāāā¬āāāāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāā
|
|
17
|
+
* // ā Task Name ā ops/sec ā p50 (μs) ā p75 (μs) ā p90 (μs) ā p95 (μs) ā p99 (μs) ā min (μs) ā max (μs) ā vs Best ā
|
|
18
|
+
* // āāāāāāāāāāāāāāā¼āāāāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāā¤
|
|
19
|
+
* // ā slugify v2 ā 1,237,144 ā 0.81 ā 0.85 ā 0.89 ā 0.95 ā 1.20 ā 0.72 ā 2.45 ā baseline ā
|
|
20
|
+
* // ā slugify ā 261,619 ā 3.82 ā 3.95 ā 4.12 ā 4.35 ā 5.10 ā 3.21 ā 12.45 ā 4.73x ā
|
|
21
|
+
* // āāāāāāāāāāāāāāā“āāāāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāā
|
|
21
22
|
* ```
|
|
22
23
|
*/
|
|
23
|
-
export const benchmark_format_table = (
|
|
24
|
+
export const benchmark_format_table = (
|
|
25
|
+
results: Array<BenchmarkResult>,
|
|
26
|
+
baseline?: string,
|
|
27
|
+
): string => {
|
|
24
28
|
if (results.length === 0) return '(no results)';
|
|
25
29
|
|
|
26
30
|
// Detect best unit for all results
|
|
@@ -28,8 +32,24 @@ export const benchmark_format_table = (results: Array<BenchmarkResult>): string
|
|
|
28
32
|
const unit = time_unit_detect_best(mean_times);
|
|
29
33
|
const unit_str = TIME_UNIT_DISPLAY[unit];
|
|
30
34
|
|
|
31
|
-
// Find
|
|
32
|
-
|
|
35
|
+
// Find baseline for relative comparison
|
|
36
|
+
let baseline_ops: number;
|
|
37
|
+
let vs_column_header: string;
|
|
38
|
+
|
|
39
|
+
if (baseline !== undefined) {
|
|
40
|
+
const baseline_result = results.find((r) => r.name === baseline);
|
|
41
|
+
if (!baseline_result) {
|
|
42
|
+
const available = results.map((r) => r.name).join(', ');
|
|
43
|
+
throw new Error(
|
|
44
|
+
`Baseline task "${baseline}" not found in results. Available tasks: ${available}`,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
baseline_ops = baseline_result.stats.ops_per_second;
|
|
48
|
+
vs_column_header = `vs ${baseline}`;
|
|
49
|
+
} else {
|
|
50
|
+
baseline_ops = Math.max(...results.map((r) => r.stats.ops_per_second));
|
|
51
|
+
vs_column_header = 'vs Best';
|
|
52
|
+
}
|
|
33
53
|
|
|
34
54
|
const rows: Array<Array<string>> = [];
|
|
35
55
|
|
|
@@ -37,20 +57,20 @@ export const benchmark_format_table = (results: Array<BenchmarkResult>): string
|
|
|
37
57
|
rows.push([
|
|
38
58
|
'Task Name',
|
|
39
59
|
'ops/sec',
|
|
40
|
-
`
|
|
60
|
+
`p50 (${unit_str})`,
|
|
41
61
|
`p75 (${unit_str})`,
|
|
42
62
|
`p90 (${unit_str})`,
|
|
43
63
|
`p95 (${unit_str})`,
|
|
44
64
|
`p99 (${unit_str})`,
|
|
45
65
|
`min (${unit_str})`,
|
|
46
66
|
`max (${unit_str})`,
|
|
47
|
-
|
|
67
|
+
vs_column_header,
|
|
48
68
|
]);
|
|
49
69
|
|
|
50
70
|
// Data rows - all use same unit
|
|
51
71
|
results.forEach((r) => {
|
|
52
72
|
const ops_sec = benchmark_format_number(r.stats.ops_per_second, 2);
|
|
53
|
-
const
|
|
73
|
+
const p50 = time_format(r.stats.p50_ns, unit, 2).replace(unit_str, '').trim();
|
|
54
74
|
const p75 = time_format(r.stats.p75_ns, unit, 2).replace(unit_str, '').trim();
|
|
55
75
|
const p90 = time_format(r.stats.p90_ns, unit, 2).replace(unit_str, '').trim();
|
|
56
76
|
const p95 = time_format(r.stats.p95_ns, unit, 2).replace(unit_str, '').trim();
|
|
@@ -59,10 +79,10 @@ export const benchmark_format_table = (results: Array<BenchmarkResult>): string
|
|
|
59
79
|
const max = time_format(r.stats.max_ns, unit, 2).replace(unit_str, '').trim();
|
|
60
80
|
|
|
61
81
|
// Calculate relative performance
|
|
62
|
-
const ratio =
|
|
63
|
-
const
|
|
82
|
+
const ratio = baseline_ops / r.stats.ops_per_second;
|
|
83
|
+
const vs_baseline = ratio === 1.0 ? 'baseline' : `${ratio.toFixed(2)}x`;
|
|
64
84
|
|
|
65
|
-
rows.push([r.name, ops_sec,
|
|
85
|
+
rows.push([r.name, ops_sec, p50, p75, p90, p95, p99, min, max, vs_baseline]);
|
|
66
86
|
});
|
|
67
87
|
|
|
68
88
|
// Calculate column widths (using display width for proper emoji handling)
|
|
@@ -107,18 +127,22 @@ export const benchmark_format_table = (results: Array<BenchmarkResult>): string
|
|
|
107
127
|
* Format results as a Markdown table with key metrics.
|
|
108
128
|
* All times use the same unit for easy comparison.
|
|
109
129
|
* @param results - Array of benchmark results
|
|
130
|
+
* @param baseline - Optional task name to use as baseline for comparison (defaults to fastest)
|
|
110
131
|
* @returns Formatted markdown table string
|
|
111
132
|
*
|
|
112
133
|
* @example
|
|
113
134
|
* ```ts
|
|
114
135
|
* console.log(benchmark_format_markdown(results));
|
|
115
|
-
* // | Task Name | ops/sec |
|
|
116
|
-
* //
|
|
117
|
-
* // | slugify v2 | 1,237,144 | 0.81
|
|
118
|
-
* // | slugify | 261,619 | 3.82
|
|
136
|
+
* // | Task Name | ops/sec | p50 (μs) | p75 (μs) | p90 (μs) | p95 (μs) | p99 (μs) | min (μs) | max (μs) | vs Best |
|
|
137
|
+
* // |------------|------------|----------|----------|----------|----------|----------|----------|----------|----------|
|
|
138
|
+
* // | slugify v2 | 1,237,144 | 0.81 | 0.85 | 0.89 | 0.95 | 1.20 | 0.72 | 2.45 | baseline |
|
|
139
|
+
* // | slugify | 261,619 | 3.82 | 3.95 | 4.12 | 4.35 | 5.10 | 3.21 | 12.45 | 4.73x |
|
|
119
140
|
* ```
|
|
120
141
|
*/
|
|
121
|
-
export const benchmark_format_markdown = (
|
|
142
|
+
export const benchmark_format_markdown = (
|
|
143
|
+
results: Array<BenchmarkResult>,
|
|
144
|
+
baseline?: string,
|
|
145
|
+
): string => {
|
|
122
146
|
if (results.length === 0) return '(no results)';
|
|
123
147
|
|
|
124
148
|
// Detect best unit for all results
|
|
@@ -126,8 +150,24 @@ export const benchmark_format_markdown = (results: Array<BenchmarkResult>): stri
|
|
|
126
150
|
const unit = time_unit_detect_best(mean_times);
|
|
127
151
|
const unit_str = TIME_UNIT_DISPLAY[unit];
|
|
128
152
|
|
|
129
|
-
// Find
|
|
130
|
-
|
|
153
|
+
// Find baseline for relative comparison
|
|
154
|
+
let baseline_ops: number;
|
|
155
|
+
let vs_column_header: string;
|
|
156
|
+
|
|
157
|
+
if (baseline !== undefined) {
|
|
158
|
+
const baseline_result = results.find((r) => r.name === baseline);
|
|
159
|
+
if (!baseline_result) {
|
|
160
|
+
const available = results.map((r) => r.name).join(', ');
|
|
161
|
+
throw new Error(
|
|
162
|
+
`Baseline task "${baseline}" not found in results. Available tasks: ${available}`,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
baseline_ops = baseline_result.stats.ops_per_second;
|
|
166
|
+
vs_column_header = `vs ${baseline}`;
|
|
167
|
+
} else {
|
|
168
|
+
baseline_ops = Math.max(...results.map((r) => r.stats.ops_per_second));
|
|
169
|
+
vs_column_header = 'vs Best';
|
|
170
|
+
}
|
|
131
171
|
|
|
132
172
|
const rows: Array<Array<string>> = [];
|
|
133
173
|
|
|
@@ -135,20 +175,20 @@ export const benchmark_format_markdown = (results: Array<BenchmarkResult>): stri
|
|
|
135
175
|
rows.push([
|
|
136
176
|
'Task Name',
|
|
137
177
|
'ops/sec',
|
|
138
|
-
`
|
|
178
|
+
`p50 (${unit_str})`,
|
|
139
179
|
`p75 (${unit_str})`,
|
|
140
180
|
`p90 (${unit_str})`,
|
|
141
181
|
`p95 (${unit_str})`,
|
|
142
182
|
`p99 (${unit_str})`,
|
|
143
183
|
`min (${unit_str})`,
|
|
144
184
|
`max (${unit_str})`,
|
|
145
|
-
|
|
185
|
+
vs_column_header,
|
|
146
186
|
]);
|
|
147
187
|
|
|
148
188
|
// Data rows - all use same unit
|
|
149
189
|
results.forEach((r) => {
|
|
150
190
|
const ops_sec = benchmark_format_number(r.stats.ops_per_second, 2);
|
|
151
|
-
const
|
|
191
|
+
const p50 = time_format(r.stats.p50_ns, unit, 2).replace(unit_str, '').trim();
|
|
152
192
|
const p75 = time_format(r.stats.p75_ns, unit, 2).replace(unit_str, '').trim();
|
|
153
193
|
const p90 = time_format(r.stats.p90_ns, unit, 2).replace(unit_str, '').trim();
|
|
154
194
|
const p95 = time_format(r.stats.p95_ns, unit, 2).replace(unit_str, '').trim();
|
|
@@ -157,10 +197,10 @@ export const benchmark_format_markdown = (results: Array<BenchmarkResult>): stri
|
|
|
157
197
|
const max = time_format(r.stats.max_ns, unit, 2).replace(unit_str, '').trim();
|
|
158
198
|
|
|
159
199
|
// Calculate relative performance
|
|
160
|
-
const ratio =
|
|
161
|
-
const
|
|
200
|
+
const ratio = baseline_ops / r.stats.ops_per_second;
|
|
201
|
+
const vs_baseline = ratio === 1.0 ? 'baseline' : `${ratio.toFixed(2)}x`;
|
|
162
202
|
|
|
163
|
-
rows.push([r.name, ops_sec,
|
|
203
|
+
rows.push([r.name, ops_sec, p50, p75, p90, p95, p99, min, max, vs_baseline]);
|
|
164
204
|
});
|
|
165
205
|
|
|
166
206
|
// Calculate column widths
|
|
@@ -196,6 +236,62 @@ export const benchmark_format_markdown = (results: Array<BenchmarkResult>): stri
|
|
|
196
236
|
return lines.join('\n');
|
|
197
237
|
};
|
|
198
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Format results as grouped Markdown tables with headers between groups.
|
|
241
|
+
* @param results - Array of benchmark results
|
|
242
|
+
* @param groups - Array of group definitions
|
|
243
|
+
* @returns Formatted markdown string with group headers and tables
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```ts
|
|
247
|
+
* const groups = [
|
|
248
|
+
* { name: 'Fast Paths', filter: (r) => r.name.includes('fast'), baseline: 'fast/reference' },
|
|
249
|
+
* { name: 'Slow Paths', filter: (r) => r.name.includes('slow') },
|
|
250
|
+
* ];
|
|
251
|
+
* console.log(benchmark_format_markdown_grouped(results, groups));
|
|
252
|
+
* // ### Fast Paths
|
|
253
|
+
* // | Task Name | ops/sec | ... | vs fast/reference |
|
|
254
|
+
* // |-----------|---------|-----|-------------------|
|
|
255
|
+
* // | ... | ... | ... | ... |
|
|
256
|
+
* //
|
|
257
|
+
* // ### Slow Paths
|
|
258
|
+
* // | Task Name | ops/sec | ... | vs Best |
|
|
259
|
+
* // |-----------|---------|-----|---------|
|
|
260
|
+
* // | ... | ... | ... | ... |
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
export const benchmark_format_markdown_grouped = (
|
|
264
|
+
results: Array<BenchmarkResult>,
|
|
265
|
+
groups: Array<BenchmarkGroup>,
|
|
266
|
+
): string => {
|
|
267
|
+
if (results.length === 0) return '(no results)';
|
|
268
|
+
|
|
269
|
+
const sections: Array<string> = [];
|
|
270
|
+
|
|
271
|
+
for (const group of groups) {
|
|
272
|
+
const group_results = results.filter(group.filter);
|
|
273
|
+
if (group_results.length === 0) continue;
|
|
274
|
+
|
|
275
|
+
// Add group header and table
|
|
276
|
+
const header = group.description
|
|
277
|
+
? `### ${group.name}\n\n${group.description}\n`
|
|
278
|
+
: `### ${group.name}\n`;
|
|
279
|
+
sections.push(header);
|
|
280
|
+
sections.push(benchmark_format_markdown(group_results, group.baseline));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Handle ungrouped results (those that don't match any group)
|
|
284
|
+
const grouped_names = new Set(groups.flatMap((g) => results.filter(g.filter).map((r) => r.name)));
|
|
285
|
+
const ungrouped = results.filter((r) => !grouped_names.has(r.name));
|
|
286
|
+
|
|
287
|
+
if (ungrouped.length > 0) {
|
|
288
|
+
sections.push('### Other\n');
|
|
289
|
+
sections.push(benchmark_format_markdown(ungrouped));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return sections.join('\n');
|
|
293
|
+
};
|
|
294
|
+
|
|
199
295
|
export interface BenchmarkFormatJsonOptions {
|
|
200
296
|
/** Whether to pretty-print (default: true) */
|
|
201
297
|
pretty?: boolean;
|
|
@@ -229,7 +325,7 @@ export const benchmark_format_json = (
|
|
|
229
325
|
total_time_ms: r.total_time_ms,
|
|
230
326
|
ops_per_second: r.stats.ops_per_second,
|
|
231
327
|
mean_ns: r.stats.mean_ns,
|
|
232
|
-
|
|
328
|
+
p50_ns: r.stats.p50_ns,
|
|
233
329
|
std_dev_ns: r.stats.std_dev_ns,
|
|
234
330
|
min_ns: r.stats.min_ns,
|
|
235
331
|
max_ns: r.stats.max_ns,
|
|
@@ -292,7 +388,7 @@ export const benchmark_format_table_grouped = (
|
|
|
292
388
|
? `\nš¦ ${group.name}\n ${group.description}`
|
|
293
389
|
: `\nš¦ ${group.name}`;
|
|
294
390
|
sections.push(header);
|
|
295
|
-
sections.push(benchmark_format_table(group_results));
|
|
391
|
+
sections.push(benchmark_format_table(group_results, group.baseline));
|
|
296
392
|
}
|
|
297
393
|
|
|
298
394
|
// Handle ungrouped results (those that don't match any group)
|
|
@@ -72,8 +72,8 @@ export interface BenchmarkCompareOptions {
|
|
|
72
72
|
export class BenchmarkStats {
|
|
73
73
|
/** Mean (average) time in nanoseconds */
|
|
74
74
|
readonly mean_ns: number;
|
|
75
|
-
/**
|
|
76
|
-
readonly
|
|
75
|
+
/** 50th percentile (median) time in nanoseconds */
|
|
76
|
+
readonly p50_ns: number;
|
|
77
77
|
/** Standard deviation in nanoseconds */
|
|
78
78
|
readonly std_dev_ns: number;
|
|
79
79
|
/** Minimum time in nanoseconds */
|
|
@@ -124,7 +124,7 @@ export class BenchmarkStats {
|
|
|
124
124
|
// If no valid timings, return empty stats
|
|
125
125
|
if (valid_timings.length === 0) {
|
|
126
126
|
this.mean_ns = NaN;
|
|
127
|
-
this.
|
|
127
|
+
this.p50_ns = NaN;
|
|
128
128
|
this.std_dev_ns = NaN;
|
|
129
129
|
this.min_ns = NaN;
|
|
130
130
|
this.max_ns = NaN;
|
|
@@ -151,7 +151,7 @@ export class BenchmarkStats {
|
|
|
151
151
|
|
|
152
152
|
// Calculate statistics on cleaned data
|
|
153
153
|
this.mean_ns = stats_mean(cleaned);
|
|
154
|
-
this.
|
|
154
|
+
this.p50_ns = stats_median(sorted_cleaned);
|
|
155
155
|
this.std_dev_ns = stats_std_dev(cleaned, this.mean_ns);
|
|
156
156
|
|
|
157
157
|
const {min, max} = stats_min_max(sorted_cleaned);
|
|
@@ -194,4 +194,11 @@ export interface BenchmarkGroup {
|
|
|
194
194
|
|
|
195
195
|
/** Filter function to determine which results belong to this group */
|
|
196
196
|
filter: (result: BenchmarkResult) => boolean;
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Task name to use as baseline for the "vs" column.
|
|
200
|
+
* When specified, ratios are computed against this task instead of the fastest.
|
|
201
|
+
* If the baseline task is not found in the group, falls back to "vs Best" with a warning.
|
|
202
|
+
*/
|
|
203
|
+
baseline?: string;
|
|
197
204
|
}
|
package/src/lib/package_json.ts
CHANGED
|
@@ -71,13 +71,6 @@ export const PackageJson = z.looseObject({
|
|
|
71
71
|
.boolean()
|
|
72
72
|
.meta({description: 'disallow publishing to the configured registry'})
|
|
73
73
|
.optional(),
|
|
74
|
-
public: z
|
|
75
|
-
.boolean()
|
|
76
|
-
.meta({
|
|
77
|
-
description:
|
|
78
|
-
'a Gro extension that enables publishing `.well-known/package.json` and `.well-known/src`',
|
|
79
|
-
})
|
|
80
|
-
.optional(),
|
|
81
74
|
description: z.string().optional(),
|
|
82
75
|
motto: z
|
|
83
76
|
.string()
|
package/src/lib/process.ts
CHANGED
|
@@ -205,3 +205,17 @@ export const spawn_restartable_process = (
|
|
|
205
205
|
void restart();
|
|
206
206
|
return {restart, kill};
|
|
207
207
|
};
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Check if a PID is still running.
|
|
211
|
+
*/
|
|
212
|
+
export const process_is_pid_running = (pid: number): boolean => {
|
|
213
|
+
try {
|
|
214
|
+
// Sending signal 0 doesn't actually send a signal, just checks if process exists
|
|
215
|
+
process.kill(pid, 0);
|
|
216
|
+
return true;
|
|
217
|
+
} catch (err: any) {
|
|
218
|
+
// ESRCH = no such process, EPERM = exists but no permission
|
|
219
|
+
return err.code === 'EPERM';
|
|
220
|
+
}
|
|
221
|
+
};
|