@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
@@ -0,0 +1,132 @@
1
+ import * as z from "zod/mini";
2
+ import { HARNESS_OVERHEAD_DOMINATES, } from "./_harness-overhead.js";
3
+ import { SPAWN_BASELINE_DOMINATES, } from "./_spawn-baseline.js";
4
+ /** Robust dispersion above which results are considered unstable. */
5
+ const HIGH_VARIANCE = 0.1;
6
+ /** Fraction of samples that must be outliers before it is worth reporting. */
7
+ const OUTLIER_FRACTION = 0.05;
8
+ /** How much slower the first sample must be to suggest missing warmup. */
9
+ const FIRST_RUN_SLOW_FACTOR = 1.5;
10
+ /** Fraction of runtime above which declared overhead dominates the result. */
11
+ const OVERHEAD_DOMINATES = 0.25;
12
+ /** Categories of quality problem detected in a result. */
13
+ export const durationBenchWarningKindSchema = z.enum([
14
+ "outliers",
15
+ "first-run-slow",
16
+ "high-variance",
17
+ "overhead-dominates",
18
+ "too-few-samples",
19
+ "target-not-reached",
20
+ "harness-overhead-dominates",
21
+ "overhead-subtracted",
22
+ "no-baseline-args",
23
+ "spawn-baseline-dominates",
24
+ "spawn-baseline-subtracted",
25
+ ]);
26
+ /** A quality problem that should be shown alongside the numbers. */
27
+ export const durationBenchWarningSchema = z.object({
28
+ kind: durationBenchWarningKindSchema,
29
+ message: z.string(),
30
+ /** Defaults to `"warning"`; `"info"` states a fact rather than a problem. */
31
+ severity: z.optional(z.enum(["info", "warning"])),
32
+ });
33
+ /**
34
+ * Surfaces conditions that make a number untrustworthy.
35
+ *
36
+ * Reporting a tidy median while the machine was busy is worse than reporting
37
+ * nothing, because it looks authoritative.
38
+ */
39
+ export function detectWarnings_(input) {
40
+ const { timings, statistics, overheadMs, minRuns } = input;
41
+ const warnings = [];
42
+ if (statistics.samples < minRuns) {
43
+ warnings.push({
44
+ kind: "too-few-samples",
45
+ message: `only ${statistics.samples} samples collected; results are indicative at best`,
46
+ });
47
+ }
48
+ // A target the run did not meet is the one thing a tight-looking interval
49
+ // must not be allowed to hide.
50
+ if (input.sampling != null &&
51
+ input.sampling.effectiveRelativeErrorTarget !== null &&
52
+ !input.sampling.targetReached) {
53
+ const achieved = input.sampling.achievedRelativeError;
54
+ warnings.push({
55
+ kind: "target-not-reached",
56
+ message: `stopped on ${input.sampling.stopReason} at ${achieved === null
57
+ ? "an unknown relative error"
58
+ : `${(achieved * 100).toFixed(1)}% relative error`}, short of the ${(input.sampling.effectiveRelativeErrorTarget * 100).toFixed(1)}% asked for`,
59
+ });
60
+ }
61
+ const harness = input.harnessOverhead;
62
+ if (harness != null && harness.share > HARNESS_OVERHEAD_DOMINATES) {
63
+ warnings.push({
64
+ kind: "harness-overhead-dominates",
65
+ message: `${(harness.share * 100).toFixed(0)}% of each ${harness.shape} sample is the harness itself (${harness.medianMs.toFixed(6)}ms); the workload is too small to time this way`,
66
+ });
67
+ }
68
+ // States a fact rather than a problem, so that nobody compares a corrected
69
+ // number against an uncorrected one by accident.
70
+ if (harness?.subtracted === true) {
71
+ warnings.push({
72
+ kind: "overhead-subtracted",
73
+ severity: "info",
74
+ message: `${harness.medianMs.toFixed(6)}ms of ${harness.shape} harness overhead was removed from every sample`,
75
+ });
76
+ }
77
+ if (input.needsBaselineArgs === true) {
78
+ warnings.push({
79
+ kind: "no-baseline-args",
80
+ message: "no baselineArgs given, so the program's own start-up time is reported as work; pass the arguments that make it do nothing",
81
+ });
82
+ }
83
+ const baseline = input.spawnBaseline;
84
+ if (baseline != null && baseline.share > SPAWN_BASELINE_DOMINATES) {
85
+ warnings.push({
86
+ kind: "spawn-baseline-dominates",
87
+ message: `${(baseline.share * 100).toFixed(0)}% of each sample is starting the program (${baseline.statistics.medianMs.toFixed(3)}ms); what is left is mostly start-up noise`,
88
+ });
89
+ }
90
+ if (baseline?.subtracted === true) {
91
+ warnings.push({
92
+ kind: "spawn-baseline-subtracted",
93
+ severity: "info",
94
+ message: `${baseline.statistics.medianMs.toFixed(3)}ms of start-up was removed from every sample behind the adjusted figure`,
95
+ });
96
+ }
97
+ if (statistics.outlierIndices.length >
98
+ statistics.samples * OUTLIER_FRACTION) {
99
+ const percent = ((statistics.outlierIndices.length / statistics.samples) *
100
+ 100).toFixed(0);
101
+ warnings.push({
102
+ kind: "outliers",
103
+ message: `${percent}% of samples are statistical outliers; the distribution is heavy-tailed (GC pauses or scheduler preemption): prefer median over the mean`,
104
+ });
105
+ }
106
+ const first = timings[0];
107
+ if (first !== undefined &&
108
+ statistics.medianMs > 0 &&
109
+ first >= statistics.maxMs &&
110
+ first > statistics.medianMs * FIRST_RUN_SLOW_FACTOR) {
111
+ warnings.push({
112
+ kind: "first-run-slow",
113
+ message: `first sample was the slowest of all and ${(first / statistics.medianMs).toFixed(1)}x the median; consider increasing warmup`,
114
+ });
115
+ }
116
+ if (statistics.relativeMad > HIGH_VARIANCE) {
117
+ warnings.push({
118
+ kind: "high-variance",
119
+ message: `median absolute deviation is ${(statistics.relativeMad * 100).toFixed(0)}% of the median; treat small differences as noise`,
120
+ });
121
+ }
122
+ if (overheadMs > 0 &&
123
+ statistics.medianMs > 0 &&
124
+ overheadMs / statistics.medianMs > OVERHEAD_DOMINATES) {
125
+ warnings.push({
126
+ kind: "overhead-dominates",
127
+ message: `fixed overhead is ${((overheadMs / statistics.medianMs) * 100).toFixed(0)}% of the measurement; compare the adjusted column`,
128
+ });
129
+ }
130
+ return warnings;
131
+ }
132
+ //# sourceMappingURL=_warnings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_warnings.js","sourceRoot":"","sources":["../src/_warnings.ts"],"names":[],"mappings":"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"}
package/dist/decl.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { MaybeAsyncCallable, MaybeAsyncCallableNoArgs } from "@ac-kit/core";
2
+ import { DurationCaseOptions, DurationCommandCaseOptions, DurationConditionOptions } from "./options.js";
3
+ export declare function durationCondition(title: string, fn: MaybeAsyncCallableNoArgs): void;
4
+ export declare function durationCondition(title: string, options: DurationConditionOptions, fn: MaybeAsyncCallableNoArgs): void;
5
+ export type DurationCaseRunFn = MaybeAsyncCallable<[signal: AbortSignal]>;
6
+ export declare function durationCase(title: string, run: DurationCaseRunFn): void;
7
+ export declare function durationCase(title: string, options: DurationCaseOptions, run: DurationCaseRunFn): void;
8
+ /**
9
+ * Measures an external program. One iteration is one execution of it.
10
+ *
11
+ * The program is started directly, never through a shell: `args` is a vector,
12
+ * so nothing in it is interpreted.
13
+ *
14
+ * @param title Case name.
15
+ * @param command Executable, resolved on `PATH`.
16
+ * @param args Arguments passed to it, unquoted and uninterpreted.
17
+ * @param options Working directory, environment, and the do-nothing arguments
18
+ * used to measure the program's own start-up.
19
+ */
20
+ export declare function durationCommandCase(title: string, command: string, args: readonly string[], options?: DurationCommandCaseOptions): void;
21
+ //# sourceMappingURL=decl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decl.d.ts","sourceRoot":"","sources":["../src/decl.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAI5E,OAAO,EACN,mBAAmB,EACnB,0BAA0B,EAC1B,wBAAwB,EACxB,MAAM,cAAc,CAAC;AAQtB,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,wBAAwB,GAC1B,IAAI,CAAC;AACR,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,wBAAwB,EACjC,EAAE,EAAE,wBAAwB,GAC1B,IAAI,CAAC;AAyBR,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;AAE1E,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,iBAAiB,GAAG,IAAI,CAAC;AAC1E,wBAAgB,YAAY,CAC3B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,mBAAmB,EAC5B,GAAG,EAAE,iBAAiB,GACpB,IAAI,CAAC;AA8BR;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAClC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,CAAC,EAAE,0BAA0B,GAClC,IAAI,CAWN"}
package/dist/decl.js ADDED
@@ -0,0 +1,54 @@
1
+ import { registerCase, registerCondition } from "@ac-bench/core/runner";
2
+ import { runDurationCase } from "./_run-case.js";
3
+ import { calibrateSpawnOverhead } from "./spawn-overhead.js";
4
+ const MEASURE_TYPE_ = "duration";
5
+ export function durationCondition(title, optionsOrFn, fn) {
6
+ const [options, callback] = typeof optionsOrFn === "function"
7
+ ? [undefined, optionsOrFn]
8
+ : [optionsOrFn, fn];
9
+ const spawnOverheads = { current: null };
10
+ registerCondition(MEASURE_TYPE_, title, callback, {
11
+ onBegin: async () => {
12
+ if (options?.spawnBaselines && options.spawnBaselines.length > 0) {
13
+ spawnOverheads.current = await calibrateSpawnOverhead(options.spawnBaselines);
14
+ }
15
+ },
16
+ additionalRunArgs: [options, spawnOverheads],
17
+ });
18
+ }
19
+ export function durationCase(title, optionsOrRun, run) {
20
+ const [options, runFn] = typeof optionsOrRun === "function"
21
+ ? [undefined, optionsOrRun]
22
+ : [optionsOrRun, run];
23
+ registerCase(MEASURE_TYPE_, title, (context, conditionOptions, spawnOverheads) => {
24
+ const language = options?.tags?.["language"];
25
+ const calibratedOverheadMs = language !== undefined
26
+ ? spawnOverheads.current?.get(language)?.meanMs
27
+ : undefined;
28
+ return runDurationCase(title, runFn, context, {
29
+ ...options,
30
+ conditionOptions,
31
+ overheadMs: options?.overheadMs ?? calibratedOverheadMs,
32
+ });
33
+ });
34
+ }
35
+ /**
36
+ * Measures an external program. One iteration is one execution of it.
37
+ *
38
+ * The program is started directly, never through a shell: `args` is a vector,
39
+ * so nothing in it is interpreted.
40
+ *
41
+ * @param title Case name.
42
+ * @param command Executable, resolved on `PATH`.
43
+ * @param args Arguments passed to it, unquoted and uninterpreted.
44
+ * @param options Working directory, environment, and the do-nothing arguments
45
+ * used to measure the program's own start-up.
46
+ */
47
+ export function durationCommandCase(title, command, args, options) {
48
+ registerCase(MEASURE_TYPE_, title, (context, conditionOptions) => runDurationCase(title, () => { }, context, {
49
+ ...options,
50
+ conditionOptions,
51
+ commandCase: { command, args, options },
52
+ }));
53
+ }
54
+ //# sourceMappingURL=decl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decl.js","sourceRoot":"","sources":["../src/decl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAGxE,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAOjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,MAAM,aAAa,GAAG,UAAmB,CAAC;AAc1C,MAAM,UAAU,iBAAiB,CAChC,KAAa,EACb,WAAgE,EAChE,EAA6B;IAE7B,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,GACxB,OAAO,WAAW,KAAK,UAAU;QAChC,CAAC,CAAC,CAAC,SAAS,EAAE,WAAW,CAAC;QAC1B,CAAC,CAAC,CAAC,WAAW,EAAE,EAAG,CAAC,CAAC;IAEvB,MAAM,cAAc,GAAsB,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAE5D,iBAAiB,CAAC,aAAa,EAAE,KAAK,EAAE,QAAQ,EAAE;QACjD,OAAO,EAAE,KAAK,IAAI,EAAE;YACnB,IAAI,OAAO,EAAE,cAAc,IAAI,OAAO,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAClE,cAAc,CAAC,OAAO,GAAG,MAAM,sBAAsB,CACpD,OAAO,CAAC,cAAc,CACtB,CAAC;YACH,CAAC;QACF,CAAC;QACD,iBAAiB,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC;KAC5C,CAAC,CAAC;AACJ,CAAC;AAUD,MAAM,UAAU,YAAY,CAC3B,KAAa,EACb,YAAqD,EACrD,GAAuB;IAEvB,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,GACrB,OAAO,YAAY,KAAK,UAAU;QACjC,CAAC,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC;QAC3B,CAAC,CAAC,CAAC,YAAY,EAAE,GAAI,CAAC,CAAC;IAEzB,YAAY,CACX,aAAa,EACb,KAAK,EACL,CAAC,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,EAAE;QAC7C,MAAM,QAAQ,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC,UAAU,CAAC,CAAC;QAC7C,MAAM,oBAAoB,GACzB,QAAQ,KAAK,SAAS;YACrB,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,MAAM;YAC/C,CAAC,CAAC,SAAS,CAAC;QAEd,OAAO,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE;YAC7C,GAAG,OAAO;YACV,gBAAgB;YAChB,UAAU,EAAE,OAAO,EAAE,UAAU,IAAI,oBAAoB;SACvD,CAAC,CAAC;IACJ,CAAC,CACD,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CAClC,KAAa,EACb,OAAe,EACf,IAAuB,EACvB,OAAoC;IAEpC,YAAY,CACX,aAAa,EACb,KAAK,EACL,CAAC,OAAO,EAAE,gBAAgB,EAAE,EAAE,CAC7B,eAAe,CAAC,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,OAAO,EAAE;QACzC,GAAG,OAAO;QACV,gBAAgB;QAChB,WAAW,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE;KACvC,CAAC,CACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { afterAll, afterEach, beforeAll, beforeEach, } from "@ac-bench/core/runner";
2
+ export * from "./decl.js";
3
+ export * from "./options.js";
4
+ export * from "./spawn-overhead.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,QAAQ,EACR,SAAS,EACT,SAAS,EACT,UAAU,GACV,MAAM,uBAAuB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { afterAll, afterEach, beforeAll, beforeEach, } from "@ac-bench/core/runner";
2
+ export * from "./decl.js";
3
+ export * from "./options.js";
4
+ export * from "./spawn-overhead.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,QAAQ,EACR,SAAS,EACT,SAAS,EACT,UAAU,GACV,MAAM,uBAAuB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=iteration-child.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iteration-child.d.ts","sourceRoot":"","sources":["../src/iteration-child.ts"],"names":[],"mappings":""}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * One-iteration entry point for `execution: "per-iteration-process"`.
3
+ *
4
+ * Spawned fresh for every sample, with no IPC channel: its whole result is its
5
+ * wall time and its exit code, so nothing in this file is inside the number
6
+ * being reported except the work itself. The phase line it writes to stdout is
7
+ * emitted after the case body has returned.
8
+ */
9
+ import { pathToFileURL } from "node:url";
10
+ import { drainConditions, resolveForkUnit, runCaseOnce, } from "@ac-bench/core/runner";
11
+ import { iterationPayloadSchema, SINGLE_ITERATION_OPTION, } from "./_iteration-protocol.js";
12
+ const [, , rawPayload] = process.argv;
13
+ if (rawPayload === undefined) {
14
+ process.stderr.write("iteration child: missing payload argument\n");
15
+ process.exit(2);
16
+ }
17
+ const payload = iterationPayloadSchema.parse(JSON.parse(rawPayload));
18
+ const importBegan = performance.now();
19
+ await import(pathToFileURL(payload.file).href);
20
+ const importEnded = performance.now();
21
+ const unit = resolveForkUnit(drainConditions(), payload.conditionPath, payload.conditionTitles);
22
+ // Cancellation reaches this process as a signal to its group, not through here.
23
+ const never = new AbortController().signal;
24
+ const runBegan = performance.now();
25
+ await runCaseOnce(unit, payload.caseTitle, never, {
26
+ file: payload.file,
27
+ measureOptions: { [SINGLE_ITERATION_OPTION]: true },
28
+ });
29
+ const runEnded = performance.now();
30
+ const { nodeStart, bootstrapComplete } = performance.nodeTiming;
31
+ const phases = {
32
+ startupMs: bootstrapComplete - nodeStart,
33
+ importMs: importEnded - importBegan,
34
+ runMs: runEnded - runBegan,
35
+ };
36
+ process.stdout.write(`${JSON.stringify(phases)}\n`);
37
+ //# sourceMappingURL=iteration-child.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iteration-child.js","sourceRoot":"","sources":["../src/iteration-child.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EACN,eAAe,EACf,eAAe,EACf,WAAW,GACX,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACN,sBAAsB,EACtB,uBAAuB,GACvB,MAAM,0BAA0B,CAAC;AAGlC,MAAM,CAAC,EAAE,AAAD,EAAG,UAAU,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;AAEtC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;IAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC;AAED,MAAM,OAAO,GAAG,sBAAsB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;AAErE,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;AACtC,MAAM,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;AAC/C,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;AAEtC,MAAM,IAAI,GAAG,eAAe,CAC3B,eAAe,EAAE,EACjB,OAAO,CAAC,aAAa,EACrB,OAAO,CAAC,eAAe,CACvB,CAAC;AAEF,gFAAgF;AAChF,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC,MAAM,CAAC;AAE3C,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;AACnC,MAAM,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE;IACjD,IAAI,EAAE,OAAO,CAAC,IAAI;IAClB,cAAc,EAAE,EAAE,CAAC,uBAAuB,CAAC,EAAE,IAAI,EAAE;CACnD,CAAC,CAAC;AACH,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;AAEnC,MAAM,EAAE,SAAS,EAAE,iBAAiB,EAAE,GAAG,WAAW,CAAC,UAAU,CAAC;AAEhE,MAAM,MAAM,GAAoB;IAC/B,SAAS,EAAE,iBAAiB,GAAG,SAAS;IACxC,QAAQ,EAAE,WAAW,GAAG,WAAW;IACnC,KAAK,EAAE,QAAQ,GAAG,QAAQ;CAC1B,CAAC;AAEF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,59 @@
1
+ import { CaseCommonOptions } from "@ac-bench/core/runner";
2
+ import { DurationSampling } from "./_sampling-options.js";
3
+ import { SpawnBaseline } from "./spawn-overhead.js";
4
+ export type DurationConditionOptions = {
5
+ sampling?: Partial<DurationSampling>;
6
+ /**
7
+ * Reference commands to calibrate once, before any case runs, and match
8
+ * against a case's `tags.language` to set its
9
+ * {@link DurationCaseOptions.overheadMs} automatically. A case that already
10
+ * declares `overheadMs` is left alone.
11
+ */
12
+ spawnBaselines?: readonly SpawnBaseline[];
13
+ };
14
+ export type DurationExecution =
15
+ /** Default. One iteration is one call in the measuring process. */
16
+ "in-process"
17
+ /**
18
+ * One iteration is one fresh process. Measures cold start — module load, JIT
19
+ * warm-up, first-call cost — which is exactly what `"in-process"`
20
+ * deliberately warms away.
21
+ *
22
+ * The enclosing conditions' `beforeEach`/`afterEach` still run in the
23
+ * measuring process; the case's own `setup`/`teardown` run inside each
24
+ * spawned process, and so are part of the sample.
25
+ */
26
+ | "per-iteration-process";
27
+ export type DurationCaseOptions = CaseCommonOptions & {
28
+ /** Defaults to `"in-process"`. */
29
+ execution?: DurationExecution;
30
+ /**
31
+ * Fixed overhead in milliseconds subtracted from each timing.
32
+ *
33
+ * Mutually exclusive with a measured spawn baseline: declaring both is a
34
+ * validation error rather than a silent double subtraction.
35
+ */
36
+ overheadMs?: number;
37
+ /**
38
+ * Measure what starting the program costs and report it beside the result.
39
+ *
40
+ * Defaults to `true` whenever a spawn baseline applies at all. The baseline
41
+ * is never folded into the primary statistics; it drives the `adjusted`
42
+ * figure, the same way a declared `overheadMs` does.
43
+ */
44
+ subtractSpawnBaseline?: boolean;
45
+ };
46
+ export type DurationCommandCaseOptions = Omit<DurationCaseOptions, "execution"> & {
47
+ cwd?: string;
48
+ env?: Readonly<Record<string, string>>;
49
+ /**
50
+ * Arguments that make the program do nothing, used to measure its own
51
+ * start-up baseline.
52
+ *
53
+ * Without them the baseline would have to be guessed from an unrelated
54
+ * interpreter, so a command case that omits them emits a `no-baseline-args`
55
+ * warning rather than silently reporting start-up time as work.
56
+ */
57
+ baselineArgs?: readonly string[];
58
+ };
59
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,MAAM,wBAAwB,GAAG;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAErC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,iBAAiB;AAC5B,mEAAmE;AACjE,YAAY;AACd;;;;;;;;GAQG;GACD,uBAAuB,CAAC;AAE3B,MAAM,MAAM,mBAAmB,GAAG,iBAAiB,GAAG;IACrD,kCAAkC;IAClC,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAE9B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAC5C,mBAAmB,EACnB,WAAW,CACX,GAAG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAEvC;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@ac-bench/core/plugin").MeasurePlugin;
2
+ export default _default;
3
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":""}
package/dist/plugin.js ADDED
@@ -0,0 +1,205 @@
1
+ import { defineMeasurePlugin, MeasureResultValidationError, } from "@ac-bench/core/plugin";
2
+ import { ratioWithUncertainty, } from "@ac-kit/math-stats";
3
+ import { columnCell, dataFrameWithField, getDataFrameColumnByName, MILLISECOND, OPERATIONS_PER_SECOND, } from "@ac-kit/model-dataset";
4
+ import * as z from "zod/mini";
5
+ import { poolDurationExecutions } from "./_pool.js";
6
+ import { durationBenchRunCaseResultSchema, durationBenchRunConditionResultSchema, } from "./_types.js";
7
+ const MEASURE_ID = "duration";
8
+ const FIELDS = [
9
+ { name: "case", kind: "nominal", title: "case" },
10
+ {
11
+ name: "medianMs",
12
+ kind: "quantitative",
13
+ title: "median",
14
+ unit: MILLISECOND,
15
+ direction: "lower-is-better",
16
+ format: { minimumFractionDigits: 3, maximumFractionDigits: 3 },
17
+ },
18
+ {
19
+ name: "medianMarginMs",
20
+ kind: "quantitative",
21
+ title: "ci",
22
+ unit: MILLISECOND,
23
+ prefix: "±",
24
+ direction: "lower-is-better",
25
+ format: { minimumFractionDigits: 3, maximumFractionDigits: 3 },
26
+ },
27
+ {
28
+ name: "madMs",
29
+ kind: "quantitative",
30
+ title: "mad",
31
+ unit: MILLISECOND,
32
+ prefix: "±",
33
+ format: { minimumFractionDigits: 3, maximumFractionDigits: 3 },
34
+ },
35
+ {
36
+ name: "p95Ms",
37
+ kind: "quantitative",
38
+ title: "p95",
39
+ unit: MILLISECOND,
40
+ direction: "lower-is-better",
41
+ format: { minimumFractionDigits: 3, maximumFractionDigits: 3 },
42
+ },
43
+ {
44
+ name: "opsPerSecond",
45
+ kind: "quantitative",
46
+ title: "rate",
47
+ unit: OPERATIONS_PER_SECOND,
48
+ direction: "higher-is-better",
49
+ format: { maximumFractionDigits: 0 },
50
+ },
51
+ {
52
+ name: "samples",
53
+ kind: "quantitative",
54
+ title: "n",
55
+ format: { maximumFractionDigits: 0 },
56
+ },
57
+ {
58
+ name: "adjustedMs",
59
+ kind: "quantitative",
60
+ title: "adjusted",
61
+ unit: MILLISECOND,
62
+ direction: "lower-is-better",
63
+ format: { minimumFractionDigits: 3, maximumFractionDigits: 3 },
64
+ },
65
+ ];
66
+ /**
67
+ * Half-width of the interval around the reported estimate.
68
+ *
69
+ * Prefers the pooled interval, which accounts for the spread between the
70
+ * processes that measured the case; the within-process interval alone would
71
+ * claim a precision no single process can support.
72
+ */
73
+ function getEstimatedUncertainty(result) {
74
+ if (result.estimate !== null) {
75
+ return (result.estimate.upperMs - result.estimate.lowerMs) / 2;
76
+ }
77
+ return result.statistics.medianConfidence95Ms;
78
+ }
79
+ /** One field's cells as numbers, with anything non-numeric read as absent. */
80
+ function numericColumn_(frame, name) {
81
+ const column = getDataFrameColumnByName(frame, name);
82
+ const values = Array.from({ length: frame.rowCount });
83
+ for (let rowIndex = 0; rowIndex < frame.rowCount; rowIndex++) {
84
+ const value = columnCell(column, rowIndex);
85
+ values[rowIndex] = typeof value === "number" ? value : null;
86
+ }
87
+ return values;
88
+ }
89
+ /** The dispersion `relativeTo` wants, rebuilt from the two reported columns. */
90
+ function relativeSpread_(centre, mad) {
91
+ return mad === null || centre <= 0 ? 0 : mad / centre;
92
+ }
93
+ export default defineMeasurePlugin({
94
+ id: "duration",
95
+ label: "Duration",
96
+ fields: FIELDS,
97
+ caseRow: (result) => {
98
+ const stats = result.statistics;
99
+ if (!stats) {
100
+ return [result.name, null, null, null, null, null, null, null];
101
+ }
102
+ const uncertainty = getEstimatedUncertainty(result);
103
+ return [
104
+ result.name,
105
+ stats.medianMs,
106
+ uncertainty,
107
+ stats.madMs,
108
+ stats.p95Ms,
109
+ stats.opsPerSecond ?? null,
110
+ stats.samples,
111
+ result.adjusted ? result.adjusted.medianMs : null,
112
+ ];
113
+ },
114
+ comparison: {
115
+ field: "medianMs",
116
+ interval: "medianMarginMs",
117
+ ratioField: "ratio",
118
+ },
119
+ deriveFields: (frame) => {
120
+ const medians = numericColumn_(frame, "medianMs");
121
+ const mads = numericColumn_(frame, "madMs");
122
+ let fastestIndex = -1;
123
+ let fastest = Number.POSITIVE_INFINITY;
124
+ for (let rowIndex = 0; rowIndex < frame.rowCount; rowIndex++) {
125
+ const median = medians[rowIndex] ?? null;
126
+ if (median !== null && median < fastest) {
127
+ fastestIndex = rowIndex;
128
+ fastest = median;
129
+ }
130
+ }
131
+ if (fastestIndex === -1 || fastest <= 0) {
132
+ return frame;
133
+ }
134
+ const baselineSpread = relativeSpread_(fastest, mads[fastestIndex] ?? null);
135
+ // `relativeTo` needs only a centre and a relative spread, both of which the
136
+ // frame already carries — so the ratio is derived from the rows on show
137
+ // rather than from the raw results, and a partial frame derives partial
138
+ // ratios instead of no column at all.
139
+ const ratioAt = (rowIndex) => {
140
+ const median = medians[rowIndex] ?? null;
141
+ if (median === null) {
142
+ return null;
143
+ }
144
+ return ratioWithUncertainty(median, relativeSpread_(median, mads[rowIndex] ?? null), fastest, baselineSpread);
145
+ };
146
+ const withRatio = dataFrameWithField(frame, {
147
+ name: "ratio",
148
+ kind: "quantitative",
149
+ title: "rel",
150
+ direction: "lower-is-better",
151
+ uncertaintyField: "ratioMargin",
152
+ suffix: "×",
153
+ format: { minimumFractionDigits: 2, maximumFractionDigits: 2 },
154
+ }, (_frame, rowIndex) => rowIndex === fastestIndex ? 1 : (ratioAt(rowIndex)?.ratio ?? null));
155
+ return dataFrameWithField(withRatio, {
156
+ name: "ratioMargin",
157
+ kind: "quantitative",
158
+ title: "margin",
159
+ prefix: "±",
160
+ suffix: "×",
161
+ format: { minimumFractionDigits: 2, maximumFractionDigits: 2 },
162
+ }, (_frame, rowIndex) => rowIndex === fastestIndex
163
+ ? null
164
+ : (ratioAt(rowIndex)?.errorMargin ?? null));
165
+ },
166
+ warnings: (result) => {
167
+ const diagnostics = [];
168
+ if (result.failure) {
169
+ diagnostics.push({
170
+ severity: "error",
171
+ code: "case-failed",
172
+ message: result.failure,
173
+ });
174
+ }
175
+ for (const warning of result.warnings) {
176
+ diagnostics.push({
177
+ severity: "warning",
178
+ code: warning.kind,
179
+ message: warning.message,
180
+ });
181
+ }
182
+ return diagnostics;
183
+ },
184
+ scheduling: { rounds: "many", grouping: "case" },
185
+ pool: (executions) => poolDurationExecutions(executions),
186
+ parseCaseResult: (value) => {
187
+ const result = durationBenchRunCaseResultSchema.safeParse(value);
188
+ if (!result.success) {
189
+ throw new MeasureResultValidationError(MEASURE_ID, z.prettifyError(result.error));
190
+ }
191
+ return result.data;
192
+ },
193
+ parseConditionResult: (value) => {
194
+ const result = durationBenchRunConditionResultSchema.safeParse(value);
195
+ if (!result.success) {
196
+ throw new MeasureResultValidationError(MEASURE_ID, z.prettifyError(result.error));
197
+ }
198
+ return result.data;
199
+ },
200
+ toJsonCase: (result) => result,
201
+ toJsonCondition: (result) => ({
202
+ conditions: [{ kind: "bench", ...result }],
203
+ }),
204
+ });
205
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}