@ac-bench/measure-duration 0.1.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.
Files changed (79) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +34 -0
  3. package/dist/_harness-overhead.d.ts +36 -0
  4. package/dist/_harness-overhead.d.ts.map +1 -0
  5. package/dist/_harness-overhead.js +62 -0
  6. package/dist/_harness-overhead.js.map +1 -0
  7. package/dist/_iteration-protocol.d.ts +18 -0
  8. package/dist/_iteration-protocol.d.ts.map +1 -0
  9. package/dist/_iteration-protocol.js +17 -0
  10. package/dist/_iteration-protocol.js.map +1 -0
  11. package/dist/_iteration-source.d.ts +64 -0
  12. package/dist/_iteration-source.d.ts.map +1 -0
  13. package/dist/_iteration-source.js +135 -0
  14. package/dist/_iteration-source.js.map +1 -0
  15. package/dist/_pool.d.ts +48 -0
  16. package/dist/_pool.d.ts.map +1 -0
  17. package/dist/_pool.js +219 -0
  18. package/dist/_pool.js.map +1 -0
  19. package/dist/_process-execution.d.ts +62 -0
  20. package/dist/_process-execution.d.ts.map +1 -0
  21. package/dist/_process-execution.js +142 -0
  22. package/dist/_process-execution.js.map +1 -0
  23. package/dist/_run-case.d.ts +32 -0
  24. package/dist/_run-case.d.ts.map +1 -0
  25. package/dist/_run-case.js +267 -0
  26. package/dist/_run-case.js.map +1 -0
  27. package/dist/_sampling-options.d.ts +61 -0
  28. package/dist/_sampling-options.d.ts.map +1 -0
  29. package/dist/_sampling-options.js +19 -0
  30. package/dist/_sampling-options.js.map +1 -0
  31. package/dist/_sampling-report.d.ts +44 -0
  32. package/dist/_sampling-report.d.ts.map +1 -0
  33. package/dist/_sampling-report.js +43 -0
  34. package/dist/_sampling-report.js.map +1 -0
  35. package/dist/_spawn-baseline.d.ts +35 -0
  36. package/dist/_spawn-baseline.d.ts.map +1 -0
  37. package/dist/_spawn-baseline.js +22 -0
  38. package/dist/_spawn-baseline.js.map +1 -0
  39. package/dist/_statistics-schema.d.ts +28 -0
  40. package/dist/_statistics-schema.d.ts.map +1 -0
  41. package/dist/_statistics-schema.js +59 -0
  42. package/dist/_statistics-schema.js.map +1 -0
  43. package/dist/_statistics.d.ts +25 -0
  44. package/dist/_statistics.d.ts.map +1 -0
  45. package/dist/_statistics.js +72 -0
  46. package/dist/_statistics.js.map +1 -0
  47. package/dist/_types.d.ts +302 -0
  48. package/dist/_types.d.ts.map +1 -0
  49. package/dist/_types.js +57 -0
  50. package/dist/_types.js.map +1 -0
  51. package/dist/_warnings.d.ts +62 -0
  52. package/dist/_warnings.d.ts.map +1 -0
  53. package/dist/_warnings.js +132 -0
  54. package/dist/_warnings.js.map +1 -0
  55. package/dist/decl.d.ts +21 -0
  56. package/dist/decl.d.ts.map +1 -0
  57. package/dist/decl.js +54 -0
  58. package/dist/decl.js.map +1 -0
  59. package/dist/index.d.ts +5 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +5 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/iteration-child.d.ts +2 -0
  64. package/dist/iteration-child.d.ts.map +1 -0
  65. package/dist/iteration-child.js +37 -0
  66. package/dist/iteration-child.js.map +1 -0
  67. package/dist/options.d.ts +59 -0
  68. package/dist/options.d.ts.map +1 -0
  69. package/dist/options.js +2 -0
  70. package/dist/options.js.map +1 -0
  71. package/dist/plugin.d.ts +3 -0
  72. package/dist/plugin.d.ts.map +1 -0
  73. package/dist/plugin.js +205 -0
  74. package/dist/plugin.js.map +1 -0
  75. package/dist/spawn-overhead.d.ts +59 -0
  76. package/dist/spawn-overhead.d.ts.map +1 -0
  77. package/dist/spawn-overhead.js +80 -0
  78. package/dist/spawn-overhead.js.map +1 -0
  79. package/package.json +77 -0
