ostia 0.1.6 → 0.2.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 +460 -96
- package/chunk-qbt85kmg.js +4 -0
- package/chunk-r5b8vf8w.js +21 -0
- package/cli.js +147 -62
- package/index.d.ts +301 -31
- package/index.js +1 -1
- package/package.json +1 -1
- package/runner.ts +5 -4
- package/chunk-nfgy545q.js +0 -4
- package/chunk-tw064qem.js +0 -21
package/index.d.ts
CHANGED
|
@@ -1,16 +1,37 @@
|
|
|
1
|
-
|
|
1
|
+
export declare function defineConfig(config: Partial<OstiaConfig>): Partial<OstiaConfig>;
|
|
2
|
+
|
|
3
|
+
interface TimeOptions {
|
|
2
4
|
commands: (string | string[])[];
|
|
3
|
-
|
|
5
|
+
/** Exact trial count. When set, `budgetMs` is ignored. */
|
|
6
|
+
samples?: number;
|
|
7
|
+
/** Wall-clock time budget for the sampling loop, ms (default: a
|
|
8
|
+
* hyperfine-style ~3s min-total-time loop when neither `samples` nor
|
|
9
|
+
* `budgetMs` is given). */
|
|
10
|
+
budgetMs?: number;
|
|
11
|
+
/** Hard floor on trials when no exact `samples` count is given. */
|
|
12
|
+
minSamples?: number;
|
|
4
13
|
warmup?: number;
|
|
14
|
+
/** Round-robin trials across commands (one trial per command, repeated)
|
|
15
|
+
* instead of running each command's whole trial loop to completion before
|
|
16
|
+
* the next command starts. Default: true when 2+ commands are given (a
|
|
17
|
+
* single command has nothing to interleave against). Spreads any drift
|
|
18
|
+
* over the run's wall-clock span (thermal throttling, a noisy neighbor
|
|
19
|
+
* process) evenly across every command instead of favoring whichever ran
|
|
20
|
+
* first or last. */
|
|
21
|
+
interleave?: boolean;
|
|
5
22
|
cwd?: string;
|
|
6
23
|
env?: Record<string, string>;
|
|
7
24
|
cpu?: boolean;
|
|
8
25
|
heap?: boolean;
|
|
9
26
|
cpuIntervalUs?: number;
|
|
10
27
|
outDir?: string;
|
|
28
|
+
/** Measure this machine's noise floor before the first command (default:
|
|
29
|
+
* true) and stamp it on the document as `environment`. Set false to skip
|
|
30
|
+
* the ~200ms reference measurement. */
|
|
31
|
+
noiseCheck?: boolean;
|
|
11
32
|
}
|
|
12
33
|
|
|
13
|
-
export declare function
|
|
34
|
+
export declare function time(opts: TimeOptions): Promise<ProfileDocument>;
|
|
14
35
|
|
|
15
36
|
interface ProfileOptions {
|
|
16
37
|
intervalUs?: number;
|
|
@@ -19,13 +40,67 @@ interface ProfileOptions {
|
|
|
19
40
|
|
|
20
41
|
interface ProfileResult<T> {
|
|
21
42
|
result: T;
|
|
22
|
-
|
|
43
|
+
measurement: Measurement;
|
|
44
|
+
document: ProfileDocument;
|
|
23
45
|
}
|
|
24
46
|
|
|
25
47
|
export declare function profile<T>(fn: () => T | Promise<T>, opts?: ProfileOptions): Promise<ProfileResult<T>>;
|
|
26
48
|
|
|
49
|
+
interface WorkloadConfig {
|
|
50
|
+
label?: string;
|
|
51
|
+
command?: string[];
|
|
52
|
+
suites?: string[];
|
|
53
|
+
inputs?: string[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface BenchConfig {
|
|
57
|
+
/** Suite file globs, resolved with Bun.Glob against the config's directory
|
|
58
|
+
* (e.g. "bench/**\/*.bench.ts"). Ignored when suite files are also given
|
|
59
|
+
* on the command line - CLI args replace this list rather than merging
|
|
60
|
+
* with it. */
|
|
61
|
+
suites?: string[];
|
|
62
|
+
preload?: string[];
|
|
63
|
+
jobs?: number | "auto";
|
|
64
|
+
budgetMs?: number;
|
|
65
|
+
samples?: number;
|
|
66
|
+
minSamples?: number;
|
|
67
|
+
gc?: boolean;
|
|
68
|
+
cpu?: boolean;
|
|
69
|
+
alloc?: boolean;
|
|
70
|
+
filter?: string;
|
|
71
|
+
isolate?: boolean;
|
|
72
|
+
outDir?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface OstiaConfig {
|
|
76
|
+
runs: number | null;
|
|
77
|
+
warmup: number;
|
|
78
|
+
outDir: string;
|
|
79
|
+
baselineDir: string;
|
|
80
|
+
baseline: string;
|
|
81
|
+
cpuIntervalUs: number;
|
|
82
|
+
thresholds: Thresholds;
|
|
83
|
+
workloads: WorkloadConfig[];
|
|
84
|
+
bench?: BenchConfig;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface Thresholds {
|
|
88
|
+
timingPct: number;
|
|
89
|
+
frameSelfPct: number;
|
|
90
|
+
heapTypePct: number;
|
|
91
|
+
minFrameSelfUs: number;
|
|
92
|
+
/** Significance level for the Mann-Whitney p-value: a `regressed` /
|
|
93
|
+
* `improved` verdict also requires `pValue < alpha`. */
|
|
94
|
+
alpha: number;
|
|
95
|
+
/** Bootstrap resample rounds for the timing CI. Capped work regardless:
|
|
96
|
+
* see `bootstrapMedianDiffCi`. */
|
|
97
|
+
bootstrapIterations: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export declare function compareDocuments(base: ProfileDocument, cand: ProfileDocument, thresholds?: Thresholds): Comparison[];
|
|
101
|
+
|
|
27
102
|
export interface ProfileDocument {
|
|
28
|
-
schemaVersion:
|
|
103
|
+
schemaVersion: 2;
|
|
29
104
|
toolVersion: string;
|
|
30
105
|
bunVersion: string;
|
|
31
106
|
platform: {
|
|
@@ -34,8 +109,41 @@ export interface ProfileDocument {
|
|
|
34
109
|
};
|
|
35
110
|
createdAt: string;
|
|
36
111
|
workloads: Workload[];
|
|
37
|
-
|
|
112
|
+
measurements: Measurement[];
|
|
38
113
|
comparisons?: Comparison[];
|
|
114
|
+
/** Machine conditions when this document was measured. Additive, no
|
|
115
|
+
* schema bump. Absent when `noiseCheck: false` (or `--no-noise-check`)
|
|
116
|
+
* skipped the reference measurement. */
|
|
117
|
+
environment?: Environment;
|
|
118
|
+
/** Repo state when this document was measured, from `git rev-parse` /
|
|
119
|
+
* `git status --porcelain` in the process's cwd. Additive, no schema
|
|
120
|
+
* bump. Absent outside a git repo (or when `git` itself isn't
|
|
121
|
+
* available). Metadata only: never part of any fingerprint or id, so a
|
|
122
|
+
* commit or a dirty working tree never orphans a cached run or baseline. */
|
|
123
|
+
git?: GitMetadata;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
interface GitMetadata {
|
|
127
|
+
sha: string;
|
|
128
|
+
branch: string;
|
|
129
|
+
dirty: boolean;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
interface NoiseFloor {
|
|
133
|
+
/** `mad / median` of the reference workload's trial times, as a percent -
|
|
134
|
+
* how noisy this machine is right now, independent of what's being
|
|
135
|
+
* measured. */
|
|
136
|
+
floorPct: number;
|
|
137
|
+
referenceMedianNs: number;
|
|
138
|
+
samples: number;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
interface Environment {
|
|
142
|
+
cpuModel: string;
|
|
143
|
+
cores: number;
|
|
144
|
+
loadAvg1: number;
|
|
145
|
+
loadAvg5: number;
|
|
146
|
+
noise: NoiseFloor;
|
|
39
147
|
}
|
|
40
148
|
|
|
41
149
|
export interface Workload {
|
|
@@ -67,11 +175,23 @@ export interface Workload {
|
|
|
67
175
|
* on the task, its group, or the suite), vs. sharing its suite file's
|
|
68
176
|
* subprocess with other tasks. */
|
|
69
177
|
isolated?: boolean;
|
|
178
|
+
/** Structured parameters this task point represents (e.g. `{ size: 800,
|
|
179
|
+
* impl: "fast" }`), from `task(name, fn, { params })` or a `sweep()` point.
|
|
180
|
+
* Lets renderers pivot and `compare` match on them instead of only on the
|
|
181
|
+
* task name. Part of the workload id when present, so two points that
|
|
182
|
+
* share a task name (a `sweep()`'s whole point) don't collide. */
|
|
183
|
+
params?: Record<string, string | number | boolean>;
|
|
184
|
+
/** From `task.skip()` or a `group.skip()` this task was inside. The runner
|
|
185
|
+
* never measures it, so this workload has no matching `Measurement`; a
|
|
186
|
+
* renderer prints it as a "- skipped" row instead of omitting it, and
|
|
187
|
+
* `compare` treats it as `unchanged` (with a `skipped` warning) rather
|
|
188
|
+
* than silently passing or failing to match it. */
|
|
189
|
+
skipped?: boolean;
|
|
70
190
|
}
|
|
71
191
|
|
|
72
192
|
type Phase = "timing" | "cpu" | "heap" | "memstats";
|
|
73
193
|
|
|
74
|
-
interface
|
|
194
|
+
interface Measurement {
|
|
75
195
|
id: string;
|
|
76
196
|
workloadId: string;
|
|
77
197
|
phase: Phase;
|
|
@@ -86,7 +206,14 @@ interface Run {
|
|
|
86
206
|
jit?: JitTierBreakdown;
|
|
87
207
|
warnings: Warning[];
|
|
88
208
|
artifacts: ArtifactRef[];
|
|
89
|
-
|
|
209
|
+
baselineMeasurementId?: string;
|
|
210
|
+
/** True when this timing measurement's trials were run round-robin against
|
|
211
|
+
* the other commands in the same `time()` call (`--interleave`, default on
|
|
212
|
+
* for 2+ commands) rather than run to completion before the next command
|
|
213
|
+
* started, so drift over the run's wall-clock span (thermal throttling, a
|
|
214
|
+
* noisy neighbor process) lands on every command equally instead of
|
|
215
|
+
* favoring whichever ran first or last. */
|
|
216
|
+
interleaved?: boolean;
|
|
90
217
|
}
|
|
91
218
|
|
|
92
219
|
interface Trial {
|
|
@@ -110,6 +237,15 @@ interface TimingStats {
|
|
|
110
237
|
mild: number;
|
|
111
238
|
severe: number;
|
|
112
239
|
};
|
|
240
|
+
/** 75th percentile, ns. Optional: absent on documents saved before this
|
|
241
|
+
* field existed (`loadDocument` never backfills it). */
|
|
242
|
+
p75?: number;
|
|
243
|
+
/** 99th percentile, ns. Same caveat as `p75`. */
|
|
244
|
+
p99?: number;
|
|
245
|
+
/** Median absolute deviation, ns: the median of `|sample - median|` across
|
|
246
|
+
* all samples. A robust spread measure that (unlike stddev) isn't skewed
|
|
247
|
+
* by the long right tail typical of wall-clock timings. */
|
|
248
|
+
mad?: number;
|
|
113
249
|
}
|
|
114
250
|
|
|
115
251
|
interface Frame {
|
|
@@ -166,6 +302,11 @@ interface MemoryEvidence {
|
|
|
166
302
|
maxRssBytes?: number;
|
|
167
303
|
peakCommitBytes?: number;
|
|
168
304
|
pageFaults?: number;
|
|
305
|
+
/** Bytes allocated per call, from `ostia bench --alloc`: heap size delta
|
|
306
|
+
* (`bun:jsc`'s `heapStats().heapSize`, falling back to
|
|
307
|
+
* `process.memoryUsage().heapUsed`) around one `Bun.gc(true)`-bracketed
|
|
308
|
+
* batch, divided by the batch size. */
|
|
309
|
+
bytesPerOp?: number;
|
|
169
310
|
}
|
|
170
311
|
|
|
171
312
|
interface JitTierBreakdown {
|
|
@@ -183,7 +324,7 @@ interface JitTierBreakdown {
|
|
|
183
324
|
}[];
|
|
184
325
|
}
|
|
185
326
|
|
|
186
|
-
export type WarningCode = "slow-first-run" | "outliers-detected" | "fast-command" | "nonzero-exit" | "instrumented-timing" | "artifact-missing" | "empty-profile" | "below-timer-resolution" | "cache-fallback-rerun" | "low-sample-count";
|
|
327
|
+
export type WarningCode = "slow-first-run" | "outliers-detected" | "fast-command" | "nonzero-exit" | "instrumented-timing" | "artifact-missing" | "empty-profile" | "below-timer-resolution" | "cache-fallback-rerun" | "low-sample-count" | "thin-comparison" | "noisy-machine" | "skipped" | "jit-cold";
|
|
187
328
|
|
|
188
329
|
export interface Warning {
|
|
189
330
|
code: WarningCode;
|
|
@@ -201,13 +342,28 @@ interface ArtifactRef {
|
|
|
201
342
|
|
|
202
343
|
interface Comparison {
|
|
203
344
|
id: string;
|
|
204
|
-
|
|
205
|
-
|
|
345
|
+
baselineMeasurementId: string;
|
|
346
|
+
candidateMeasurementId: string;
|
|
206
347
|
timing?: {
|
|
207
348
|
medianDeltaPct: number;
|
|
208
349
|
meanDeltaPct: number;
|
|
350
|
+
/** Same value as `medianDeltaPct`, named for what it is used for: the
|
|
351
|
+
* effect size the verdict rule tests against `thresholds.timingPct`. */
|
|
352
|
+
effectPct: number;
|
|
353
|
+
/** 95% bootstrap confidence interval on the difference of medians,
|
|
354
|
+
* percent of the baseline median. Absent when either side had fewer
|
|
355
|
+
* than 5 samples (see the `thin-comparison` warning). */
|
|
356
|
+
ci95?: [number, number];
|
|
357
|
+
/** Two-sided Mann-Whitney U p-value, tie-corrected normal approximation.
|
|
358
|
+
* Same absence condition as `ci95`. */
|
|
359
|
+
pValue?: number;
|
|
360
|
+
/** Seed for the bootstrap's PRNG, so `ci95` is reproducible. */
|
|
361
|
+
seed?: number;
|
|
209
362
|
verdict: "improved" | "regressed" | "unchanged";
|
|
210
363
|
};
|
|
364
|
+
/** Attached when timing fell back to the point-estimate rule (thin
|
|
365
|
+
* samples) or otherwise carries a caveat about this comparison. */
|
|
366
|
+
warnings?: Warning[];
|
|
211
367
|
frames?: {
|
|
212
368
|
frameKey: string;
|
|
213
369
|
name: string;
|
|
@@ -228,15 +384,37 @@ interface Comparison {
|
|
|
228
384
|
frameSelfPct: number;
|
|
229
385
|
heapTypePct: number;
|
|
230
386
|
minFrameSelfUs: number;
|
|
387
|
+
alpha: number;
|
|
388
|
+
bootstrapIterations: number;
|
|
389
|
+
/** `max(timingPct, base.environment.noise.floorPct,
|
|
390
|
+
* cand.environment.noise.floorPct)` - the threshold timing was actually
|
|
391
|
+
* tested against, once machine noise widens it past `timingPct`. */
|
|
392
|
+
effectiveTimingPct: number;
|
|
231
393
|
};
|
|
232
394
|
verdict: "pass" | "fail";
|
|
233
395
|
}
|
|
234
396
|
|
|
397
|
+
export declare function keep(value: unknown): void;
|
|
398
|
+
|
|
235
399
|
interface BenchOptions {
|
|
236
400
|
suites: string[];
|
|
237
|
-
|
|
401
|
+
/** Wall-clock sampling budget per task, ms (default 500). */
|
|
402
|
+
budgetMs?: number;
|
|
403
|
+
/** Exact trial count per task. When set, the budget is ignored - the
|
|
404
|
+
* in-process equivalent of `time()`'s `samples`. */
|
|
405
|
+
samples?: number;
|
|
238
406
|
minSamples?: number;
|
|
239
407
|
gc?: boolean;
|
|
408
|
+
/** Capture one extra `phase: "cpu"` measurement per task (200ms of the
|
|
409
|
+
* task looped under the JSC sampling profiler, JIT tiers included), never
|
|
410
|
+
* mixed into the timing numbers. `TaskOptions.cpu` / `GroupOptions.cpu`
|
|
411
|
+
* override this per task or group. */
|
|
412
|
+
cpu?: boolean;
|
|
413
|
+
/** Capture one extra `phase: "memstats"` measurement per task: bytes
|
|
414
|
+
* allocated per call, from a `Bun.gc(true)`-bracketed batch.
|
|
415
|
+
* `TaskOptions.alloc` / `GroupOptions.alloc` override this per task or
|
|
416
|
+
* group. */
|
|
417
|
+
alloc?: boolean;
|
|
240
418
|
filter?: string;
|
|
241
419
|
/** Suite files to run at once, each still in its own child process (default:
|
|
242
420
|
* 1). Files are independent by design, so this is a wall-clock win for
|
|
@@ -263,18 +441,34 @@ interface BenchOptions {
|
|
|
263
441
|
* of the suite's own top-level code. Consumer-authored; ostia ships no
|
|
264
442
|
* preload scripts itself. */
|
|
265
443
|
preload?: string[];
|
|
444
|
+
/** Extra flags passed through to the `bun` invocation that runs each suite
|
|
445
|
+
* file (e.g. `["--conditions", "browser"]`), inserted before the runner
|
|
446
|
+
* script path so `bun` itself parses them rather than the runner. Useful
|
|
447
|
+
* for suites that import packages whose `exports` map branches on a
|
|
448
|
+
* resolution condition Bun doesn't set by default (e.g. Svelte/Vue's
|
|
449
|
+
* `browser` vs `default` builds). */
|
|
450
|
+
bunFlags?: string[];
|
|
451
|
+
/** Measure this machine's noise floor before the first task per suite
|
|
452
|
+
* subprocess (default: true) and stamp it on the document as
|
|
453
|
+
* `environment`. Set false to skip the ~200ms reference measurement. */
|
|
454
|
+
noiseCheck?: boolean;
|
|
266
455
|
}
|
|
267
456
|
|
|
268
457
|
export declare function bench(opts: BenchOptions): Promise<ProfileDocument>;
|
|
269
458
|
|
|
270
459
|
export declare function range(start: number, end: number, multiplier?: number): number[];
|
|
271
460
|
|
|
461
|
+
type Hook = () => unknown | Promise<unknown>;
|
|
462
|
+
|
|
272
463
|
export interface TaskOptions {
|
|
273
464
|
/** Marks this task as the Relative reference for its group in the table
|
|
274
465
|
* renderer, mirroring mitata's `baseline()`. At most one per group. */
|
|
275
466
|
baseline?: boolean;
|
|
276
|
-
/** Per-task time budget; overrides the suite-wide `--
|
|
277
|
-
|
|
467
|
+
/** Per-task time budget; overrides the suite-wide `--budget` / `budgetMs`. */
|
|
468
|
+
budgetMs?: number;
|
|
469
|
+
/** Per-task exact trial count; overrides the suite-wide `--samples` /
|
|
470
|
+
* `samples`. When set, the budget is ignored for this task. */
|
|
471
|
+
samples?: number;
|
|
278
472
|
/** Per-task hard floor on trials; overrides the suite-wide `--min-samples` /
|
|
279
473
|
* `minSamples`. */
|
|
280
474
|
minSamples?: number;
|
|
@@ -289,6 +483,30 @@ export interface TaskOptions {
|
|
|
289
483
|
/** Overrides the suite-wide `bench({ gc })` / `--gc` (and any
|
|
290
484
|
* `GroupOptions.gc`) for this task only. */
|
|
291
485
|
gc?: boolean;
|
|
486
|
+
/** Overrides the suite-wide `bench({ cpu })` / `--cpu` (and any
|
|
487
|
+
* `GroupOptions.cpu`) for this task only: after the timing measurement,
|
|
488
|
+
* capture one extra `phase: "cpu"` measurement (JIT tiers included) on
|
|
489
|
+
* the same workload, never mixed into the timing numbers. */
|
|
490
|
+
cpu?: boolean;
|
|
491
|
+
/** Overrides the suite-wide `bench({ alloc })` / `--alloc` (and any
|
|
492
|
+
* `GroupOptions.alloc`) for this task only: after the timing measurement,
|
|
493
|
+
* capture one extra `phase: "memstats"` measurement with bytes
|
|
494
|
+
* allocated per call. */
|
|
495
|
+
alloc?: boolean;
|
|
496
|
+
/** Structured parameters this task represents (e.g. `{ size: 800, impl:
|
|
497
|
+
* "fast" }`), written to `Workload.params` and folded into the workload id
|
|
498
|
+
* so points with the same task name don't collide. Inside `sweep()`, the
|
|
499
|
+
* current point is inherited automatically; an explicit `params` here
|
|
500
|
+
* merges over it (explicit keys win). */
|
|
501
|
+
params?: Record<string, string | number | boolean>;
|
|
502
|
+
/** Runs once, unmeasured, immediately before this task's warmup - in the
|
|
503
|
+
* task's own process, so it works with `isolate`. No per-trial hook: that
|
|
504
|
+
* would defeat batching. Use `gc` (Bun.gc between trials) or `isolate`
|
|
505
|
+
* (a fresh process per task) for per-trial concerns instead. */
|
|
506
|
+
before?: Hook;
|
|
507
|
+
/** Runs once, unmeasured, immediately after this task's last trial. Same
|
|
508
|
+
* process/no-per-trial caveats as `before`. */
|
|
509
|
+
after?: Hook;
|
|
292
510
|
}
|
|
293
511
|
|
|
294
512
|
export interface GroupOptions {
|
|
@@ -301,20 +519,57 @@ export interface GroupOptions {
|
|
|
301
519
|
/** Default `gc` for every task in this group, unless a task overrides it
|
|
302
520
|
* with its own `TaskOptions.gc`. */
|
|
303
521
|
gc?: boolean;
|
|
522
|
+
/** Default `cpu` for every task in this group, unless a task overrides it
|
|
523
|
+
* with its own `TaskOptions.cpu`. */
|
|
524
|
+
cpu?: boolean;
|
|
525
|
+
/** Default `alloc` for every task in this group, unless a task overrides
|
|
526
|
+
* it with its own `TaskOptions.alloc`. */
|
|
527
|
+
alloc?: boolean;
|
|
528
|
+
/** Runs once, unmeasured, before the group's first task's warmup (not
|
|
529
|
+
* before every task) - in whichever process runs that task, so it works
|
|
530
|
+
* with `isolate`. */
|
|
531
|
+
before?: Hook;
|
|
532
|
+
/** Runs once, unmeasured, after the group's last task's last trial. */
|
|
533
|
+
after?: Hook;
|
|
304
534
|
}
|
|
305
535
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
536
|
+
interface GroupFn {
|
|
537
|
+
(name: string, fn: () => void, opts?: GroupOptions): void;
|
|
538
|
+
/** Registers every task inside as skipped: the runner never measures
|
|
539
|
+
* them, but the document still carries their workloads (marked
|
|
540
|
+
* `Workload.skipped`). */
|
|
541
|
+
skip: (name: string, fn: () => void, opts?: GroupOptions) => void;
|
|
542
|
+
/** When any task or group in the suite uses `.only`, the runner restricts
|
|
543
|
+
* the whole suite file to only those tasks (before `--filter` narrows
|
|
544
|
+
* further) and prints a one-line notice to stderr. */
|
|
545
|
+
only: (name: string, fn: () => void, opts?: GroupOptions) => void;
|
|
546
|
+
}
|
|
309
547
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
548
|
+
export declare const group: GroupFn;
|
|
549
|
+
|
|
550
|
+
interface TaskFn {
|
|
551
|
+
(name: string, fn: () => unknown | Promise<unknown>, opts?: TaskOptions): void;
|
|
552
|
+
/** Registers the task as skipped: the runner never measures it, but the
|
|
553
|
+
* document still carries its workload (marked `Workload.skipped`) so a
|
|
554
|
+
* renderer or `compare` can say so explicitly instead of the task simply
|
|
555
|
+
* being absent. */
|
|
556
|
+
skip: (name: string, fn: () => unknown | Promise<unknown>, opts?: TaskOptions) => void;
|
|
557
|
+
/** When any task or group in the suite uses `.only`, the runner restricts
|
|
558
|
+
* the whole suite file to only those tasks (before `--filter` narrows
|
|
559
|
+
* further) and prints a one-line notice to stderr. */
|
|
560
|
+
only: (name: string, fn: () => unknown | Promise<unknown>, opts?: TaskOptions) => void;
|
|
315
561
|
}
|
|
316
562
|
|
|
317
|
-
export declare
|
|
563
|
+
export declare const task: TaskFn;
|
|
564
|
+
|
|
565
|
+
type SweepPoint<T extends Record<string, readonly unknown[]>> = {
|
|
566
|
+
[K in keyof T]: T[K][number];
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
export declare function sweep<T extends Record<string, readonly unknown[]>>(dims: T, fn: (point: SweepPoint<T>) => void): void;
|
|
570
|
+
|
|
571
|
+
export declare function newDocument(workloads: Workload[], measurements: Measurement[], environment?: Environment): ProfileDocument;
|
|
572
|
+
export { newDocument as createDocument };
|
|
318
573
|
|
|
319
574
|
export declare function saveDocument(doc: ProfileDocument, path: string): Promise<void>;
|
|
320
575
|
|
|
@@ -342,14 +597,24 @@ export interface MinimalLine {
|
|
|
342
597
|
group?: string;
|
|
343
598
|
description?: string;
|
|
344
599
|
groupDescription?: string;
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
600
|
+
/** From `task(name, fn, { params })` or a `sweep()` point. */
|
|
601
|
+
params?: Record<string, string | number | boolean>;
|
|
602
|
+
/** From `task.skip()` / `group.skip()`: no measurement was taken, so every
|
|
603
|
+
* stats field below is absent on this line. */
|
|
604
|
+
skipped?: true;
|
|
605
|
+
unit?: "ns";
|
|
606
|
+
samples?: number;
|
|
607
|
+
mean?: number;
|
|
608
|
+
median?: number;
|
|
609
|
+
stddev?: number;
|
|
610
|
+
stddevPct?: number;
|
|
611
|
+
min?: number;
|
|
612
|
+
max?: number;
|
|
613
|
+
/** 75th/99th percentile and median absolute deviation, ns. Absent on
|
|
614
|
+
* documents saved before these fields existed. */
|
|
615
|
+
p75?: number;
|
|
616
|
+
p99?: number;
|
|
617
|
+
mad?: number;
|
|
353
618
|
/** Median over the group's reference median (its baseline task, else its
|
|
354
619
|
* fastest). Only present when the document has more than one timing run. */
|
|
355
620
|
relative?: number;
|
|
@@ -365,5 +630,10 @@ export interface MinimalLine {
|
|
|
365
630
|
meanPct: number;
|
|
366
631
|
verdict: "improved" | "regressed" | "unchanged";
|
|
367
632
|
pass: boolean;
|
|
633
|
+
/** 95% bootstrap CI on the difference of medians and the Mann-Whitney
|
|
634
|
+
* p-value behind the verdict. Absent on a thin (<5 samples/side)
|
|
635
|
+
* comparison, which falls back to a point-estimate threshold. */
|
|
636
|
+
ci95?: [number, number];
|
|
637
|
+
pValue?: number;
|
|
368
638
|
};
|
|
369
639
|
}
|
package/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{
|
|
2
|
+
import{c,b,_,L,s,H,R,J}from"./chunk-r5b8vf8w.js";import{n,r,t,x,V,G}from"./chunk-qbt85kmg.js";export{c as bench,b as compareDocuments,n as createDocument,H as defineConfig,V as group,x as keep,t as loadDocument,J as profile,_ as range,s as renderers,r as saveDocument,L as sweep,G as task,R as time};
|
package/package.json
CHANGED
package/runner.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
-
import{
|
|
4
|
-
`),2;let
|
|
5
|
-
`),2;let
|
|
6
|
-
`),
|
|
3
|
+
import{n,N,i,a,o,r,d,f,g,F,O,B,h,C,U,W,A,j}from"./chunk-qbt85kmg.js";import{heapStats as X}from"bun:jsc";var Y=100;function z(){try{return X().heapSize}catch{return process.memoryUsage().heapUsed}}async function Z(l,m=Y){let w=Bun.nanoseconds();Bun.gc(!0);let t=z();for(let u=0;u<m;u++){let p=l();if(p instanceof Promise)await p}Bun.gc(!0);let k=z(),c=Bun.nanoseconds()-w;return{memory:{origin:"heapStats",bytesPerOp:Math.max(0,(k-t)/m)},diagnosticWallNs:c}}var ee=200,te=20;async function q(l,m=ee){let w=m*1e6,t=async()=>{let p=Bun.nanoseconds();while(Bun.nanoseconds()-p<w){let b=l();if(b instanceof Promise)await b}},{cpu:k,jit:c,diagnosticWallNs:u}=await d(t);return{cpu:k,jit:c,diagnosticWallNs:u}}function K(l){let{llint:m,baseline:w,dfg:t,ftl:k}=l.tiers,c=m+w+t+k;if(c===0)return;let u=m/c*100,p=w/c*100,b=t/c*100,x=k/c*100;if(u+p<=te)return;return{code:"jit-cold",message:`${(u+p).toFixed(1)}% of CPU samples were in the llint/baseline tiers: the JIT never warmed this task up.`,data:{llintPct:u,baselinePct:p,dfgPct:b,ftlPct:x}}}async function ne(){let[l,m,w]=process.argv.slice(2);if(!l||!m)return process.stderr.write(`bench runner: usage: runner.ts <suiteFile> <outputPath> [optsJson]
|
|
4
|
+
`),2;let t=w?JSON.parse(w):{};for(let s of t.preload??[])await import(s);B(),await import(l);let k=O();if(k.length===0)return process.stderr.write(`bench runner: ${l} registered no tasks (no task() calls found).
|
|
5
|
+
`),2;let c=k.filter((s)=>s.only),u=c.length>0?c:k;if(c.length>0)process.stderr.write(`bench: ${c.length} task(s) selected by .only
|
|
6
|
+
`);let p=j(u,t.filter);if(t.taskIds){let s=new Set(t.taskIds);p=p.filter((e)=>s.has(h(e)))}if(p.length===0)return process.stderr.write(`bench runner: --filter ${JSON.stringify(t.filter)} matched zero of ${u.length} registered task(s) in ${l}.
|
|
7
|
+
`),2;if(t.planPath){let s=p.map((e)=>({id:h(e),isolate:C(e,t.isolate??!1)}));await Bun.write(t.planPath,JSON.stringify({tasks:s}))}let b=t.taskIds?p:p.filter((s)=>!C(s,t.isolate??!1)),S=b.some((s)=>!s.skipped)&&t.noiseCheck!==!1?f():void 0,R=S?g(S):void 0,v=new Map,E=new Map;b.forEach((s,e)=>{if(s.groupName===void 0||s.skipped)return;if(!v.has(s.groupName))v.set(s.groupName,e);E.set(s.groupName,e)});let J=[],P=[];for(let s=0;s<b.length;s++){let e=b[s],D=h(e),I=N(l,D,{label:D,baseline:e.baseline,group:e.groupName,description:e.opts?.description,groupDescription:e.groupDescription,isolated:t.markIsolated,params:e.params,skipped:e.skipped});if(J.push(I),e.skipped)continue;let Q=e.groupName!==void 0&&v.get(e.groupName)===s,V=e.groupName!==void 0&&E.get(e.groupName)===s;if(Q&&e.groupBefore)await e.groupBefore();if(e.opts?.before)await e.opts.before();let _=e.opts?.budgetMs??t.budgetMs,L=e.opts?.samples??t.samples,H=e.opts?.minSamples??t.minSamples,T={...t,..._!==void 0&&{budgetMs:_},...L!==void 0&&{samples:L},...H!==void 0&&{minSamples:H},gc:U(e,t.gc??!1)},M=await F(e.fn,T);if(P.push(i({workload:I,configFingerprint:o({budgetMs:T.budgetMs??null,samples:T.samples??null,minSamples:T.minSamples??null,gc:T.gc??!1}),trials:M.trials,timing:M.timing,warnings:R&&P.length===0?[...M.warnings,R]:M.warnings})),W(e,t.cpu??!1)){let y=await q(e.fn),G=K(y.jit);P.push(a({workload:I,phase:"cpu",configFingerprint:o({cpu:!0}),diagnosticWallNs:y.diagnosticWallNs,cpu:y.cpu,jit:y.jit,warnings:G?[G]:[],artifacts:[]}))}if(A(e,t.alloc??!1)){let y=await Z(e.fn);P.push(a({workload:I,phase:"memstats",configFingerprint:o({alloc:!0}),diagnosticWallNs:y.diagnosticWallNs,memory:y.memory,warnings:[],artifacts:[]}))}if(e.opts?.after)await e.opts.after();if(V&&e.groupAfter)await e.groupAfter()}return await r(n(J,P,S),m),0}ne().then((l)=>process.exit(l));
|
package/chunk-nfgy545q.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
function h(n){return JSON.stringify(F(n))}function F(n){if(Array.isArray(n))return n.map(F);if(n!==null&&typeof n==="object"){let a={};for(let d of Object.keys(n).sort())a[d]=F(n[d]);return a}return n}function e(n,...a){let d=Bun.CryptoHasher.hash("sha256",h(a),"hex");return`${n}_${d.slice(0,16)}`}var c="0.1.0";function r(n,a){return{schemaVersion:1,toolVersion:c,bunVersion:Bun.version,platform:{os:process.platform,arch:process.arch},createdAt:new Date().toISOString(),workloads:n,runs:a}}function u(n,a){return{id:e("wl","subprocess",n,process.cwd()),kind:"subprocess",command:n,label:a}}function R(n,a){return{id:e("wl","inprocess",n.name,n.toString()),kind:"inprocess",label:a}}function P(n,a,d={}){return{id:e("wl","inprocess-entry",n,a),kind:"inprocess",entry:{file:n,task:a,...d.group!==void 0&&{group:d.group}},...d.label!==void 0&&{label:d.label},...d.baseline!==void 0&&{baseline:d.baseline},...d.description!==void 0&&{description:d.description},...d.groupDescription!==void 0&&{groupDescription:d.groupDescription},...d.isolated!==void 0&&{isolated:d.isolated}}}function i(n){return{id:e("run",n.workload.id,"timing",n.configFingerprint,Bun.version,c),workloadId:n.workload.id,phase:"timing",instrumented:!1,configFingerprint:n.configFingerprint,trials:n.trials,timing:n.timing,warnings:n.warnings,artifacts:[],memory:Q(n.trials)}}function Q(n){let a=n.map((d)=>d.maxRssBytes).filter((d)=>d!==void 0);if(a.length===0)return;return{origin:"resourceUsage",perTrial:n.map((d)=>({rssBytes:d.maxRssBytes})),maxRssBytes:Math.max(...a)}}function b(n){return{id:e("run",n.workload.id,n.phase,n.configFingerprint,Bun.version,c),workloadId:n.workload.id,phase:n.phase,instrumented:!0,configFingerprint:n.configFingerprint,trials:[{i:0,wallNs:n.diagnosticWallNs,exitCode:n.exitCode}],diagnosticWallNs:n.diagnosticWallNs,cpu:n.cpu,heap:n.heap,jit:n.jit,warnings:n.warnings,artifacts:n.artifacts}}async function T(n,a,d){let f=await Bun.file(d).arrayBuffer(),w=new Bun.CryptoHasher("sha256");return w.update(f),{id:e("art",n,a,d),kind:a,path:d,sha256:w.digest("hex"),bytes:f.byteLength}}function s(n){return e("cfg",n)}function k(n){return`${JSON.stringify(F(n),null,2)}
|
|
3
|
-
`}async function o(n,a){await Bun.write(a,k(n))}async function t(n){let a=await Bun.file(n).text();return JSON.parse(a)}var G=[],W;function U(n,a,d){let g=W;W={name:n,description:d?.description,isolate:d?.isolate,gc:d?.gc};try{a()}finally{W=g}}function M(n,a,d){G.push({groupName:W?.name,groupDescription:W?.description,groupIsolate:W?.isolate,groupGc:W?.gc,name:n,fn:a,baseline:d?.baseline,opts:d})}function I(){return G}function C(){G.length=0,W=void 0}function m(n){return n.groupName?`${n.groupName}/${n.name}`:n.name}function N(n,a){return n.opts?.isolate??n.groupIsolate??a}function D(n,a){return n.opts?.gc??n.groupGc??a}function v(n,a){if(!a)return[...n];let d=new RegExp(a);return n.filter((g)=>d.test(m(g)))}function l(n){if(n.length===0)throw Error("computeTimingStats: samples must be non-empty");let a=n.length,d=L(n),g=0;for(let y=0;y<a;y++)g+=n[y];let f=g/a,w=O(d,0.5),x=0;for(let y=0;y<a;y++){let S=n[y]-f;x+=S*S}let A=Math.sqrt(x/a),H=d[0],B=d[a-1],q=O(d,0.25),_=O(d,0.75),E=_-q,z=q-1.5*E,V=_+1.5*E,K=q-3*E,Z=_+3*E,J=0,j=0;for(let y=0;y<a;y++){let S=n[y];if(S<K||S>Z)j++;else if(S<z||S>V)J++}return{unit:"ns",samples:n,mean:f,median:w,stddev:A,min:H,max:B,outliers:{mild:J,severe:j}}}function L(n){let a=new Float64Array(n.length);return a.set(n),a.sort(),a}function O(n,a){let d=n.length;if(d===1)return n[0];let g=a*(d-1),f=Math.floor(g),w=Math.ceil(g);if(f===w)return n[f];let x=g-f;return n[f]*(1-x)+n[w]*x}var X=5000000,Y=200;function p(n,a,d="subprocess"){let g=[],f=n.samples[0];if(f!==void 0){let x=L(n.samples),A=O(x,0.25),B=O(x,0.75)-A;if(f>n.median+3*B&&B>0)g.push({code:"slow-first-run",message:`First run took ${(f/1e6).toFixed(2)}ms, much slower than the median ${(n.median/1e6).toFixed(2)}ms. Consider more warmup.`,data:{firstNs:f,medianNs:n.median}})}if(n.outliers.mild+n.outliers.severe>0)g.push({code:"outliers-detected",message:`${n.outliers.mild+n.outliers.severe} outlier(s) detected (${n.outliers.severe} severe, ${n.outliers.mild} mild).`,data:n.outliers});if(d==="subprocess"&&n.median<X)g.push({code:"fast-command",message:`Median run time (${(n.median/1e6).toFixed(3)}ms) is very fast; results may be dominated by spawn overhead.`,data:{medianNs:n.median}});if(d==="inprocess"&&n.median<Y)g.push({code:"below-timer-resolution",message:`Median run time (${n.median.toFixed(0)}ns) is close to timer resolution; consider a larger batch size or a coarser operation.`,data:{medianNs:n.median}});let w=a.filter((x)=>x!==void 0&&x!==0);if(w.length>0)g.push({code:"nonzero-exit",message:`${w.length} of ${a.length} trial(s) exited non-zero.`,data:{exitCodes:w}});return g}
|
|
4
|
-
export{h,e,c,r,u,R,P,i,b,T,s,k,o,t,l,p,U,M,I,C,m,N,D,v};
|
package/chunk-tw064qem.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import{h,e,r,u,R,i,b,T,s,k,t,l,p}from"./chunk-nfgy545q.js";function _e(o){return o.startsWith("file://")?o.slice(7):o}function ie(o,N,c){let I=o.nodes,m=I.length,v=new Map,C=[],P=new Map,U=new Int32Array(m);for(let W=0;W<m;W++){let E=I[W],B=E.callFrame,H=_e(B.url),G=v.get(B.functionName);if(G===void 0)G=new Map,v.set(B.functionName,G);let Y=G.get(H);if(Y===void 0)Y=C.length,G.set(H,Y),C.push({key:e("fr",B.functionName,H),name:B.functionName,url:H||void 0,line:B.lineNumber>=0?B.lineNumber:void 0,col:B.columnNumber>=0?B.columnNumber:void 0});U[W]=Y,P.set(E.id,W)}let D=Array(m);for(let W=0;W<m;W++){let E=I[W];D[W]={id:E.id,frameIx:U[P.get(E.id)],children:E.children??[]}}let M=new Float64Array(m),O=new Float64Array(m),{samples:A,timeDeltas:z}=o;for(let W=0;W<A.length;W++){let E=P.get(A[W]);if(E===void 0)continue;M[E]+=z[W]??0,O[E]+=1}let K=new Int32Array(m).fill(-1);for(let W=0;W<m;W++){let E=I[W].children;if(!E)continue;for(let B of E){let H=P.get(B);if(H!==void 0)K[H]=W}}let _=[],J=[];for(let W=m-1;W>=0;W--)if(K[W]===-1)J.push(W);while(J.length>0){let W=J.pop();_.push(W);let E=I[W].children;if(!E)continue;for(let B of E){let H=P.get(B);if(H!==void 0&&K[H]===W)J.push(H)}}let V=new Float64Array(m);for(let W=_.length-1;W>=0;W--){let E=_[W];V[E]+=M[E];let B=K[E];if(B>=0)V[B]+=V[E]}let L=Array(C.length),j=[];for(let W=0;W<m;W++){let E=D[W].frameIx,B=L[E];if(B)B.selfUs+=M[W],B.totalUs+=V[W],B.samples+=O[W];else{let H={frameIx:E,selfUs:M[W],totalUs:V[W],samples:O[W]};L[E]=H,j.push(H)}}return{origin:N,samplingIntervalUs:c,frames:C,nodes:D,totals:j.sort((W,E)=>E.selfUs-W.selfUs),samples:{nodeIds:o.samples,timeDeltasUs:o.timeDeltas}}}function Le(o,N,c,I){let m=["--cpu-prof","--cpu-prof-dir",N,"--cpu-prof-name",c,"--cpu-prof-interval",String(I)],v=o[0];if(v==="bun"||v?.endsWith("/bun"))return[v,...m,...o.slice(1)];return o}async function fe(o){let N=`${o.artifactDir}/${o.fileName}`,c=o.argv[0],I=c==="bun"||c?.endsWith("/bun"),m=Le(o.argv,o.artifactDir,o.fileName,o.intervalUs),v=I?o.env:{...process.env,...o.env,BUN_OPTIONS:`--cpu-prof --cpu-prof-dir ${o.artifactDir} --cpu-prof-name ${o.fileName} --cpu-prof-interval ${o.intervalUs}`},C=Bun.nanoseconds(),U=await Bun.spawn(m,{cwd:o.cwd,env:v,stdout:"ignore",stderr:"ignore",stdin:"ignore"}).exited,D=Bun.nanoseconds()-C,M=Bun.file(N);if(!await M.exists())return{diagnosticWallNs:D,exitCode:U,warnings:[{code:"artifact-missing",message:`Expected a .cpuprofile at ${N} after exit ${U}, found nothing. The workload's argv[0] must be a \`bun\` binary for CPU capture.`,data:{artifactPath:N,argv:o.argv}}]};let O=await M.json(),A=ie(O,"cpu-prof",o.intervalUs),z=O.samples.length===0?[{code:"empty-profile",message:"CPU capture produced zero samples."}]:[];return{diagnosticWallNs:D,exitCode:U,artifactPath:N,cpu:A,warnings:z}}function ge(o,N="heap-prof"){let{node_fields:c,node_types:I}=o.snapshot.meta,m=c.indexOf("type"),v=c.indexOf("self_size"),C=c.length,P=I[0];if(m===-1||v===-1||!Array.isArray(P))return{origin:N,typeCounts:[],objectCount:o.snapshot.node_count};let U=P.length,D=Array(U),M=new Map,O=[],A=0,z=o.nodes,K=z.length;for(let j=0;j<K;j+=C){let W=z[j+m],E=z[j+v]??0;A+=E;let B;if(W>=0&&W<U){if(B=D[W],B===void 0)B={type:P[W],count:0,bytes:0},D[W]=B,O.push(B)}else{let H=`unknown(${W})`;if(B=M.get(H),B===void 0)B={type:H,count:0,bytes:0},M.set(H,B),O.push(B)}B.count++,B.bytes+=E}let _=O.sort((j,W)=>W.count-j.count),J=_.slice(0,20),V=_.slice(20),L=J.map(({type:j,count:W,bytes:E})=>({type:j,count:W,retainedBytes:E}));if(V.length>0){let j=0,W=0;for(let E of V)j+=E.count,W+=E.bytes;L.push({type:"other",count:j,retainedBytes:W})}return{origin:N,heapSizeBytes:A,objectCount:o.snapshot.node_count,typeCounts:L}}function He(o,N,c){let I=["--heap-prof","--heap-prof-dir",N,"--heap-prof-name",c],m=o[0];if(m==="bun"||m?.endsWith("/bun"))return[m,...I,...o.slice(1)];return o}async function he(o){let N=`${o.artifactDir}/${o.fileName}`,c=o.argv[0],I=c==="bun"||c?.endsWith("/bun"),m=He(o.argv,o.artifactDir,o.fileName),v=I?o.env:{...process.env,...o.env,BUN_OPTIONS:`--heap-prof --heap-prof-dir ${o.artifactDir} --heap-prof-name ${o.fileName}`},C=Bun.nanoseconds(),U=await Bun.spawn(m,{cwd:o.cwd,env:v,stdout:"ignore",stderr:"ignore",stdin:"ignore"}).exited,D=Bun.nanoseconds()-C,M=Bun.file(N);if(!await M.exists())return{diagnosticWallNs:D,exitCode:U,warnings:[{code:"artifact-missing",message:`Expected a heap snapshot at ${N} after exit ${U}, found nothing. The workload's argv[0] must be a \`bun\` binary for heap capture.`,data:{artifactPath:N,argv:o.argv}}]};let O=await M.json(),A=ge(O,"heap-prof");return{diagnosticWallNs:D,exitCode:U,artifactPath:N,heap:A,warnings:[]}}import{Session as Je}from"inspector/promises";var ze=1000;async function be(o,N={}){let c=N.intervalUs??ze,I=new Je;I.connect();let m=Bun.nanoseconds();try{await I.post("Profiler.enable"),await I.post("Profiler.setSamplingInterval",{interval:c}),await I.post("Profiler.start");let v=await o(),{profile:C}=await I.post("Profiler.stop"),P=Bun.nanoseconds()-m,U=ie(C,"inspector",c);return{result:v,cpu:U,diagnosticWallNs:P}}finally{I.disconnect()}}import{profile as Ke}from"bun:jsc";var Ve=new Map([["LLInt","llint"],["Baseline","baseline"],["DFG","dfg"],["FTL","ftl"]]),ye=4294967295;function we(o,N){let c=N??o.interval*1e6,I=new Map,m=[];function v(B,H,G,Y){let Q=I.get(B);if(Q===void 0)Q=new Map,I.set(B,Q);let q=H??"",Z=Q.get(q);if(Z===void 0)Z=m.length,Q.set(q,Z),m.push({key:e("fr",B,q),name:B,url:H,line:G,col:Y});return Z}function C(B){let H=B.line===ye,G=H?void 0:B.line-1,Y=H||B.column===ye?void 0:B.column-1;return v(B.name,B.sourceURL,G,Y)}let P=v("(root)",void 0,void 0,void 0),U=1,D={id:0,frameIx:P,children:new Map,selfUs:0,samples:0,totalUs:0},M=new Map([[0,D]]),O={llint:0,baseline:0,dfg:0,ftl:0},A=new Map,z=[],K=[];for(let B of o.traces){let H=B.frames,G=D;for(let q=H.length-1;q>=0;q--){let Z=C(H[q]),te=G.children.get(Z);if(!te)te={id:U++,frameIx:Z,children:new Map,selfUs:0,samples:0,totalUs:0},G.children.set(Z,te),M.set(te.id,te);G=te}G.selfUs+=c,G.samples+=1,z.push(G.id),K.push(c);let Y=H[0],Q=Y&&Ve.get(Y.category);if(Q){O[Q]++;let q=A.get(Q)??new Map;q.set(G.frameIx,(q.get(G.frameIx)??0)+1),A.set(Q,q)}}function _(B){let H=B.selfUs;for(let G of B.children.values())H+=_(G);return B.totalUs=H,H}_(D);let J=new Map;function V(B){let H=J.get(B.frameIx);if(H)H.selfUs+=B.selfUs,H.totalUs+=B.totalUs,H.samples+=B.samples;else J.set(B.frameIx,{frameIx:B.frameIx,selfUs:B.selfUs,totalUs:B.totalUs,samples:B.samples});for(let G of B.children.values())V(G)}V(D);let L=[...M.values()].map((B)=>({id:B.id,frameIx:B.frameIx,children:[...B.children.values()].map((H)=>H.id)})),j={origin:"jsc-profile",samplingIntervalUs:c,frames:m,nodes:L,totals:[...J.values()].sort((B,H)=>H.selfUs-B.selfUs),samples:{nodeIds:z,timeDeltasUs:K}},W=[...A.entries()].flatMap(([B,H])=>[...H.entries()].sort((G,Y)=>Y[1]-G[1]).slice(0,3).map(([G,Y])=>({tier:B,frameKey:m[G].key,samples:Y})));return{cpu:j,jit:{origin:"jsc-profile",tiers:O,topFramesByTier:W}}}var Ge=1000;async function xe(o,N={}){let c=N.intervalUs??Ge,I,m=Bun.nanoseconds(),v=await Ke(async()=>(I=await o(),I),c),C=Bun.nanoseconds()-m,{cpu:P,jit:U}=we(v.stackTraces,c);return{result:I,cpu:P,jit:U,diagnosticWallNs:C}}async function le(o){let N=Bun.nanoseconds(),c=Bun.spawn(o.argv,{cwd:o.cwd,env:o.env,stdout:"ignore",stderr:"ignore",stdin:"ignore"}),I=await c.exited,m=Bun.nanoseconds(),v=c.resourceUsage?.();return{wallNs:m-N,exitCode:I,userNs:v?Number(v.cpuTime.user)*1000:void 0,systemNs:v?Number(v.cpuTime.system)*1000:void 0,maxRssBytes:v?.maxRSS}}function Re(o){return o.trim().split(/\s+/).filter(Boolean)}var Ye=10,qe=3000000000,Qe=3;async function g(o){let N=o.warmup??Qe;for(let M=0;M<N;M++)await le(o);let c=[],I=o.runs??o.minRuns??Ye,m=o.runs!==void 0?0:o.minTotalNs??qe,v=0,C=0;while(C<I||v<m){let M=await le(o);if(c.push({i:C,wallNs:M.wallNs,exitCode:M.exitCode,userNs:M.userNs,systemNs:M.systemNs,maxRssBytes:M.maxRssBytes}),v+=M.wallNs,C++,o.runs!==void 0&&C>=o.runs)break}let P=c.map((M)=>M.wallNs),U=l(P),D=p(U,c.map((M)=>M.exitCode));return{trials:c,timing:U,warnings:D}}var Pe=new URL("./runner.ts",import.meta.url).pathname,Xe="node_modules/.cache/ostia";function x(){return Math.max(1,navigator.hardwareConcurrency||1)}async function d(o){let c=`${o.outDir??Xe}/bench-tmp`,I=o.cwd??process.cwd(),m=Math.max(1,Math.floor(o.jobs??1)),v={timeBudgetMs:o.timeBudgetMs,minSamples:o.minSamples,gc:o.gc},C=o.suites.map((D)=>D.startsWith("/")?D:`${I}/${D}`),P=(o.preload??[]).map((D)=>D.startsWith("/")?D:`${I}/${D}`),U=async(D,M)=>{let O=new Set,A=0,z,K=async()=>{while(z===void 0&&A<D.length){let _=A++;try{let J=Bun.spawn(D[_],{cwd:I,stdout:"inherit",stderr:"inherit",stdin:"ignore"});O.add(J);let V=await J.exited;if(O.delete(J),V!==0)throw Error(`Bench suite failed: ${M(_)} (runner exited ${V})`)}catch(J){z??=J instanceof Error?J:Error(String(J));for(let V of O)V.kill()}}};if(await Promise.all(Array.from({length:Math.min(m,D.length)},K)),z)throw z};try{let D=C.map((L)=>`${c}/${e("bench-plan",L)}.json`),M=C.map((L,j)=>["bun",Pe,L,D[j],JSON.stringify({...v,filter:o.filter,isolate:o.isolate,preload:P,planOnly:!0})]);await U(M,(L)=>o.suites[L]);let O=await Promise.all(D.map(async(L)=>{let{tasks:j}=await Bun.file(L).json();return j})),A=[];for(let L=0;L<O.length;L++){let j=O[L].filter((W)=>!W.isolate).map((W)=>W.id);if(j.length>0)A.push({suiteIndex:L,taskIds:j,markIsolated:!1});for(let W of O[L])if(W.isolate)A.push({suiteIndex:L,taskIds:[W.id],markIsolated:!0})}let z=A.map((L,j)=>`${c}/${e("bench-item",C[L.suiteIndex],j)}.json`),K=A.map((L,j)=>["bun",Pe,C[L.suiteIndex],z[j],JSON.stringify({...v,taskIds:L.taskIds,preload:P,...L.markIsolated&&{markIsolated:!0}})]);await U(K,(L)=>o.suites[A[L].suiteIndex]);let _=await Promise.all(z.map(t)),J=[],V=[];for(let L=0;L<O.length;L++){let j=A.findIndex((H)=>H.suiteIndex===L&&!H.markIsolated),W=j>=0?_[j]:void 0,E=0,B=new Map;A.forEach((H,G)=>{if(H.suiteIndex===L&&H.markIsolated)B.set(H.taskIds[0],_[G])});for(let H of O[L])if(H.isolate){let G=B.get(H.id);J.push(G.workloads[0]),V.push(G.runs[0])}else J.push(W.workloads[E]),V.push(W.runs[E]),E++}return r(J,V)}finally{await Bun.spawn(["rm","-rf",c]).exited}}function F(o,N,c=8){if(c<=1)throw RangeError(`range: multiplier must be > 1, got ${c}`);if(o<=0)throw RangeError(`range: start must be > 0, got ${o}`);let I=[];for(let m=o;m<=N;m*=c)I.push(m);if(!I.includes(N))I.push(N);return I}var a={timingPct:5,frameSelfPct:10,heapTypePct:10,minFrameSelfUs:1000};function ae(o,N){if(o===0)return N===0?0:1/0;return(N-o)/o*100}function re(o,N,c){return o.runs.find((I)=>I.workloadId===N&&I.phase===c)}function f(o,N,c=a){let I=new Set(N.workloads.map((v)=>v.id)),m=[];for(let v of o.workloads){if(!I.has(v.id))continue;let C=w(o,N,v.id,c);if(C)m.push(C)}return m}function w(o,N,c,I=a){let m=re(o,c,"timing"),v=re(N,c,"timing"),C=re(o,c,"cpu"),P=re(N,c,"cpu"),U=re(o,c,"heap"),D=re(N,c,"heap"),M=m?.id??C?.id??U?.id,O=v?.id??P?.id??D?.id;if(!M||!O)return;let A=!1,z;if(m?.timing&&v?.timing){let J=ae(m.timing.median,v.timing.median),V=ae(m.timing.mean,v.timing.mean),L=J>I.timingPct?"regressed":J<-I.timingPct?"improved":"unchanged";if(L==="regressed")A=!0;z={medianDeltaPct:J,meanDeltaPct:V,verdict:L}}let K;if(C?.cpu&&P?.cpu){let J=new Map(C.cpu.totals.map((E)=>[C.cpu.frames[E.frameIx].key,E])),V=new Map(P.cpu.totals.map((E)=>[P.cpu.frames[E.frameIx].key,E])),L=new Map(C.cpu.frames.map((E)=>[E.key,E.name])),j=new Map(P.cpu.frames.map((E)=>[E.key,E.name]));K=[...new Set([...J.keys(),...V.keys()])].map((E)=>{let B=J.get(E)?.selfUs??0,H=V.get(E)?.selfUs??0;return{frameKey:E,name:j.get(E)??L.get(E)??E,baseSelfUs:B,candSelfUs:H,deltaPct:ae(B,H)}}).sort((E,B)=>Math.abs(B.deltaPct)-Math.abs(E.deltaPct));for(let E of K)if((E.baseSelfUs>=I.minFrameSelfUs||E.candSelfUs>=I.minFrameSelfUs)&&E.deltaPct>I.frameSelfPct)A=!0}let _;if(U?.heap&&D?.heap){let J=new Map(U.heap.typeCounts.map((j)=>[j.type,j])),V=new Map(D.heap.typeCounts.map((j)=>[j.type,j]));_=[...new Set([...J.keys(),...V.keys()])].map((j)=>{let W=J.get(j),E=V.get(j);return{type:j,baseCount:W?.count??0,candCount:E?.count??0,baseBytes:W?.retainedBytes,candBytes:E?.retainedBytes,deltaPct:ae(W?.count??0,E?.count??0)}}).sort((j,W)=>Math.abs(W.deltaPct)-Math.abs(j.deltaPct));for(let j of _)if(j.deltaPct>I.heapTypePct)A=!0}return{id:e("cmp",M,O),baselineRunId:M,candidateRunId:O,timing:z,frames:K,heapTypes:_,thresholds:I,verdict:A?"fail":"pass"}}function ee(o,N){if(N){let c=o.runs.find((I)=>I.id===N);return c?.cpu?[c]:[]}return o.runs.filter((c)=>c.phase==="cpu"&&c.cpu)}function se(o){let N=o.nodes,c=N.length,I=Ze(o),m=new Int32Array(c).fill(-1);for(let U=0;U<c;U++)for(let D of N[U].children){let M=I(D);if(M!==-1)m[M]=U}let v=[];for(let U=0;U<c;U++)if(m[U]===-1)v.push(U);let C=[],P=[];for(let U=v.length-1;U>=0;U--)P.push(v[U]);while(P.length>0){let U=P.pop();C.push(U);for(let D of N[U].children){let M=I(D);if(M!==-1&&m[M]===U)P.push(M)}}return{count:c,indexOf:I,parentIx:m,roots:v,order:C}}function Ze(o){let N=o.nodes,c=N.length,I=1/0,m=-1/0,v=!0;for(let P=0;P<c;P++){let U=N[P].id;if(!Number.isInteger(U)){v=!1;break}if(U<I)I=U;if(U>m)m=U}if(v&&c>0&&m-I<c*4+64){let P=m-I+1,U=new Int32Array(P).fill(-1);for(let D=0;D<c;D++)U[N[D].id-I]=D;return(D)=>{let M=D-I;return M>=0&&M<P?U[M]:-1}}let C=new Map;for(let P=0;P<c;P++)C.set(N[P].id,P);return(P)=>C.get(P)??-1}function ke(o,N){let{count:c,indexOf:I,parentIx:m,order:v}=N,C=new Float64Array(c),P=new Float64Array(c),U=o.samples?.nodeIds??[],D=o.samples?.timeDeltasUs??[];for(let O=0;O<U.length;O++){let A=I(U[O]);if(A===-1)continue;C[A]+=D[O]??0,P[A]+=1}let M=new Float64Array(c);for(let O=v.length-1;O>=0;O--){let A=v[O];M[A]+=C[A];let z=m[A];if(z>=0)M[z]+=M[A]}return{selfUs:C,totalUs:M,samples:P}}var Te={name:"collapsed",async render(o,N={}){return{files:ee(o,N.runId).map((m)=>{let v=m.cpu,{nodes:C,frames:P}=v,U=se(v),D=Array(U.count);for(let K of U.order){let _=P[C[K].frameIx].name||"(anonymous)",J=U.parentIx[K];D[K]=J===-1?_:`${D[J]};${_}`}let M=new Float64Array(U.count),O=[],A=v.samples?.nodeIds??[];for(let K=0;K<A.length;K++){let _=U.indexOf(A[K]);if(_===-1)continue;if(M[_]++===0)O.push(_)}let z=Array(O.length);for(let K=0;K<O.length;K++){let _=O[K];z[K]=`${D[_]} ${M[_]}`}return{path:`${m.id}.collapsed.txt`,content:z.join(`
|
|
3
|
-
`)+(z.length>0?`
|
|
4
|
-
`:"")}})}}};var Ie={name:"cpuprofile",async render(o,N={}){let c=ee(o,N.runId),I=[],m=[];for(let v of c){if(v.cpu?.origin!=="cpu-prof"&&v.cpu?.origin!=="inspector"){m.push(`${v.id} (origin ${v.cpu?.origin??"unknown"} has no .cpuprofile artifact to pass through)`);continue}let C=v.artifacts.find((U)=>U.kind==="cpuprofile");if(!C){m.push(`${v.id} (no cpuprofile artifact recorded on this run)`);continue}let P=Bun.file(C.path);if(!await P.exists()){m.push(`${v.id} (artifact missing on disk: ${C.path})`);continue}I.push({path:`${v.id}.cpuprofile`,content:await P.text()})}if(I.length===0&&m.length>0)return{text:`No .cpuprofile artifacts available:
|
|
5
|
-
${m.map((v)=>` - ${v}`).join(`
|
|
6
|
-
`)}
|
|
7
|
-
`};return{files:I}}};var $e={name:"json",async render(o){return{text:k(o)}}};var ve={name:"jsonl",async render(o){let{runs:N,...c}=o;return{text:`${[h(c),...N.map((m)=>h(m))].join(`
|
|
8
|
-
`)}
|
|
9
|
-
`}}};function ne(o){return(o/1e6).toFixed(3)}function ce(o){return o?.label??o?.command?.join(" ")??o?.entry?.task??o?.id??"unknown"}var Ce=10,Ne=10,Ue={name:"markdown",async render(o){let N=new Map(o.workloads.map((m)=>[m.id,m])),c=[];c.push("# Profile Report",""),c.push(`Bun ${o.bunVersion} \xB7 ostia ${o.toolVersion} \xB7 ${o.platform.os}/${o.platform.arch} \xB7 ${o.createdAt}`,"");let I=o.runs.filter((m)=>m.phase==="timing"&&m.timing!==void 0);if(I.length>0){c.push("## Timing",""),c.push("| Command | Mean \xB1 SD (ms) | Min\u2026Max (ms) | Median (ms) |","|---|---|---|---|");for(let v of I){let C=ce(N.get(v.workloadId)),P=v.timing;c.push(`| ${C} | ${ne(P.mean)} \xB1 ${ne(P.stddev)} | ${ne(P.min)}\u2026${ne(P.max)} | ${ne(P.median)} |`)}c.push("");let m=I.filter((v)=>v.warnings.length>0);if(m.length>0){c.push("### Warnings","");for(let v of m){let C=ce(N.get(v.workloadId));for(let P of v.warnings)c.push(`- **${C}**: ${P.message} (\`${P.code}\`)`)}c.push("")}}for(let m of o.runs){if(m.phase!=="cpu"&&m.phase!=="heap")continue;let v=ce(N.get(m.workloadId));if(m.phase==="cpu"){if(c.push(`## CPU capture - ${v}`,""),c.push(`instrumented, diagnostic wall ${ne(m.diagnosticWallNs??0)}ms`,""),m.cpu){c.push(`origin: \`${m.cpu.origin}\`, interval: ${m.cpu.samplingIntervalUs}\xB5s`,""),c.push("| Self % | Self (ms) | Total (ms) | Frame |","|---|---|---|---|");let C=m.cpu.totals.reduce((P,U)=>P+U.selfUs,0)||1;for(let P of m.cpu.totals.slice(0,Ce)){let U=m.cpu.frames[P.frameIx],D=(P.selfUs/C*100).toFixed(1);c.push(`| ${D}% | ${(P.selfUs/1000).toFixed(2)} | ${(P.totalUs/1000).toFixed(2)} | ${U?.name||"(anonymous)"} |`)}if(c.push(""),m.jit){let P=m.jit.tiers;c.push(`JIT tiers: LLInt ${P.llint} \xB7 Baseline ${P.baseline} \xB7 DFG ${P.dfg} \xB7 FTL ${P.ftl}`,"")}}}else if(c.push(`## Heap snapshot - ${v}`,""),c.push(`instrumented, diagnostic wall ${ne(m.diagnosticWallNs??0)}ms`,""),m.heap){c.push(`${m.heap.objectCount??"?"} objects, ${((m.heap.heapSizeBytes??0)/1e6).toFixed(2)}MB`,""),c.push("| Count | Type |","|---|---|");for(let C of m.heap.typeCounts.slice(0,Ne))c.push(`| ${C.count} | ${C.type} |`);c.push("")}for(let C of m.artifacts)c.push(`- artifact: \`${C.path}\``);for(let C of m.warnings)c.push(`- ! ${C.message} (\`${C.code}\`)`);if(m.artifacts.length>0||m.warnings.length>0)c.push("")}if(o.comparisons&&o.comparisons.length>0){c.push("## Comparisons","");for(let m of o.comparisons){let v=o.runs.find((P)=>P.id===m.candidateRunId),C=ce(v?N.get(v.workloadId):void 0);if(c.push(`### ${m.verdict==="pass"?"\u2713":"\u2717"} ${C}`,""),m.timing){let P=m.timing.medianDeltaPct>0?"+":"";c.push(`- timing: ${P}${m.timing.medianDeltaPct.toFixed(1)}% median (**${m.timing.verdict}**)`)}for(let P of m.frames?.slice(0,Ce)??[]){if(Math.abs(P.deltaPct)<0.5)continue;let U=P.deltaPct>0?"+":"";c.push(`- frame \`${P.name}\`: ${U}${P.deltaPct.toFixed(1)}% self-time (${(P.baseSelfUs/1000).toFixed(2)}ms \u2192 ${(P.candSelfUs/1000).toFixed(2)}ms)`)}for(let P of m.heapTypes?.slice(0,Ne)??[]){if(Math.abs(P.deltaPct)<0.5)continue;let U=P.deltaPct>0?"+":"";c.push(`- heap \`${P.type}\`: ${U}${P.deltaPct.toFixed(1)}% count (${P.baseCount} \u2192 ${P.candCount})`)}c.push("")}}return{text:c.join(`
|
|
10
|
-
`)}}};var en=15;function pe(o){return`n${o}`}function nn(o,N,c){return`${(o||"(anonymous)").replace(/"/g,"'")} (self ${(N/1000).toFixed(2)}ms, total ${(c/1000).toFixed(2)}ms)`}function tn(o,N,c,I){let m=[];if(I<=0)return m;for(let v=0;v<N;v++){if(v===c)continue;let C=o[v];if(m.length===I&&C<=o[m[I-1]])continue;let P=m.length;while(P>0&&o[m[P-1]]<C)P--;if(m.splice(P,0,v),m.length>I)m.pop()}return m}var De={name:"mermaid",async render(o,N={}){let c=N.topN??en;return{files:ee(o,N.runId).map((v)=>{let C=v.cpu,{nodes:P,frames:U}=C,D=se(C),{selfUs:M,totalUs:O}=ke(C,D),{parentIx:A}=D,z=D.roots[0]??-1,K=tn(M,D.count,z,c),_=new Set(z!==-1?[z]:[]),J=[];for(let L of K){J.length=0;for(let j=L;j!==-1;j=A[j])J.push(j);for(let j=J.length-1;j>=0;j--)_.add(J[j])}let V=["graph TD"];for(let L of _){let j=P[L].id;V.push(` ${pe(j)}["${nn(U[P[L].frameIx].name,M[L],O[L])}"]`)}for(let L of _){let j=A[L];if(j!==-1&&_.has(j))V.push(` ${pe(P[j].id)} --> ${pe(P[L].id)}`)}return{path:`${v.id}.mermaid.md`,content:`${V.join(`
|
|
11
|
-
`)}
|
|
12
|
-
`}})}}};function Fe(o){if(!o?.entry)return;if(o.entry.group!==void 0)return o.entry.group;let N=o.entry.task,c=N.lastIndexOf("/");return c===-1?void 0:N.slice(0,c)}function ue(o){let N=Math.min(...o.map((m)=>m.run.timing.median)),c=new Map;for(let m of o){let v=Fe(m.workload);if(v===void 0)continue;let C=c.get(v);if(C)C.push(m);else c.set(v,[m])}let I=new Map;for(let m of o){let v=Fe(m.workload);if(v===void 0){I.set(m,N);continue}let C=c.get(v)??[m],P=C.find((U)=>U.workload?.baseline);I.set(m,P?P.run.timing.median:Math.min(...C.map((U)=>U.run.timing.median)))}return I}function X(o){return Number.isFinite(o)?Number(o.toPrecision(6)):o}function rn(o,N){return o?.entry?.task??o?.label??o?.command?.join(" ")??N.workloadId}function sn(o){let N=new Map(o.workloads.map((v)=>[v.id,v])),c=o.runs.filter((v)=>v.phase==="timing"&&v.timing!==void 0).map((v)=>({run:v,workload:N.get(v.workloadId)})),I=c.length>1?ue(c):void 0,m=new Map((o.comparisons??[]).map((v)=>[v.candidateRunId,v]));return c.map((v)=>{let{run:C,workload:P}=v,U=C.timing,D={task:rn(P,C),unit:"ns",samples:U.samples.length,mean:X(U.mean),median:X(U.median),stddev:X(U.stddev),stddevPct:X(U.mean===0?0:U.stddev/U.mean*100),min:X(U.min),max:X(U.max),warnings:C.warnings.map((O)=>O.data?{code:O.code,data:O.data}:{code:O.code})};if(P?.entry?.group!==void 0)D.group=P.entry.group;if(P?.description!==void 0)D.description=P.description;if(P?.groupDescription!==void 0)D.groupDescription=P.groupDescription;if(I)D.relative=X(U.median/(I.get(v)??U.median));if(P?.baseline)D.baseline=!0;let M=m.get(C.id);if(M?.timing)D.delta={medianPct:X(M.timing.medianDeltaPct),meanPct:X(M.timing.meanDeltaPct),verdict:M.timing.verdict,pass:M.verdict==="pass"};return D})}var Me={name:"minimal",async render(o){let N=sn(o).map((c)=>JSON.stringify(c));return{text:N.length>0?`${N.join(`
|
|
13
|
-
`)}
|
|
14
|
-
`:""}}};var on="https://www.speedscope.app/file-format-schema.json";function Se(o){return o?.label??o?.command?.join(" ")??o?.entry?.task??"profile"}var Be={name:"speedscope",async render(o,N={}){let c=ee(o,N.runId),I=new Map(o.workloads.map((v)=>[v.id,v]));return{files:c.map((v)=>{let C=v.cpu,{nodes:P}=C,U=se(C),D=C.samples?.nodeIds??[],M=C.samples?.timeDeltasUs??[],O=Array(U.count);for(let _ of U.order){let J=U.parentIx[_],V=P[_].frameIx;O[_]=J===-1?[V]:[...O[J],V]}let A=Array(D.length);for(let _=0;_<D.length;_++){let J=U.indexOf(D[_]);A[_]=J===-1?[]:O[J]}let z=0;for(let _=0;_<M.length;_++)z+=M[_];let K={$schema:on,exporter:"ostia",name:Se(I.get(v.workloadId)),activeProfileIndex:0,shared:{frames:C.frames.map((_)=>({name:_.name||"(anonymous)",file:_.url,line:_.line!==void 0?_.line+1:void 0}))},profiles:[{type:"sampled",name:Se(I.get(v.workloadId)),unit:"microseconds",startValue:0,endValue:z,samples:A,weights:M}]};return{path:`${v.id}.speedscope.json`,content:`${JSON.stringify(K,null,2)}
|
|
15
|
-
`}})}}};function oe(o){return(o/1e6).toFixed(3)}function me(o){return o.label??o.command?.join(" ")??o.entry?.task??o.id}var We={name:"table",async render(o){let N=o.runs.filter((O)=>O.phase==="timing"&&O.timing!==void 0),c=new Map(o.workloads.map((O)=>[O.id,O]));if(N.length===0){let O=Oe(o,c);return{text:O.length>0?`${O.join(`
|
|
16
|
-
`)}
|
|
17
|
-
`:`(no timing runs)
|
|
18
|
-
`}}let I=N.map((O)=>{let A=c.get(O.workloadId);return{run:O,workload:A,label:A?me(A):O.workloadId}}),m=I.length>1,v=ue(I),C=[],P=Math.max(7,...I.map((O)=>O.label.length)),U=m?`${"Command".padEnd(P)} Mean [ms] Min\u2026Max [ms] Relative`:`${"Command".padEnd(P)} Mean [ms] Min\u2026Max [ms]`;C.push(U),C.push("-".repeat(U.length));for(let O of I){let{run:A,label:z,workload:K}=O,_=A.timing,J=`${oe(_.mean)} \xB1 ${oe(_.stddev)}`,V=`${oe(_.min)}\u2026${oe(_.max)}`,L=`${z.padEnd(P)} ${J.padEnd(15)} ${V.padEnd(18)}`;if(m){let j=_.median/(v.get(O)??_.median);if(j===1)L+=K?.baseline?" 1.00\xD7 (baseline)":" 1.00\xD7";else if(j>1)L+=` ${j.toFixed(2)}\xD7 slower`;else L+=` ${(1/j).toFixed(2)}\xD7 faster`}C.push(L);for(let j of A.warnings)C.push(` ! ${j.message}`)}let D=cn(o,c);if(D.length>0)C.push(""),C.push(...D);let M=Oe(o,c);if(M.length>0)C.push(""),C.push(...M);return{text:`${C.join(`
|
|
19
|
-
`)}
|
|
20
|
-
`}}};function an(o,N,c){let I=o.runs.find((v)=>v.id===c),m=I?N.get(I.workloadId):void 0;return m?me(m):c}function Oe(o,N){if(!o.comparisons||o.comparisons.length===0)return[];let c=[];for(let I of o.comparisons){let m=an(o,N,I.candidateRunId),v=I.verdict==="pass"?"\u2713":"\u2717";if(c.push(`${v} ${m}`),I.timing){let C=I.timing.medianDeltaPct>0?"+":"";c.push(` timing: ${C}${I.timing.medianDeltaPct.toFixed(1)}% median (${I.timing.verdict})`)}if(I.frames)for(let C of I.frames.slice(0,Ee)){if(Math.abs(C.deltaPct)<0.5)continue;let P=C.deltaPct>0?"+":"";c.push(` frame ${C.name}: ${P}${C.deltaPct.toFixed(1)}% self-time (${(C.baseSelfUs/1000).toFixed(2)}ms -> ${(C.candSelfUs/1000).toFixed(2)}ms)`)}if(I.heapTypes)for(let C of I.heapTypes.slice(0,Ae)){if(Math.abs(C.deltaPct)<0.5)continue;let P=C.deltaPct>0?"+":"";c.push(` heap ${C.type}: ${P}${C.deltaPct.toFixed(1)}% count (${C.baseCount} -> ${C.candCount})`)}}return c}var Ee=5,Ae=5;function cn(o,N){let c=[];for(let I of o.runs){if(I.phase!=="cpu"&&I.phase!=="heap")continue;let m=N.get(I.workloadId),v=m?me(m):I.workloadId;if(I.phase==="cpu")if(I.cpu){c.push(`CPU capture - ${v} (instrumented, ${I.cpu.samplingIntervalUs}\xB5s interval, diagnostic wall ${oe(I.diagnosticWallNs??0)}ms)`);let C=I.cpu.totals.reduce((P,U)=>P+U.selfUs,0)||1;for(let P of I.cpu.totals.slice(0,Ee)){let U=I.cpu.frames[P.frameIx],D=(P.selfUs/C*100).toFixed(1);c.push(` ${D.padStart(5)}% ${(P.selfUs/1000).toFixed(2).padStart(8)}ms self ${U?.name??"?"}`)}}else c.push(`CPU capture - ${v} (instrumented, no evidence captured)`);else if(I.heap){let C=((I.heap.heapSizeBytes??0)/1e6).toFixed(2);c.push(`Heap snapshot - ${v} (instrumented, ${I.heap.objectCount??"?"} objects, ${C}MB)`);for(let P of I.heap.typeCounts.slice(0,Ae))c.push(` ${String(P.count).padStart(6)} ${P.type}`)}else c.push(`Heap snapshot - ${v} (instrumented, no evidence captured)`);for(let C of I.artifacts)c.push(` artifact: ${C.path}`);for(let C of I.warnings)c.push(` ! ${C.message}`)}return c}var n={table:We,json:$e,markdown:Ue,jsonl:ve,minimal:Me,collapsed:Te,mermaid:De,speedscope:Be,cpuprofile:Ie};var un="node_modules/.cache/ostia",de=1000;async function y(o){let N=s({runs:o.runs??null,warmup:o.warmup??null,cpu:o.cpu??!1,heap:o.heap??!1,cpuIntervalUs:o.cpuIntervalUs??de}),I=`${o.outDir??un}/artifacts`,m=[],v=[];for(let C of o.commands){let P=Array.isArray(C)?C:Re(C),U=u(P,Array.isArray(C)?void 0:C);m.push(U);let D=await g({argv:P,cwd:o.cwd,env:o.env,runs:o.runs,warmup:o.warmup}),M=i({workload:U,configFingerprint:N,trials:D.trials,timing:D.timing,warnings:D.warnings});if(v.push(M),o.cpu){let O=`${M.id}-cpu.cpuprofile`,A=await fe({argv:P,cwd:o.cwd,env:o.env,artifactDir:I,fileName:O,intervalUs:o.cpuIntervalUs??de});v.push(await je({workload:U,phase:"cpu",configFingerprint:N,diagnosticWallNs:A.diagnosticWallNs,exitCode:A.exitCode,cpu:A.cpu,artifactPath:A.artifactPath,artifactKind:"cpuprofile",warnings:A.warnings}))}if(o.heap){let O=`${M.id}-heap.heapsnapshot`,A=await he({argv:P,cwd:o.cwd,env:o.env,artifactDir:I,fileName:O});v.push(await je({workload:U,phase:"heap",configFingerprint:N,diagnosticWallNs:A.diagnosticWallNs,exitCode:A.exitCode,heap:A.heap,artifactPath:A.artifactPath,artifactKind:"heapsnapshot",warnings:A.warnings}))}}return r(m,v)}async function je(o){let N=`${o.workload.id}-${o.phase}-${o.configFingerprint}`,c=o.artifactPath?[await T(N,o.artifactKind,o.artifactPath)]:[];return b({workload:o.workload,phase:o.phase,configFingerprint:o.configFingerprint,diagnosticWallNs:o.diagnosticWallNs,exitCode:o.exitCode,cpu:o.cpu,heap:o.heap,warnings:o.warnings,artifacts:c})}async function S(o,N={}){let c=R(o),I=s({intervalUs:N.intervalUs??de,origin:N.origin??"inspector"}),m=(D)=>D.samples?.nodeIds.length===0?[{code:"empty-profile",message:"In-process capture produced zero samples."}]:[];if(N.origin==="jsc"){let{result:D,cpu:M,jit:O,diagnosticWallNs:A}=await xe(o,N),z=b({workload:c,phase:"cpu",configFingerprint:I,diagnosticWallNs:A,cpu:M,jit:O,warnings:m(M),artifacts:[]});return{result:D,run:z}}let{result:v,cpu:C,diagnosticWallNs:P}=await be(o,N),U=b({workload:c,phase:"cpu",configFingerprint:I,diagnosticWallNs:P,cpu:C,warnings:m(C),artifacts:[]});return{result:v,run:U}}
|
|
21
|
-
export{x,d,a,f,w,g,F,n,y,S};
|