package/dist/_pool.js ADDED
@@ -0,0 +1,219 @@
1
+ import { poolRandomEffects, sumBy, } from "@ac-kit/math-stats";
2
+ import * as z from "zod/mini";
3
+ import { summarise } from "./_statistics.js";
4
+ /**
5
+ * Normal quantile for a two-sided 95% interval, matching what `summarise`
6
+ * reports.
7
+ */
8
+ const Z_95_ = 1.959964;
9
+ export const pooledEstimateSchema = z.object({
10
+ /** The answer: every execution's estimate, weighted by its precision. */
11
+ estimateMs: z.number(),
12
+ lowerMs: z.number(),
13
+ upperMs: z.number(),
14
+ /** Between-process variance, τ². The process lottery, quantified. */
15
+ betweenVarianceMs2: z.number(),
16
+ /** Share of total variance that is between processes rather than within. */
17
+ heterogeneity: z.number(),
18
+ /** How many executions were combined. */
19
+ executions: z.number(),
20
+ /** Which of the reported statistics was pooled. */
21
+ estimator: z.enum(["mean", "median"]),
22
+ });
23
+ export const contaminationSchema = z.object({
24
+ /** Shared-arm estimate minus isolated-arm estimate, in milliseconds. */
25
+ differenceMs: z.number(),
26
+ /** As a fraction of the isolated estimate. */
27
+ share: z.number(),
28
+ /** Executions behind each side. */
29
+ sharedExecutions: z.number(),
30
+ isolatedExecutions: z.number(),
31
+ });
32
+ /**
33
+ * Combines every execution of one case into the result that answers for it.
34
+ *
35
+ * Executions come from different processes, and a process contributes an offset
36
+ * of its own — GC seed, address-space layout, which JIT tier the code settled
37
+ * in, the thermal state at that moment. Averaging the estimates would report an
38
+ * interval far too narrow, because it would treat that offset as if it did not
39
+ * exist. Random-effects pooling estimates it instead and widens the interval by
40
+ * it.
41
+ *
42
+ * The pooled interval is symmetric by construction, which is inverse-variance
43
+ * pooling's assumption rather than a property of the samples: a skewed median
44
+ * interval is symmetrised on the way in.
45
+ *
46
+ * @param executions At least one, in no particular order.
47
+ * @param estimator Which reported statistic to pool.
48
+ * @returns The pooled result, or the sole execution's own result when there is
49
+ * only one.
50
+ */
51
+ export function poolDurationExecutions(executions, estimator = "median") {
52
+ const measured = executions.filter((execution) => execution.result.statistics !== null);
53
+ // Every execution failed, or there was only ever one: nothing to combine, and
54
+ // inventing a pooled number over a single group would only look authoritative.
55
+ if (measured.length === 0) {
56
+ return executions[0].result;
57
+ }
58
+ if (measured.length === 1) {
59
+ return measured[0].result;
60
+ }
61
+ const groups = measured.map((execution) => ({
62
+ estimate: estimate_(execution.result, estimator),
63
+ variance: variance_(execution.result, estimator),
64
+ }));
65
+ // A clock too coarse to separate the samples leaves nothing to weight by.
66
+ if (groups.some((group) => group.variance <= 0)) {
67
+ return mergeSamples_(measured, estimator, null, null);
68
+ }
69
+ const pooled = poolRandomEffects(groups);
70
+ return mergeSamples_(measured, estimator, {
71
+ estimateMs: pooled.estimate,
72
+ lowerMs: pooled.interval.lower,
73
+ upperMs: pooled.interval.upper,
74
+ betweenVarianceMs2: pooled.betweenVariance,
75
+ heterogeneity: pooled.heterogeneity,
76
+ executions: measured.length,
77
+ estimator,
78
+ }, contamination_(measured, estimator));
79
+ }
80
+ /**
81
+ * Builds the pooled result on the concatenated samples.
82
+ *
83
+ * The reported `statistics` describe every sample from every execution, so the
84
+ * spread they show includes the between-process spread rather than one lucky
85
+ * process's view of it. `estimate` carries the properly weighted answer.
86
+ */
87
+ function mergeSamples_(executions, estimator, estimate, contamination) {
88
+ const first = executions[0].result;
89
+ const timings = executions.flatMap((execution) => execution.result.timings);
90
+ return {
91
+ ...first,
92
+ statistics: timings.length > 0 ? summarise(timings) : first.statistics,
93
+ timings,
94
+ sampling: mergeSampling_(executions, estimate),
95
+ estimate,
96
+ contamination,
97
+ warnings: dedupeWarnings_(executions),
98
+ };
99
+ }
100
+ /**
101
+ * One report describing every execution.
102
+ *
103
+ * Carrying the first execution's report unchanged would state one process's
104
+ * sample count and precision next to statistics covering all of them.
105
+ */
106
+ function mergeSampling_(executions, estimate) {
107
+ const reports = executions
108
+ .map((execution) => execution.result.sampling)
109
+ .filter((report) => report !== null);
110
+ const first = reports[0];
111
+ if (first === undefined) {
112
+ return null;
113
+ }
114
+ const stopReasons = new Set(reports.map((report) => report.stopReason));
115
+ const achieved = estimate === null ? null : pooledRelativeError_(estimate);
116
+ return {
117
+ ...first,
118
+ // A claim about precision is the weakest link: one precise replicate must
119
+ // not cover for four sloppy ones.
120
+ targetReached: reports.every((report) => report.targetReached),
121
+ achievedRelativeError: achieved ?? first.achievedRelativeError,
122
+ stopReason: stopReasons.size === 1 ? first.stopReason : "mixed",
123
+ measuredMs: sumBy(reports, (report) => report.measuredMs),
124
+ activeMs: sumBy(reports, (report) => report.activeMs),
125
+ elapsedMs: sumBy(reports, (report) => report.elapsedMs),
126
+ samples: sumBy(reports, (report) => report.samples),
127
+ batches: sumBy(reports, (report) => report.batches),
128
+ };
129
+ }
130
+ /** Half-width of the pooled interval, relative to the pooled estimate. */
131
+ function pooledRelativeError_(estimate) {
132
+ if (estimate.estimateMs <= 0) {
133
+ return null;
134
+ }
135
+ return (estimate.upperMs - estimate.lowerMs) / 2 / estimate.estimateMs;
136
+ }
137
+ /**
138
+ * One warning per kind, with how many executions raised it.
139
+ *
140
+ * Keying on the message would keep all of them: each quotes its own numbers, so
141
+ * eight executions produce eight distinct texts saying the same thing. The
142
+ * retained message is one execution's, and the count is what says whether the
143
+ * finding was systematic or a one-off.
144
+ */
145
+ function dedupeWarnings_(executions) {
146
+ const byKind = new Map();
147
+ for (const execution of executions) {
148
+ for (const warning of execution.result.warnings) {
149
+ const existing = byKind.get(warning.kind);
150
+ if (existing === undefined) {
151
+ byKind.set(warning.kind, { warning, seen: 1 });
152
+ continue;
153
+ }
154
+ existing.seen += 1;
155
+ }
156
+ }
157
+ const total = executions.length;
158
+ return [...byKind.values()].map(({ warning, seen }) => seen === 1 && total === 1
159
+ ? warning
160
+ : {
161
+ ...warning,
162
+ message: `${warning.message} (in ${seen} of ${total} executions)`,
163
+ });
164
+ }
165
+ /**
166
+ * How much slower a case runs alongside its siblings than alone.
167
+ *
168
+ * `null` unless both arms ran: with one arm there is nothing to compare, and a
169
+ * difference of an estimate against itself is zero by construction rather than
170
+ * by measurement.
171
+ */
172
+ function contamination_(executions, estimator) {
173
+ const shared = executions.filter((execution) => execution.arm === "shared");
174
+ const isolated = executions.filter((execution) => execution.arm === "isolated");
175
+ if (shared.length === 0 || isolated.length === 0) {
176
+ return null;
177
+ }
178
+ const sharedMs = meanEstimate_(shared, estimator);
179
+ const isolatedMs = meanEstimate_(isolated, estimator);
180
+ return {
181
+ differenceMs: sharedMs - isolatedMs,
182
+ share: isolatedMs > 0 ? (sharedMs - isolatedMs) / isolatedMs : 0,
183
+ sharedExecutions: shared.length,
184
+ isolatedExecutions: isolated.length,
185
+ };
186
+ }
187
+ function meanEstimate_(executions, estimator) {
188
+ const total = executions.reduce((sum, execution) => sum + estimate_(execution.result, estimator), 0);
189
+ return total / executions.length;
190
+ }
191
+ function estimate_(result, estimator) {
192
+ const statistics = result.statistics;
193
+ return estimator === "mean" ? statistics.meanMs : statistics.medianMs;
194
+ }
195
+ /**
196
+ * Variance of the estimate, from the interval the sampler already reports.
197
+ *
198
+ * `halfWidth = z · σ`, so `σ² = (halfWidth / z)²` — exact for the mean's normal
199
+ * approximation, and the closest honest reading of the median's
200
+ * distribution-free interval.
201
+ */
202
+ function variance_(result, estimator) {
203
+ const statistics = result.statistics;
204
+ const halfWidth = estimator === "mean"
205
+ ? statistics.confidence95Ms
206
+ : statistics.medianConfidence95Ms;
207
+ return (halfWidth / Z_95_) ** 2;
208
+ }
209
+ /** An interval a reader can quote, from the pooled estimate. */
210
+ export function pooledInterval(estimate) {
211
+ return {
212
+ estimate: estimate.estimateMs,
213
+ lower: estimate.lowerMs,
214
+ upper: estimate.upperMs,
215
+ halfWidth: (estimate.upperMs - estimate.lowerMs) / 2,
216
+ level: 0.95,
217
+ };
218
+ }
219
+ //# sourceMappingURL=_pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_pool.js","sourceRoot":"","sources":["../src/_pool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import * as z from "zod/mini";
2
+ /**
3
+ * Wall-clock breakdown of one cold start, reported by the spawned process.
4
+ *
5
+ * A total plus an exit code is enough to compare two cold starts but not to say
6
+ * where the time went, which is the only actionable part of a slow one.
7
+ */
8
+ export declare const coldStartPhasesSchema: z.ZodMiniObject<{
9
+ startupMs: z.ZodMiniNumber<number>;
10
+ importMs: z.ZodMiniNumber<number>;
11
+ runMs: z.ZodMiniNumber<number>;
12
+ }, z.core.$strip>;
13
+ export type ColdStartPhases = z.infer<typeof coldStartPhasesSchema>;
14
+ export type IterationOutcome = {
15
+ readonly wallMs: number;
16
+ readonly exitCode: number | null;
17
+ readonly terminatedBy: string | null;
18
+ /** Last {@link STDERR_TAIL_BYTES_} bytes of stderr, empty when it wrote none. */
19
+ readonly stderrTail: string;
20
+ /** `null` unless phases were requested and the process reported them. */
21
+ readonly phases: ColdStartPhases | null;
22
+ };
23
+ export type SpawnIterationOptions = {
24
+ readonly cwd?: string;
25
+ readonly env?: Readonly<Record<string, string>>;
26
+ /** Reads one JSON line of {@link ColdStartPhases} from stdout. */
27
+ readonly capturePhases?: boolean;
28
+ };
29
+ /**
30
+ * Owns every process spawned for a case, and guarantees none outlives it.
31
+ *
32
+ * Killing a process does not kill its children on POSIX: a hung grandchild is
33
+ * reparented to init and survives the whole run, holding a port or a lock, with
34
+ * nothing left pointing at it. Each iteration is therefore started `detached`,
35
+ * which makes `terminate`/`kill` address its whole process group.
36
+ */
37
+ export declare class ProcessGroup {
38
+ private readonly live;
39
+ private readonly onProcessExit;
40
+ private disposed;
41
+ constructor();
42
+ /**
43
+ * Spawns one iteration and resolves when it has exited.
44
+ *
45
+ * @param command Executable, resolved on `PATH`. Never passed through a
46
+ * shell.
47
+ * @param args Arguments, passed as a vector rather than a command line.
48
+ * @param options Working directory, environment and phase capture.
49
+ * @param signal Aborting tears the whole group down.
50
+ * @returns What the process cost and how it ended.
51
+ * @throws {Error} If the executable cannot be started at all.
52
+ */
53
+ run(command: string, args: readonly string[], options: SpawnIterationOptions, signal: AbortSignal): Promise<IterationOutcome>;
54
+ /**
55
+ * Terminates every process still running, escalating only if it has to.
56
+ *
57
+ * @returns Once the group is gone, or once it has been killed outright for
58
+ * failing to leave within the grace period.
59
+ */
60
+ dispose(): Promise<void>;
61
+ }
62
+ //# sourceMappingURL=_process-execution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_process-execution.d.ts","sourceRoot":"","sources":["../src/_process-execution.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC;AAQ9B;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;;;;iBAUhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,MAAM,MAAM,gBAAgB,GAAG;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,iFAAiF;IACjF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IACnC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,kEAAkE;IAClE,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF;;;;;;;GAOG;AACH,qBAAa,YAAY;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6B;IAClD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAI5B;IAEF,OAAO,CAAC,QAAQ,CAAS;IAEzB,cAIC;IAED;;;;;;;;;;OAUG;IACG,GAAG,CACR,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE,WAAW,GACjB,OAAO,CAAC,gBAAgB,CAAC,CAuC3B;IAED;;;;;OAKG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAW7B;CACD"}
@@ -0,0 +1,142 @@
1
+ import { startProcess } from "@ac-kit/node";
2
+ import * as z from "zod/mini";
3
+ /** Grace given to a `SIGTERM` before the group is killed outright. */
4
+ const TERMINATE_GRACE_MS_ = 2000;
5
+ /** Bytes of stderr retained per iteration for failure diagnostics. */
6
+ const STDERR_TAIL_BYTES_ = 4096;
7
+ /**
8
+ * Wall-clock breakdown of one cold start, reported by the spawned process.
9
+ *
10
+ * A total plus an exit code is enough to compare two cold starts but not to say
11
+ * where the time went, which is the only actionable part of a slow one.
12
+ */
13
+ export const coldStartPhasesSchema = z.object({
14
+ /**
15
+ * Interpreter start-up, before any user code: `bootstrapComplete -
16
+ * nodeStart`.
17
+ */
18
+ startupMs: z.number(),
19
+ /** Importing the bench file and everything it pulls in. */
20
+ importMs: z.number(),
21
+ /** The case body's single call. */
22
+ runMs: z.number(),
23
+ });
24
+ /**
25
+ * Owns every process spawned for a case, and guarantees none outlives it.
26
+ *
27
+ * Killing a process does not kill its children on POSIX: a hung grandchild is
28
+ * reparented to init and survives the whole run, holding a port or a lock, with
29
+ * nothing left pointing at it. Each iteration is therefore started `detached`,
30
+ * which makes `terminate`/`kill` address its whole process group.
31
+ */
32
+ export class ProcessGroup {
33
+ live = new Set();
34
+ onProcessExit = () => {
35
+ for (const child of this.live) {
36
+ child.kill("SIGKILL");
37
+ }
38
+ };
39
+ disposed = false;
40
+ constructor() {
41
+ // Last resort: the owning process is going away and async teardown will
42
+ // never get a turn.
43
+ process.on("exit", this.onProcessExit);
44
+ }
45
+ /**
46
+ * Spawns one iteration and resolves when it has exited.
47
+ *
48
+ * @param command Executable, resolved on `PATH`. Never passed through a
49
+ * shell.
50
+ * @param args Arguments, passed as a vector rather than a command line.
51
+ * @param options Working directory, environment and phase capture.
52
+ * @param signal Aborting tears the whole group down.
53
+ * @returns What the process cost and how it ended.
54
+ * @throws {Error} If the executable cannot be started at all.
55
+ */
56
+ async run(command, args, options, signal) {
57
+ signal.throwIfAborted();
58
+ const child = await startProcess(command, args, {
59
+ // Group leader, so teardown reaches whatever the iteration spawned.
60
+ detached: process.platform !== "win32",
61
+ stdout: options.capturePhases === true ? "pipe" : "ignore",
62
+ stderr: "pipe",
63
+ cwd: options.cwd,
64
+ env: options.env === undefined ? undefined : { ...options.env },
65
+ graceMs: TERMINATE_GRACE_MS_,
66
+ });
67
+ this.live.add(child);
68
+ const stderr = collectTail_(child.stderr);
69
+ const stdout = options.capturePhases === true ? collectTail_(child.stdout) : null;
70
+ // Straight to `SIGKILL`: an aborted run is not waiting out a grace period.
71
+ const onAbort = () => {
72
+ child.kill("SIGKILL");
73
+ };
74
+ signal.addEventListener("abort", onAbort, { once: true });
75
+ try {
76
+ const exit = await child.waitForExit();
77
+ return {
78
+ wallMs: exit.durationMs,
79
+ exitCode: exit.code,
80
+ terminatedBy: exit.signal,
81
+ stderrTail: await stderr,
82
+ phases: stdout === null ? null : parsePhases_(await stdout),
83
+ };
84
+ }
85
+ finally {
86
+ signal.removeEventListener("abort", onAbort);
87
+ this.live.delete(child);
88
+ }
89
+ }
90
+ /**
91
+ * Terminates every process still running, escalating only if it has to.
92
+ *
93
+ * @returns Once the group is gone, or once it has been killed outright for
94
+ * failing to leave within the grace period.
95
+ */
96
+ async dispose() {
97
+ if (this.disposed) {
98
+ return;
99
+ }
100
+ this.disposed = true;
101
+ process.off("exit", this.onProcessExit);
102
+ await Promise.all([...this.live].map((child) => child.terminate(TERMINATE_GRACE_MS_)));
103
+ }
104
+ }
105
+ /**
106
+ * Drains a stream to its end, keeping only the tail.
107
+ *
108
+ * Draining is not optional: an unread pipe fills and blocks the writer, which
109
+ * would show up as a mysteriously slow iteration rather than as a deadlock.
110
+ */
111
+ async function collectTail_(stream) {
112
+ if (stream === null) {
113
+ return "";
114
+ }
115
+ const chunks = [];
116
+ let size = 0;
117
+ for await (const chunk of stream) {
118
+ const buffer = Buffer.from(chunk);
119
+ chunks.push(buffer);
120
+ size += buffer.length;
121
+ while (chunks.length > 1 &&
122
+ size - (chunks[0]?.length ?? 0) >= STDERR_TAIL_BYTES_) {
123
+ size -= chunks.shift()?.length ?? 0;
124
+ }
125
+ }
126
+ return Buffer.concat(chunks).subarray(-STDERR_TAIL_BYTES_).toString("utf8");
127
+ }
128
+ /** @returns The reported phases, or `null` when the process reported none. */
129
+ function parsePhases_(stdout) {
130
+ const line = stdout.trimEnd().split("\n").at(-1);
131
+ if (line === undefined || line === "") {
132
+ return null;
133
+ }
134
+ try {
135
+ return coldStartPhasesSchema.parse(JSON.parse(line));
136
+ }
137
+ catch {
138
+ // A case that writes its own output to stdout is not an error.
139
+ return null;
140
+ }
141
+ }
142
+ //# sourceMappingURL=_process-execution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_process-execution.js","sourceRoot":"","sources":["../src/_process-execution.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import { BenchCaseRunContext } from "@ac-bench/core/runner";
2
+ import { DurationCaseRunFn } from "./decl.js";
3
+ import { DurationCaseOptions, DurationCommandCaseOptions, DurationConditionOptions } from "./options.js";
4
+ export type DurationRunCaseOptions = DurationCaseOptions & {
5
+ conditionOptions?: DurationConditionOptions;
6
+ /** Set by `durationCommandCase`; the case body is then a formality. */
7
+ commandCase?: {
8
+ readonly command: string;
9
+ readonly args: readonly string[];
10
+ readonly options: DurationCommandCaseOptions | undefined;
11
+ };
12
+ };
13
+ /**
14
+ * Runs one benchmark case under the adaptive sampler.
15
+ *
16
+ * The loop belongs to the sampler: it decides how many samples to take next and
17
+ * when the interval around the estimate is tight enough. What happens here is
18
+ * only the measuring — the timed span is `performance.now()` immediately around
19
+ * one iteration and nothing else, so hooks, statistics and reporting stay
20
+ * outside the number being reported.
21
+ *
22
+ * An iteration is one call in this process, one fresh process running this same
23
+ * case body, or one execution of an external program. All three go through the
24
+ * same loop and the same stopping rule; only what an iteration _is_ differs.
25
+ *
26
+ * @param title Case name, carried into the reported result.
27
+ * @param runFn The work to measure.
28
+ * @param context Run context, used to report the result and check for abort.
29
+ * @param options Sampling policy and execution mode.
30
+ */
31
+ export declare function runDurationCase(title: string, runFn: DurationCaseRunFn, context: BenchCaseRunContext, options?: DurationRunCaseOptions): Promise<void>;
32
+ //# sourceMappingURL=_run-case.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_run-case.d.ts","sourceRoot":"","sources":["../src/_run-case.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,mBAAmB,EAGnB,MAAM,uBAAuB,CAAC;AA8B/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EACN,mBAAmB,EACnB,0BAA0B,EAC1B,wBAAwB,EACxB,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,sBAAsB,GAAG,mBAAmB,GAAG;IAC1D,gBAAgB,CAAC,EAAE,wBAAwB,CAAC;IAC5C,uEAAuE;IACvE,WAAW,CAAC,EAAE;QACb,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;QACjC,QAAQ,CAAC,OAAO,EAAE,0BAA0B,GAAG,SAAS,CAAC;KACzD,CAAC;CACF,CAAC;AAKF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,eAAe,CACpC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,iBAAiB,EACxB,OAAO,EAAE,mBAAmB,EAC5B,OAAO,CAAC,EAAE,sBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAiRf"}