@cosyte/bench 0.1.2 → 0.1.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 027dbf5: A reading measures the empty-call floor once per run: the peak of a call that does nothing, read in
8
+ its own process through the same probe as the cases, which is what the probe itself costs. It is in
9
+ the reading as `memoryFloor`, the Markdown prints it, and every case's peak also gives each figure
10
+ above the floor's median, never below zero: `peakTotalAboveFloorBytes`,
11
+ `medianPeakTotalAboveFloorBytes`, and the same for the heap and external memory.
12
+
13
+ `budgetPeakBytes(caseReading)` is the peak a memory budget reads. For a case whose inputs average
14
+ under `BENCH_CONTRACT.FLOOR_INPUT_BYTES` (64 KiB) it is the median call's total above the floor's
15
+ median; for any other, or for a suite that gave no byte count, the highest raw `peakTotalBytes`, as
16
+ before. On a GitHub-hosted runner the floor's median call is about 3.3 KiB and its highest about
17
+ 28 KiB, and that highest lands in a small case's highest peak, so a small input reads its median
18
+ call. A budget script that read `peakTotalBytes` should call `budgetPeakBytes` instead, and a suite
19
+ that added its own empty "floor" case can drop it.
20
+
21
+ One case that cannot be measured no longer stops the run. `cosyte-bench run` writes every other case,
22
+ lists what failed in the reading's `failures` (the phase, `timing`, `memory` or `floor`, the case,
23
+ and the first line of the error) and in the Markdown, prints the whole error to stderr, and exits 2.
24
+ A case whose timing failed is left out of `cases`; one whose memory failed keeps its timing. A script
25
+ that ran each case alone to get past one failure can run them together and read `failures`.
26
+
27
+ Every new field is added within results version 1, and documents written before still validate.
28
+
3
29
  ## 0.1.2
4
30
 
5
31
  ### Patch Changes
package/README.md CHANGED
@@ -98,14 +98,21 @@ cosyte-bench check bench.json
98
98
  Exit codes: 0 clean, 1 a finding (a regression, or a results file that does not match the schema),
99
99
  2 could not measure (a skip, a process that died, an input it could not read). A skip is not a pass.
100
100
 
101
+ A `run` whose measuring process fails for one case still measures and writes every other case. The
102
+ reading lists what failed in `failures` (the phase, `timing`, `memory` or `floor`, the case, and the
103
+ first line of the error; the whole error goes to stderr) and the command exits 2. A case whose timing
104
+ failed is left out of `cases`; one whose memory failed keeps its timing with its memory `null`. A
105
+ case whose warmup never settled is not a failure: it is in `cases`, its throughput `skipped` with
106
+ the reason `warmup-unstable`.
107
+
101
108
  ## Entry points
102
109
 
103
- | entry point | what it is |
104
- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
105
- | `cosyte-bench` | the bin: `run`, `compare`, `calibrate` and `check` |
106
- | `@cosyte/bench` | writing a suite (`defineSuite`, `defineBenchmark`), judging ratios (`judge`), reading results (`readResults`, `RESULTS_SCHEMA`), and `BENCH_CONTRACT` |
107
- | `@cosyte/bench/schema/results-v1.schema.json` | the results schema as JSON Schema, for a tool in another language |
108
- | `@cosyte/bench/package.json` | the manifest |
110
+ | entry point | what it is |
111
+ | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
112
+ | `cosyte-bench` | the bin: `run`, `compare`, `calibrate` and `check` |
113
+ | `@cosyte/bench` | writing a suite (`defineSuite`, `defineBenchmark`), judging ratios (`judge`), reading results (`readResults`, `RESULTS_SCHEMA`, `budgetPeakBytes`), and `BENCH_CONTRACT` |
114
+ | `@cosyte/bench/schema/results-v1.schema.json` | the results schema as JSON Schema, for a tool in another language |
115
+ | `@cosyte/bench/package.json` | the manifest |
109
116
 
110
117
  The module entry point has no side effects: importing it starts no process. The schema is plain JSON:
111
118
 
@@ -137,19 +144,38 @@ the command line:
137
144
 
138
145
  ## What it measures
139
146
 
140
- | Measure | How | What it cannot see |
141
- | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
142
- | Throughput, small to very large inputs | Per size, fresh processes; a warmup by the scaling gate's rule, or by the large-case plan below when every warmup batch is one long pass; 5 samples of whole passes of at least 50 ms; the median of the processes' medians | Anything the suite's sizes do not cover; numbers from another machine |
143
- | Latency p50, p95, p99 | One call at a time on a nanosecond clock, up to 2 s or 5,000 calls; a percentile is reported only with at least 10 calls above it | Calls shorter than 20 clock reads (skipped, typed); the tail of a size with few calls |
144
- | Allocations per operation | Heap growth over windows of `n` and `2n` operations in which no collection ran, in a process with a 64 MiB young generation; the difference cancels the cost of reading the heap. When every window collects, the largest pair is counted with what its collections reclaimed (`method: "gc-accounted"`) | Native memory a library allocates outside V8's accounting |
145
- | Peak memory | Per call, the higher of what was in use just before any collection during the call and at its end, above what was in use before it: the V8 heap (`peakHeapBytes`), external memory such as Buffers and ArrayBuffers (`peakExternalBytes`), and the two together (`peakTotalBytes`), which is what a memory budget reads | Native memory a library allocates outside V8's accounting |
146
- | Heap flat over a long run | 30 rounds, the heap settled after each; a Theil-Sen slope over the last 24; growing when the fitted growth reaches 64 KiB. On the long-run size and on the largest size whose 30 rounds fit in 60 s | A leak too small to reach 64 KiB over the run (each result says the smallest it could see, `detectableBytesPerOp`); external memory |
147
- | Cold import and loaded bytes | 20 fresh processes import the target, 20 import an empty module, interleaved; one more records every file the import loads, with its gzip size | What a bundler keeps after tree shaking |
147
+ | Measure | How | What it cannot see |
148
+ | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
149
+ | Throughput, small to very large inputs | Per size, fresh processes; a warmup by the scaling gate's rule, or by the large-case plan below when every warmup batch is one long pass; 5 samples of whole passes of at least 50 ms; the median of the processes' medians | Anything the suite's sizes do not cover; numbers from another machine |
150
+ | Latency p50, p95, p99 | One call at a time on a nanosecond clock, up to 2 s or 5,000 calls; a percentile is reported only with at least 10 calls above it | Calls shorter than 20 clock reads (skipped, typed); the tail of a size with few calls |
151
+ | Allocations per operation | Heap growth over windows of `n` and `2n` operations in which no collection ran, in a process with a 64 MiB young generation; the difference cancels the cost of reading the heap. When every window collects, the largest pair is counted with what its collections reclaimed (`method: "gc-accounted"`) | Native memory a library allocates outside V8's accounting |
152
+ | Peak memory | Per call, the higher of what was in use just before any collection during the call and at its end, above what was in use before it: the V8 heap (`peakHeapBytes`), external memory such as Buffers and ArrayBuffers (`peakExternalBytes`), and the two together (`peakTotalBytes`); each also above the run's empty-call floor (`peakTotalAboveFloorBytes` and the like), below | Native memory a library allocates outside V8's accounting |
153
+ | Heap flat over a long run | 30 rounds, the heap settled after each; a Theil-Sen slope over the last 24; growing when the fitted growth reaches 64 KiB. On the long-run size and on the largest size whose 30 rounds fit in 60 s | A leak too small to reach 64 KiB over the run (each result says the smallest it could see, `detectableBytesPerOp`); external memory |
154
+ | Cold import and loaded bytes | 20 fresh processes import the target, 20 import an empty module, interleaved; one more records every file the import loads, with its gzip size | What a bundler keeps after tree shaking |
148
155
 
149
156
  Memory measurements need `gc()`; the runner starts those processes with `--expose-gc` itself, and
150
157
  with `--no-concurrent-array-buffer-sweeping`, so a collection frees the ArrayBuffers it found dead
151
158
  before it ends and the external memory it reports before and after is exact.
152
159
 
160
+ ### The empty-call floor and small inputs
161
+
162
+ Reading the peak costs memory of its own: settling the heap, the profiler that records each
163
+ collection, the record itself. A reading therefore measures an empty call once, in a fresh process
164
+ through the same probe (`memoryFloor`), and gives each peak figure both raw and above the floor's
165
+ median, never below zero (`peakTotalAboveFloorBytes`, `medianPeakTotalAboveFloorBytes`, and the same
166
+ for the heap and external memory).
167
+
168
+ On a GitHub-hosted runner the floor's median call is about 3.3 KiB and its highest about 28 KiB (one
169
+ call, the first). That highest call is the probe's, and it lands in a case's highest peak too, which
170
+ is why a few hundred bytes of input could not read under 20 times its size whatever the code did.
171
+
172
+ A memory budget reads `budgetPeakBytes(caseReading)`: for a case whose inputs average under
173
+ `BENCH_CONTRACT.FLOOR_INPUT_BYTES` (64 KiB) it is the median call's total above the floor's median,
174
+ the code's typical call without the probe; for any other case, or one whose suite gave no byte
175
+ count, it is the highest raw `peakTotalBytes`. At 64 KiB even the probe's highest call is about 2%
176
+ of a 20 times budget, so a large input keeps the raw highest figure, which needs nothing from
177
+ another process. A suite that added its own empty "floor" case can drop it.
178
+
153
179
  ### Large cases
154
180
 
155
181
  A case whose single call takes longer than a warmup batch (50 ms) cannot settle by the scaling
@@ -203,6 +229,8 @@ CI runner image, never by host name, user or absolute path.
203
229
  - `judge(ratios, threshold?, alpha?)`: the comparison's verdict for one case from its per-round ratios.
204
230
  - `readResults(text)`: parse and check a results document; throws a `TypeError` listing every
205
231
  problem. `RESULTS_SCHEMA` is the schema, also shipped as `schema/results-v1.schema.json`.
232
+ - `budgetPeakBytes(caseReading)`: the peak a memory budget reads for a case: the median call above
233
+ the empty-call floor for an input under 64 KiB, the highest raw total for any other.
206
234
  - `BENCH_CONTRACT`: the frozen constants.
207
235
  - `BenchError`: what a run that could not measure throws.
208
236
 
@@ -21,6 +21,7 @@ var BENCH_CONTRACT = Object.freeze({
21
21
  ALLOC_WINDOW_MS: 100,
22
22
  ALLOC_SEMI_SPACE_MIB: 64,
23
23
  PEAK_CALLS: 20,
24
+ FLOOR_INPUT_BYTES: 64 * 1024,
24
25
  LONG_RUN_ROUNDS: 30,
25
26
  LONG_RUN_SETTLE_ROUNDS: 5,
26
27
  LONG_RUN_ROUND_MS: 100,
@@ -487,10 +488,17 @@ function measureJob(job, shape, warmup, pass, call, deps) {
487
488
  };
488
489
  }
489
490
  function measuredSomething(result) {
491
+ if (result.mode === "floor") return result.peak.status === "measured";
490
492
  if (result.mode === "timing") return result.status === "measured";
491
493
  return [result.allocations, result.peak, result.longRun].some((r) => r?.status === "measured");
492
494
  }
493
495
  async function runJob(job, deps = realDeps()) {
496
+ if (job.mode === "floor") {
497
+ return {
498
+ mode: "floor",
499
+ peak: deps.probe === null ? SKIPPED_NO_GC : measurePeak(() => void 0, deps.probe)
500
+ };
501
+ }
494
502
  const suiteModule = await import(pathToFileURL(job.suite).href);
495
503
  const suite = checkSuite(suiteModule.default);
496
504
  const target = await import(pathToFileURL(job.target).href);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/contract.ts","../src/stats.ts","../src/memory.ts","../../test-utils/src/perf/contract.ts","../src/measure.ts","../src/suite.ts","../src/child.ts","../src/child-entry.ts"],"names":["round","msPerOp","a","b"],"mappings":"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","file":"child-entry.mjs","sourcesContent":["/**\n * The benchmark constants, frozen.\n *\n * Every value here is fixed by the performance standard,\n * `documentation/decisions/0005-the-performance-standard.md` in the `config` repo, which says for\n * each one whether it was measured or chosen, and on what basis. The warmup rule is not here: it\n * is `PERF_CONTRACT` in `@cosyte/test-utils/perf` (ADR 0001 section 2), which `measure.ts` imports\n * and the build bundles, so the scaling gate and the benchmarks warm up by one rule kept in one\n * place.\n *\n * They are not tuning knobs and cannot be overridden per package. A comparison's false alarm rate\n * was measured with these values; change one and the measurement no longer describes the tool.\n *\n * @packageDocumentation\n */\n\n/** The benchmark constants. See {@link BENCH_CONTRACT}. */\nexport interface BenchContractShape {\n /** Timed samples per case per process. Each sample is whole passes over the corpus. */\n readonly SAMPLES: number;\n /** Minimum timed length of one sample, in milliseconds. A sample runs whole passes until it is reached. */\n readonly SAMPLE_MIN_MS: number;\n /** Rounds in one base against head comparison. Each round runs both sides once, in fresh processes. */\n readonly ROUNDS: number;\n /** One-sided error allowed at each end of the comparison's interval on the median ratio. */\n readonly ALPHA: number;\n /** The smallest slowdown a comparison will call a regression, as a fraction: 0.03 is 3%. */\n readonly THRESHOLD: number;\n /** Time budget for the latency calls of one case, in milliseconds. */\n readonly LATENCY_BUDGET_MS: number;\n /** Most latency calls taken for one case. */\n readonly LATENCY_MAX_CALLS: number;\n /** Fewest latency calls taken for one case, whatever the budget says. */\n readonly LATENCY_MIN_CALLS: number;\n /** A percentile q is reported only when at least this many calls lie above it: n(1 - q) >= 10. */\n readonly LATENCY_TAIL_CALLS: number;\n /** The median call must be at least this many timer reads long, or latency is skipped. */\n readonly LATENCY_TIMER_MULTIPLE: number;\n /** Most operations in the first allocation window. Halved while a garbage collection lands in a window. */\n readonly ALLOC_WINDOW_OPS: number;\n /** The first allocation window is cut to about this many milliseconds of operations, from the warmup's pace. */\n readonly ALLOC_WINDOW_MS: number;\n /** The young generation size the allocation process runs with, in MiB, so a window rarely collects. */\n readonly ALLOC_SEMI_SPACE_MIB: number;\n /** Calls measured one by one for the peak heap. */\n readonly PEAK_CALLS: number;\n /** Rounds in the long run that checks the heap stays flat. */\n readonly LONG_RUN_ROUNDS: number;\n /** Rounds at the start of the long run left out of the slope, while caches and compiled code settle. */\n readonly LONG_RUN_SETTLE_ROUNDS: number;\n /** Minimum length of one long run round, in milliseconds, fixed as a number of operations by the first. */\n readonly LONG_RUN_ROUND_MS: number;\n /** A long run whose fitted growth over the measured rounds reaches this many bytes is reported as growing. */\n readonly LONG_RUN_GROWTH_BYTES: number;\n /** A garbage collection round that reclaims less than this, in bytes, counts as settled (ADR 0001 section 8). */\n readonly GC_SETTLE_BYTES: number;\n /** Consecutive settled rounds needed (ADR 0001 section 8). */\n readonly GC_SETTLE_ROUNDS: number;\n /** Most garbage collection rounds before giving up (ADR 0001 section 8). */\n readonly GC_MAX_ROUNDS: number;\n /** Fresh processes timed for the cold import, for the target and for an empty module each. */\n readonly IMPORT_RUNS: number;\n /**\n * A case whose last three warmup batches each took at least this many milliseconds a pass runs on\n * the large-case plan: every batch is one pass, so the warmup rule's aggregation cannot happen.\n */\n readonly LARGE_PASS_MS: number;\n /** Passes in each of the two windows whose medians the large-case warmup compares. */\n readonly LARGE_WARMUP_WINDOW: number;\n /** Hard cap on the large-case warmup, in milliseconds, from the start of the warmup. */\n readonly LARGE_WARMUP_MAX_MS: number;\n /**\n * The long run also runs on a benchmark's largest size whose rounds fit in this many milliseconds,\n * a round being at least one pass.\n */\n readonly LONG_RUN_MAX_MS: number;\n /** The timing comparison may become a required check when A/A false alarms are under this rate. */\n readonly REQUIRED_FALSE_ALARM_RATE: number;\n /** The confidence that the false alarm rate is under the line, for the rule above. */\n readonly REQUIRED_CONFIDENCE: number;\n}\n\n/**\n * The frozen benchmark constants.\n *\n * @example\n * ```ts\n * import { BENCH_CONTRACT } from \"@cosyte/bench\";\n *\n * BENCH_CONTRACT.ROUNDS; // => 12\n * BENCH_CONTRACT.THRESHOLD; // => 0.03\n * ```\n */\nexport const BENCH_CONTRACT: BenchContractShape = Object.freeze({\n SAMPLES: 5,\n SAMPLE_MIN_MS: 50,\n ROUNDS: 12,\n ALPHA: 0.05,\n THRESHOLD: 0.03,\n LATENCY_BUDGET_MS: 2_000,\n LATENCY_MAX_CALLS: 5_000,\n LATENCY_MIN_CALLS: 20,\n LATENCY_TAIL_CALLS: 10,\n LATENCY_TIMER_MULTIPLE: 20,\n ALLOC_WINDOW_OPS: 256,\n ALLOC_WINDOW_MS: 100,\n ALLOC_SEMI_SPACE_MIB: 64,\n PEAK_CALLS: 20,\n LONG_RUN_ROUNDS: 30,\n LONG_RUN_SETTLE_ROUNDS: 5,\n LONG_RUN_ROUND_MS: 100,\n LONG_RUN_GROWTH_BYTES: 64 * 1024,\n GC_SETTLE_BYTES: 64 * 1024,\n GC_SETTLE_ROUNDS: 2,\n GC_MAX_ROUNDS: 7,\n IMPORT_RUNS: 20,\n LARGE_PASS_MS: 50,\n LARGE_WARMUP_WINDOW: 5,\n LARGE_WARMUP_MAX_MS: 30_000,\n LONG_RUN_MAX_MS: 60_000,\n REQUIRED_FALSE_ALARM_RATE: 0.01,\n REQUIRED_CONFIDENCE: 0.95,\n});\n","/**\n * The statistics the benchmarks report and judge by. Plain arithmetic with no dependencies, each\n * function small enough to check by hand against the worked numbers in its tests.\n *\n * @packageDocumentation\n */\n\n/**\n * A sorted copy, ascending. The input is not touched.\n *\n * @internal\n */\nexport function sortedCopy(xs: readonly number[]): number[] {\n return [...xs].sort((a, b) => a - b);\n}\n\n/**\n * The median: the middle value, or the mean of the two middle values. `NaN` for an empty vector,\n * so an empty measurement can never read as a fast one.\n *\n * @internal\n */\nexport function median(xs: readonly number[]): number {\n const s = sortedCopy(xs);\n const mid = Math.floor(s.length / 2);\n // An even length of at least 2 has both middle values. An empty vector takes the even branch and\n // reads two missing values, which is `NaN`: the empty answer, by arithmetic rather than a check.\n return s.length % 2 === 1\n ? (s[mid] as number)\n : ((s[mid - 1] as number) + (s[mid] as number)) / 2;\n}\n\n/**\n * A percentile by nearest rank: the smallest value with at least a fraction `q` of the values at or\n * below it. No interpolation, so every reported percentile is a value that was observed.\n *\n * @internal\n */\nexport function nearestRank(xs: readonly number[], q: number): number {\n if (xs.length === 0) return Number.NaN;\n const s = sortedCopy(xs);\n const rank = Math.max(1, Math.ceil(q * s.length));\n return s[rank - 1] as number;\n}\n\n/**\n * `P(X <= k)` for `X ~ Binomial(n, p)`, summed in log space so it stays finite for large `n`, for\n * `0 < p < 1` (every caller's range). A negative `k` sums nothing and is 0; a `k` past `n` sums all\n * of it and is 1.\n *\n * @internal\n */\nexport function binomialCdf(k: number, n: number, p: number): number {\n const logP = Math.log(p);\n const logQ = Math.log1p(-p);\n let logCoef = 0;\n let total = 0;\n for (let i = 0; i <= Math.min(k, n); i++) {\n if (i > 0) logCoef += Math.log(n - i + 1) - Math.log(i);\n total += Math.exp(logCoef + i * logP + (n - i) * logQ);\n }\n return Math.min(1, total);\n}\n\n/**\n * The sign test's rank for an interval on the median of `n` values: the largest `k` such that the\n * `k`-th smallest value lies below the true median with probability at most `alpha`, that is\n * `P(Binomial(n, 1/2) <= k - 1) <= alpha`. `0` when no rank qualifies (too few values).\n *\n * @internal\n */\nexport function signTestRank(n: number, alpha: number): number {\n let k = 0;\n while (k < n && binomialCdf(k, n, 0.5) <= alpha) k++;\n return k;\n}\n\n/** An interval on a median, from the sign test. */\nexport interface MedianInterval {\n /** The `rank`-th smallest value. */\n readonly low: number;\n /** The `rank`-th largest value. */\n readonly high: number;\n /** Which order statistics bound it. */\n readonly rank: number;\n /** The chance that each end alone misses the median, exactly: `P(Binomial(n, 1/2) <= rank - 1)`. */\n readonly tail: number;\n}\n\n/**\n * The distribution-free interval on the median of independent values: the `k`-th smallest and the\n * `k`-th largest, with `k` from {@link signTestRank}. It assumes nothing about the shape of the\n * noise, which on a shared runner is one-sided and heavy-tailed. `null` when there are too few\n * values for any rank to meet `alpha`.\n *\n * @internal\n */\nexport function signTestInterval(xs: readonly number[], alpha: number): MedianInterval | null {\n const rank = signTestRank(xs.length, alpha);\n if (rank === 0) return null;\n const s = sortedCopy(xs);\n // `rank` is at least 1 and at most `n`, so both order statistics exist.\n return {\n low: s[rank - 1] as number,\n high: s[s.length - rank] as number,\n rank,\n tail: binomialCdf(rank - 1, xs.length, 0.5),\n };\n}\n\n/**\n * The exact (Clopper-Pearson) one-sided upper confidence bound on a rate, from `k` events in `n`\n * trials: the largest rate `p` at which seeing `k` or fewer is still at least `1 - confidence`\n * likely. With no events this is `1 - (1 - confidence)^(1/n)`, about `3 / n` at 95%.\n *\n * @internal\n */\nexport function rateUpperBound(k: number, n: number, confidence: number): number {\n // No trials say nothing. Every trial an event bisects to 1 on its own.\n if (n === 0) return 1;\n const alpha = 1 - confidence;\n let lo = k / n;\n let hi = 1;\n // Stryker disable next-line EqualityOperator: a 101st halving moves the bound by under 2^-100, below a double's resolution.\n for (let i = 0; i < 100; i++) {\n const mid = (lo + hi) / 2;\n // Stryker disable next-line EqualityOperator: `>` and `>=` differ only where the tail equals alpha exactly, which bisection over doubles does not land on.\n if (binomialCdf(k, n, mid) > alpha) lo = mid;\n else hi = mid;\n }\n return hi;\n}\n\n/**\n * The fewest trials, all without an event, whose {@link rateUpperBound} is under `rate`: how long an\n * A/A calibration has to run before a clean record can show a false alarm rate under `rate`.\n *\n * @internal\n */\nexport function trialsForZeroEvents(rate: number, confidence: number): number {\n return Math.ceil(Math.log(1 - confidence) / Math.log1p(-rate));\n}\n\n/**\n * The Theil-Sen slope: the median of the slopes between every pair of points with distinct `x`.\n * One wild point moves it little, which matters for a heap reading that a late collection can\n * spike. `NaN` with fewer than two distinct `x`.\n *\n * @internal\n */\nexport function theilSenSlope(xs: readonly number[], ys: readonly number[]): number {\n // Pairs only exist where both vectors do; a caller passes vectors of one length.\n const slopes: number[] = [];\n xs.forEach((xi, i) => {\n for (let j = i + 1; j < xs.length; j++) {\n const dx = (xs[j] as number) - xi;\n if (dx !== 0) slopes.push(((ys[j] as number) - (ys[i] as number)) / dx);\n }\n });\n return median(slopes);\n}\n\n/**\n * Round to a number of decimal places, for readable reports. The full values stay in\n * the sample vectors.\n *\n * @internal\n */\nexport function round(n: number, places = 4): number {\n const f = 10 ** places;\n return Math.round(n * f) / f;\n}\n","/**\n * The memory measurements: allocations per operation, peak heap per call, and whether the heap stays\n * flat over a long run. Each runs in a measuring process started with `--expose-gc`, one case per\n * process.\n *\n * They read the heap through a {@link HeapProbe}, so their tests can drive them with a scripted heap.\n * The real probe follows ADR 0001 section 8: a zero-argument `gc()` only, settled by rule rather than\n * by a count. The measuring process also runs with `--no-concurrent-array-buffer-sweeping`, so a\n * collection frees the ArrayBuffers it found dead before it ends, and the external memory a\n * collection reports before and after is what it held and what it kept.\n *\n * @packageDocumentation\n */\n\nimport { getHeapStatistics, GCProfiler } from \"node:v8\";\n\nimport { BENCH_CONTRACT } from \"./contract.js\";\nimport type { Clock } from \"./measure.js\";\nimport { median, round, theilSenSlope } from \"./stats.js\";\n\n/**\n * What the memory measurements read the heap with.\n *\n * @internal\n */\nexport interface HeapProbe {\n /** A full collection. The real probe calls `gc()` with no argument, the only form ADR 0001 allows. */\n readonly gc: () => void;\n /** Bytes in use on the V8 heap. */\n readonly usedHeap: () => number;\n /** Bytes held outside the V8 heap by JavaScript objects (Buffers, ArrayBuffers). */\n readonly external: () => number;\n /**\n * Run `fn` and report the collections that ran during it, and the most heap in use just before any\n * of them. The heap only shrinks at a collection, so its high point is either there or at the end.\n */\n readonly profile: (fn: () => void) => ProfileReport;\n}\n\n/**\n * The collections that ran during a profiled call.\n *\n * @internal\n */\nexport interface ProfileReport {\n readonly gcs: number;\n /** The most V8 heap in use just before any of them; 0 with none. */\n readonly peakBeforeGc: number;\n /** The most external memory (Buffers, ArrayBuffers) just before any of them; 0 with none. */\n readonly peakExternalBeforeGc: number;\n /** The most of the two together just before any of them; 0 with none. */\n readonly peakTotalBeforeGc: number;\n /** V8 heap bytes the collections reclaimed: in use before each, less in use after it, summed. */\n readonly reclaimedHeap: number;\n /** External bytes the collections reclaimed, the same way. */\n readonly reclaimedExternal: number;\n}\n\n/**\n * The real probe, or `null` when the process was not started with `--expose-gc`.\n *\n * @internal\n */\nexport function realProbe(): HeapProbe | null {\n const gc: unknown = (globalThis as { gc?: unknown }).gc;\n if (typeof gc !== \"function\") return null;\n return {\n gc: () => {\n (gc as () => void)();\n },\n usedHeap: () => getHeapStatistics().used_heap_size,\n external: () => process.memoryUsage().external,\n profile: (fn) => {\n const profiler = new GCProfiler();\n profiler.start();\n fn();\n const stats = profiler.stop().statistics;\n const report = {\n gcs: stats.length,\n peakBeforeGc: 0,\n peakExternalBeforeGc: 0,\n peakTotalBeforeGc: 0,\n reclaimedHeap: 0,\n reclaimedExternal: 0,\n };\n for (const s of stats) {\n const before = s.beforeGC.heapStatistics;\n const after = s.afterGC.heapStatistics;\n report.peakBeforeGc = Math.max(report.peakBeforeGc, before.usedHeapSize);\n report.peakExternalBeforeGc = Math.max(report.peakExternalBeforeGc, before.externalMemory);\n report.peakTotalBeforeGc = Math.max(\n report.peakTotalBeforeGc,\n before.usedHeapSize + before.externalMemory,\n );\n report.reclaimedHeap += before.usedHeapSize - after.usedHeapSize;\n report.reclaimedExternal += before.externalMemory - after.externalMemory;\n }\n return report;\n },\n };\n}\n\n/** The settled heap, or the news that it would not settle. */\nexport interface SettleResult {\n readonly settled: boolean;\n readonly rounds: number;\n readonly heap: number;\n}\n\n/**\n * Collect until two rounds in a row each reclaim under `GC_SETTLE_BYTES`, at most `GC_MAX_ROUNDS`\n * rounds (ADR 0001 section 8).\n *\n * @internal\n */\nexport function settle(probe: HeapProbe): SettleResult {\n let quiet = 0;\n let before = probe.usedHeap();\n for (let round = 1; round <= BENCH_CONTRACT.GC_MAX_ROUNDS; round++) {\n probe.gc();\n const after = probe.usedHeap();\n quiet = before - after < BENCH_CONTRACT.GC_SETTLE_BYTES ? quiet + 1 : 0;\n before = after;\n if (quiet >= BENCH_CONTRACT.GC_SETTLE_ROUNDS)\n return { settled: true, rounds: round, heap: after };\n }\n return { settled: false, rounds: BENCH_CONTRACT.GC_MAX_ROUNDS, heap: before };\n}\n\n/**\n * Why a memory measurement has no number. `allocation-window-gc` is no longer written: a case whose\n * every window collects is now counted with its collections. It stays here because documents\n * written before carry it.\n */\nexport type MemorySkipReason = \"gc-unavailable\" | \"gc-unsettled\" | \"allocation-window-gc\";\n\n/**\n * How allocations were counted. `no-gc-window`: the heap's growth over windows in which no\n * collection ran. `gc-accounted`: every window collected, so the growth over each window plus what\n * its collections reclaimed.\n */\nexport type AllocationMethod = \"no-gc-window\" | \"gc-accounted\";\n\n/** Allocations per operation. */\nexport type AllocationReport =\n | {\n readonly status: \"measured\";\n /** How they were counted. Absent in a document written before it was recorded: `no-gc-window`. */\n readonly method?: AllocationMethod;\n /** Collections that ran in the two windows counted: 0 for `no-gc-window`. */\n readonly collections?: number;\n /** The smaller of the two windows, in operations. */\n readonly windowOps: number;\n /** Bytes allocated on the V8 heap per operation. */\n readonly heapBytesPerOp: number;\n /** Bytes allocated outside it (Buffers, ArrayBuffers) per operation. */\n readonly externalBytesPerOp: number;\n }\n | { readonly status: \"skipped\"; readonly reason: MemorySkipReason };\n\n/**\n * Allocations per operation, from the heap's growth across two windows of `n` and `2n` operations\n * in which no collection ran: `(growth(2n) - growth(n)) / n`, so the fixed cost of reading the heap\n * cancels. The first `n` is about `ALLOC_WINDOW_MS` of operations at `msPerOp`, at most\n * `ALLOC_WINDOW_OPS`. A collection in either window halves `n` and tries again. The process runs with a large\n * young generation (`ALLOC_SEMI_SPACE_MIB`) so that a window rarely collects.\n *\n * When a collection lands in every window, as it does when one call allocates more than the young\n * generation holds, the first and largest pair of windows is counted with its collections instead\n * (`gc-accounted`): what a window allocated is its growth plus what its collections reclaimed, read\n * from each collection's heap before and after it. No further calls are made. On a case whose windows\n * do not collect the two counts are the same number, since nothing was reclaimed.\n *\n * @internal\n */\nexport function measureAllocations(\n call: (i: number) => void,\n probe: HeapProbe,\n msPerOp: number,\n): AllocationReport {\n const window = (ops: number) => {\n const s = settle(probe);\n const h0 = probe.usedHeap();\n const e0 = probe.external();\n const p = probe.profile(() => {\n for (let i = 0; i < ops; i++) call(i);\n });\n return {\n settled: s.settled,\n gcs: p.gcs,\n heap: probe.usedHeap() - h0 + p.reclaimedHeap,\n ext: probe.external() - e0 + p.reclaimedExternal,\n };\n };\n const perOp = (\n method: AllocationMethod,\n n: number,\n a: ReturnType<typeof window>,\n b: ReturnType<typeof window>,\n ): AllocationReport => ({\n status: \"measured\",\n method,\n collections: a.gcs + b.gcs,\n windowOps: n,\n heapBytesPerOp: round((b.heap - a.heap) / n, 1),\n externalBytesPerOp: round((b.ext - a.ext) / n, 1),\n });\n const paced = Math.floor(BENCH_CONTRACT.ALLOC_WINDOW_MS / msPerOp);\n const first = Math.max(1, Math.min(BENCH_CONTRACT.ALLOC_WINDOW_OPS, paced));\n let largest: [ReturnType<typeof window>, ReturnType<typeof window>] | null = null;\n for (let n = first; n >= 1; n = Math.floor(n / 2)) {\n const a = window(n);\n const b = window(2 * n);\n if (!a.settled || !b.settled) return { status: \"skipped\", reason: \"gc-unsettled\" };\n if (a.gcs === 0 && b.gcs === 0) return perOp(\"no-gc-window\", n, a, b);\n largest ??= [a, b];\n }\n // The loop ran at least once (`first` is at least 1) and every pass that reached here collected.\n const [a, b] = largest as [ReturnType<typeof window>, ReturnType<typeof window>];\n return perOp(\"gc-accounted\", first, a, b);\n}\n\n/** Peak memory per call: the V8 heap, external memory, and the two together. */\nexport type PeakReport =\n | {\n readonly status: \"measured\";\n readonly calls: number;\n /** The highest V8 heap peak of any call, in bytes above the settled heap before it. */\n readonly peakHeapBytes: number;\n /** The median call's V8 heap peak. */\n readonly medianPeakHeapBytes: number;\n /**\n * The highest external peak of any call (Buffers, ArrayBuffers), above the external memory\n * held before it. Absent in a document written before external memory was read.\n */\n readonly peakExternalBytes?: number;\n /** The median call's external peak. */\n readonly medianPeakExternalBytes?: number;\n /**\n * The highest peak of the V8 heap and external memory together, above the two before the call:\n * the figure a memory budget reads. Not the sum of the two peaks above, which need not come at\n * one moment.\n */\n readonly peakTotalBytes?: number;\n /** The median call's peak of the two together. */\n readonly medianPeakTotalBytes?: number;\n }\n | { readonly status: \"skipped\"; readonly reason: MemorySkipReason };\n\n/**\n * The peak memory of single calls: settle, call once, and take the higher of the memory in use just\n * before any collection during the call and at its end, less what was in use before it, for the V8\n * heap, for external memory, and for the two together. Each only shrinks at a collection (the\n * process frees dead ArrayBuffers within the collection), so its high point is at one of those\n * moments. Run with V8's default heap sizing, so the collections that shape the peak are the ones a\n * user's process has.\n *\n * @internal\n */\nexport function measurePeak(call: (i: number) => void, probe: HeapProbe): PeakReport {\n const heap: number[] = [];\n const external: number[] = [];\n const total: number[] = [];\n for (let i = 0; i < BENCH_CONTRACT.PEAK_CALLS; i++) {\n const s = settle(probe);\n if (!s.settled) return { status: \"skipped\", reason: \"gc-unsettled\" };\n const e0 = probe.external();\n const p = probe.profile(() => {\n call(i);\n });\n const h1 = probe.usedHeap();\n const e1 = probe.external();\n heap.push(Math.max(p.peakBeforeGc, h1) - s.heap);\n external.push(Math.max(p.peakExternalBeforeGc, e1) - e0);\n total.push(Math.max(p.peakTotalBeforeGc, h1 + e1) - (s.heap + e0));\n }\n return {\n status: \"measured\",\n calls: heap.length,\n peakHeapBytes: Math.max(...heap),\n medianPeakHeapBytes: median(heap),\n peakExternalBytes: Math.max(...external),\n medianPeakExternalBytes: median(external),\n peakTotalBytes: Math.max(...total),\n medianPeakTotalBytes: median(total),\n };\n}\n\n/** Whether the heap stayed flat over a long run. */\nexport type LongRunReport =\n | {\n readonly status: \"measured\";\n /** `growing` when the fitted growth reaches `LONG_RUN_GROWTH_BYTES`. */\n readonly verdict: \"flat\" | \"growing\";\n /** Operations in all. */\n readonly ops: number;\n /** Operations per round, fixed by the first round. */\n readonly opsPerRound: number;\n /** The settled heap after each round, in bytes. */\n readonly settledHeap: readonly number[];\n /** Theil-Sen slope of the settled heap against operations, after the settling rounds. */\n readonly slopeBytesPerOp: number;\n /** The slope times the operations it was fitted over: the growth the verdict reads. */\n readonly growthBytes: number;\n /** The smallest leak, in bytes per operation, this run could call growing: the bound over the span. */\n readonly detectableBytesPerOp: number;\n }\n | { readonly status: \"skipped\"; readonly reason: MemorySkipReason };\n\n/**\n * Run the case for `LONG_RUN_ROUNDS` rounds, settle the heap after each, and fit the settled heap\n * against the operations run so far. A leak of `b` bytes an operation shows as a slope near `b`;\n * a heap that only grows while caches and compiled code settle is left out by\n * `LONG_RUN_SETTLE_ROUNDS`. The verdict reads the growth the slope implies over the fitted rounds,\n * not the slope alone: a settled heap reading moves by about a kilobyte with nothing running\n * (ADR 0001 section 8), so a run of few operations cannot tell a small slope from that.\n *\n * @internal\n */\nexport function measureLongRun(\n pass: () => void,\n opsPerPass: number,\n probe: HeapProbe,\n clock: Clock,\n): LongRunReport {\n const t0 = clock();\n let passesPerRound = 0;\n do {\n pass();\n passesPerRound++;\n } while (clock() - t0 < BENCH_CONTRACT.LONG_RUN_ROUND_MS);\n const opsPerRound = passesPerRound * opsPerPass;\n // Each round is the operations run so far and the heap settled after them. The sizing round\n // above is the first.\n const rounds: { ops: number; heap: number }[] = [];\n let ops = opsPerRound;\n for (;;) {\n const s = settle(probe);\n if (!s.settled) return { status: \"skipped\", reason: \"gc-unsettled\" };\n rounds.push({ ops, heap: s.heap });\n if (rounds.length === BENCH_CONTRACT.LONG_RUN_ROUNDS) break;\n for (let p = 0; p < passesPerRound; p++) pass();\n ops += opsPerRound;\n }\n const skip = BENCH_CONTRACT.LONG_RUN_SETTLE_ROUNDS;\n const fitted = rounds.slice(skip);\n const slope = theilSenSlope(\n fitted.map((r) => r.ops),\n fitted.map((r) => r.heap),\n );\n const span = (BENCH_CONTRACT.LONG_RUN_ROUNDS - 1 - skip) * opsPerRound;\n const growth = slope * span;\n return {\n status: \"measured\",\n // Stryker disable next-line EqualityOperator: growth is a fitted slope times 24 rounds, so exactly 65,536 bytes is not reached in floating point (tried in memory.test.ts history).\n verdict: growth >= BENCH_CONTRACT.LONG_RUN_GROWTH_BYTES ? \"growing\" : \"flat\",\n ops: BENCH_CONTRACT.LONG_RUN_ROUNDS * opsPerRound,\n opsPerRound,\n settledHeap: rounds.map((r) => r.heap),\n slopeBytesPerOp: round(slope, 4),\n growthBytes: Math.round(growth),\n detectableBytesPerOp: round(BENCH_CONTRACT.LONG_RUN_GROWTH_BYTES / span, 4),\n };\n}\n","/**\n * The frozen measurement constants.\n *\n * Every value here is fixed by **ADR 0001: the performance measurement contract**\n * (`documentation/decisions/0001-perf-measurement-contract.md` in the `config` repo), which in turn\n * derives them from the PERF-P0 calibration: 3,200 4N-vs-N ratios on a linear workload, 320 on a\n * deliberately O(n²) one, across two runner classes on Node 22.23.1 / V8 12.4.\n *\n * They are **not tuning knobs and are deliberately not overridable per package.** The ceiling was\n * set from the worst false alarm in that population and the weakest real signal at `hl7`'s own\n * fixture size, so it is conditional on the estimator, the sampling shape *and* the warmup rule:\n * changing any one of them moves the operating point the ceiling was measured against. The ADR's\n * review triggers are the process for changing them.\n *\n * @packageDocumentation\n */\n\n/**\n * The measurement constants, frozen. See {@link PERF_CONTRACT}.\n */\nexport interface PerfContractShape {\n /**\n * Upper bound on `min(scaled) / min(base)`. **8**: above the worst false alarm in 3,200 samples\n * (6.649, `min`, container leg) and below the weakest real O(n²) signal measured at `hl7`'s own\n * fixture size (8.84). Margins 1.20× / 1.10×. Measured.\n */\n readonly RATIO_CEILING: number;\n /**\n * Lower bound on the same ratio. **1.5**: below all 3,200 samples (worst 1.702) and above the ≈1\n * that a same-workload-on-both-phases bug produces. Margin 1.13×, the thinnest in the contract.\n * Measured.\n */\n readonly RATIO_FLOOR: number;\n /**\n * Timed repetitions per phase. **5**, measured by construction: the whole calibration was taken at\n * 5 reps, and `min` over a different rep count is a different statistic.\n */\n readonly REPS: number;\n /**\n * The multiplier between the base phase and the scaled phase. **4×**, measured by construction:\n * every ratio in the dataset is a 4× step, so the ideal linear ratio is 4.\n */\n readonly SCALE_STEP: number;\n /**\n * Floor on the base phase's `min` sample, in milliseconds. **4**: the fastest base phase in all\n * 3,200 samples was 4.14 ms; below that the ceiling is extrapolation, so the gate refuses to\n * answer. Measured.\n */\n readonly MIN_PHASE_MS: number;\n /**\n * Minimum total warmup time before the stability rule may fire, in milliseconds. **500**: one\n * further P0 trial (136 to 294 ms of the same workload) brought rep-to-rep drift from as much as\n * 1.23× inside ±5%; 500 ms is 1.7 to 3.7× that. Measured.\n */\n readonly WARMUP_MIN_MS: number;\n /**\n * Minimum timed duration of one warmup batch, in milliseconds. **50**: judgement forced by\n * measurement: a single ~4 to 9 ms pass is not stable enough to satisfy ±5% three times running\n * (1 to 92% of warm phases do, by axis), and a 50 ms batch aggregates 5 to 12 passes. Aggregation is\n * what shrinks that jitter, so the batch length is load-bearing rather than a free choice.\n */\n readonly WARMUP_BATCH_MS: number;\n /**\n * Hard cap on total warmup, in milliseconds. **5 000**: judgement, no measured basis. Bounds the\n * fleet cost and leaves ~100 batches of headroom, so the cap is reached only by a genuinely\n * unsettled runtime.\n */\n readonly WARMUP_MAX_MS: number;\n /**\n * Relative tolerance for the warmup stability rule, as a fraction. **0.05** (±5%): judgement\n * anchored on P0's rep1/rep5 ratio inside a warm 5-rep phase (0.96 to 1.05× GitHub, 0.97 to 1.02×\n * container), which is a different statistic at a different granularity.\n */\n readonly WARMUP_STABLE_TOL: number;\n /**\n * How many consecutive batches must lie inside the tolerance. **3**: two consecutive can be met\n * by a coincidental pair on a heavy-tailed distribution; a third costs 50 ms against a 5 s cap.\n */\n readonly WARMUP_STABLE_BATCHES: number;\n}\n\n/**\n * The frozen measurement constants, as decided by ADR 0001.\n *\n * Read them, print them in your own diagnostics, assert against them, but do not expect a knob to\n * override them. A package that cannot satisfy the contract at its fixture sizes has a fixture-size\n * problem, and {@link assertScalingGateFires} is what turns that into a build failure.\n *\n * @example\n * ```ts\n * import { PERF_CONTRACT } from \"@cosyte/test-utils/perf\";\n *\n * PERF_CONTRACT.RATIO_CEILING; // => 8\n * PERF_CONTRACT.SCALE_STEP; // => 4\n * ```\n */\nexport const PERF_CONTRACT: PerfContractShape = Object.freeze({\n RATIO_CEILING: 8,\n RATIO_FLOOR: 1.5,\n REPS: 5,\n SCALE_STEP: 4,\n MIN_PHASE_MS: 4,\n WARMUP_MIN_MS: 500,\n WARMUP_BATCH_MS: 50,\n WARMUP_MAX_MS: 5_000,\n WARMUP_STABLE_TOL: 0.05,\n WARMUP_STABLE_BATCHES: 3,\n});\n","/**\n * The timing primitives: the warmup, the throughput samples and the latency calls. They run inside\n * a measuring process, one case per process, and take their clocks as arguments so their tests can\n * drive them with exact arithmetic instead of a machine's noise.\n *\n * @packageDocumentation\n */\n\n// The one source of the warmup rule. A relative import of the scaling gate's own contract file, not\n// of the published `@cosyte/test-utils/perf`, so the build bundles it into `dist` and this package\n// keeps no runtime dependency (that one would bring `fast-check` in as a peer). A change to the rule\n// there changes this package's build, so it needs a changeset that bumps this package too.\nimport { PERF_CONTRACT } from \"../../test-utils/src/perf/contract.js\";\nimport { BENCH_CONTRACT } from \"./contract.js\";\nimport { median, nearestRank, round } from \"./stats.js\";\n\n/**\n * The warmup rule, read from `PERF_CONTRACT` in `@cosyte/test-utils/perf` (ADR 0001 section 2) and\n * renamed, so the scaling gate and the benchmarks warm up by one rule kept in one place.\n *\n * @internal\n */\nexport const WARMUP = Object.freeze({\n MIN_MS: PERF_CONTRACT.WARMUP_MIN_MS,\n BATCH_MS: PERF_CONTRACT.WARMUP_BATCH_MS,\n MAX_MS: PERF_CONTRACT.WARMUP_MAX_MS,\n STABLE_TOL: PERF_CONTRACT.WARMUP_STABLE_TOL,\n STABLE_BATCHES: PERF_CONTRACT.WARMUP_STABLE_BATCHES,\n});\n\n/**\n * A millisecond clock. `performance.now` in a real run.\n *\n * @internal\n */\nexport type Clock = () => number;\n\n/**\n * A nanosecond clock. `process.hrtime.bigint` in a real run.\n *\n * @internal\n */\nexport type NsClock = () => bigint;\n\n/**\n * Which warmup rule a process ended on. `standard` is ADR 0001 section 2's. `large` is the\n * large-case plan, for a case whose every batch is a single pass: see {@link warmUp}.\n */\nexport type WarmupPlan = \"standard\" | \"large\";\n\n/** What the warmup did. */\nexport interface WarmupReport {\n /**\n * Whether the warmup settled: on the standard plan, three batches in a row within 5% of their\n * median before 5 s ran out; on the large-case plan, two windows of passes with medians within 5%\n * of each other before 30 s ran out.\n */\n readonly stable: boolean;\n /** Wall time spent warming, in milliseconds. */\n readonly elapsedMs: number;\n /** Passes over the corpus. */\n readonly passes: number;\n /** Each batch's milliseconds per pass. On the large-case plan every batch after the switch is one pass. */\n readonly batches: readonly number[];\n /** The rule the warmup ended on. */\n readonly plan: WarmupPlan;\n}\n\n/**\n * Whether the last `STABLE_BATCHES` batches all lie within `STABLE_TOL` of their median.\n *\n * @internal\n */\nexport function batchesAreStable(batches: readonly number[]): boolean {\n if (batches.length < WARMUP.STABLE_BATCHES) return false;\n const window = batches.slice(-WARMUP.STABLE_BATCHES);\n const mid = median(window);\n // A zero median makes every relative distance `NaN`, which no tolerance accepts: refused.\n return window.every((b) => Math.abs(b - mid) / mid <= WARMUP.STABLE_TOL);\n}\n\n/**\n * Whether a case is large: its last `STABLE_BATCHES` batches each took at least `LARGE_PASS_MS` a\n * pass. A batch ends on the pass that reaches 50 ms, so such a batch is a single pass, and the\n * aggregation that ADR 0001 measured as what brings batch-to-batch jitter inside 5% cannot happen.\n *\n * @internal\n */\nexport function isLargeCase(batches: readonly number[]): boolean {\n if (batches.length < WARMUP.STABLE_BATCHES) return false;\n return batches.slice(-WARMUP.STABLE_BATCHES).every((b) => b >= BENCH_CONTRACT.LARGE_PASS_MS);\n}\n\n/**\n * The large-case plan's stability test: the median of the last `LARGE_WARMUP_WINDOW` passes lies\n * within `STABLE_TOL` of the median of the window before it. A median of 5 passes is moved by\n * neither of up to two stalled passes, where the standard rule needs every one of three to agree.\n *\n * @internal\n */\nexport function windowsAreStable(passes: readonly number[]): boolean {\n const w = BENCH_CONTRACT.LARGE_WARMUP_WINDOW;\n if (passes.length < 2 * w) return false;\n const last = median(passes.slice(-w));\n const before = median(passes.slice(-2 * w, -w));\n // A zero median makes the relative distance `NaN`, which no tolerance accepts: refused.\n return Math.abs(last - before) / before <= WARMUP.STABLE_TOL;\n}\n\n/**\n * Warm up by time, with a stability rule, never by a fixed count (ADR 0001 section 2): batches of\n * whole passes of at least 50 ms, at least 500 ms in all, until three batches in a row lie within\n * 5% of their median, or 5 s pass without that.\n *\n * A case whose passes are too long for that rule moves to the large-case plan: when 500 ms have\n * passed, the standard rule has not settled, and the last three batches were each a single pass of\n * at least `LARGE_PASS_MS` ({@link isLargeCase}). From there the warmup runs one pass at a time\n * until the median of the last `LARGE_WARMUP_WINDOW` passes is within 5% of the median of the\n * window before it ({@link windowsAreStable}), which takes at least twice the window in passes, or\n * until `LARGE_WARMUP_MAX_MS` from the start pass without that. A case that settles by the standard\n * rule never moves, so every case whose batches hold several passes warms up exactly as before.\n *\n * @internal\n */\nexport function warmUp(pass: () => void, clock: Clock): WarmupReport {\n const start = clock();\n const batches: number[] = [];\n let passes = 0;\n for (;;) {\n const b0 = clock();\n let inBatch = 0;\n let batchMs: number;\n do {\n pass();\n inBatch++;\n batchMs = clock() - b0;\n } while (batchMs < WARMUP.BATCH_MS);\n passes += inBatch;\n batches.push(batchMs / inBatch);\n const elapsedMs = clock() - start;\n if (elapsedMs >= WARMUP.MIN_MS) {\n if (batchesAreStable(batches)) {\n return { stable: true, elapsedMs, passes, batches, plan: \"standard\" };\n }\n if (isLargeCase(batches)) return warmUpLarge(pass, clock, start, batches, passes);\n }\n if (elapsedMs >= WARMUP.MAX_MS) {\n return { stable: false, elapsedMs, passes, batches, plan: \"standard\" };\n }\n }\n}\n\n/** The large-case plan, continued from the standard warmup's batches. See {@link warmUp}. */\nfunction warmUpLarge(\n pass: () => void,\n clock: Clock,\n start: number,\n batches: number[],\n passesSoFar: number,\n): WarmupReport {\n let passes = passesSoFar;\n for (;;) {\n const p0 = clock();\n pass();\n const t = clock();\n passes++;\n batches.push(t - p0);\n const elapsedMs = t - start;\n if (windowsAreStable(batches))\n return { stable: true, elapsedMs, passes, batches, plan: \"large\" };\n if (elapsedMs >= BENCH_CONTRACT.LARGE_WARMUP_MAX_MS) {\n return { stable: false, elapsedMs, passes, batches, plan: \"large\" };\n }\n }\n}\n\n/**\n * Take `SAMPLES` throughput samples. A sample runs whole passes until `SAMPLE_MIN_MS` has passed\n * and reports milliseconds per operation.\n *\n * `inject` plants a slowdown, for calibration only: after each pass the loop reads the clock until\n * `inject` times that pass's own measured time has passed, then starts the next pass, so each\n * sample's time includes the spin. That is all it does. It does not make the measured code slower\n * by exactly that fraction: the spin changes the loop the engine compiles and what the processor\n * does between passes, so the ratio a comparison then measures is not `1 + inject` and differs by\n * workload and machine. A calibration reports the ratio it observed for each case\n * (`CalibrationBody.observed`), and a detection rate is read against that, never against `inject`.\n * Zero in every real measurement.\n *\n * @internal\n */\nexport function sampleThroughput(\n pass: () => void,\n opsPerPass: number,\n clock: Clock,\n inject = 0,\n): number[] {\n const samples: number[] = [];\n for (let s = 0; s < BENCH_CONTRACT.SAMPLES; s++) {\n const t0 = clock();\n let t = t0;\n let passes = 0;\n do {\n const p0 = t;\n pass();\n t = clock();\n // Zero in a real measurement, so `until` is `t` and the clock is not read again.\n const until = t + inject * (t - p0);\n while (t < until) t = clock();\n passes++;\n } while (t - t0 < BENCH_CONTRACT.SAMPLE_MIN_MS);\n samples.push((t - t0) / (passes * opsPerPass));\n }\n return samples;\n}\n\n/**\n * The median gap between two back-to-back reads of the nanosecond clock: what one latency reading\n * costs before the call itself.\n *\n * @internal\n */\nexport function timerOverheadNs(ns: NsClock, reads = 1_000): number {\n const gaps: number[] = [];\n for (let i = 0; i < reads; i++) {\n const a = ns();\n const b = ns();\n gaps.push(Number(b - a));\n }\n return median(gaps);\n}\n\n/** Per-call latency, or why there is none. */\nexport type LatencyReport =\n | {\n readonly status: \"measured\";\n readonly calls: number;\n readonly timerOverheadNs: number;\n /** `null` when fewer than `LATENCY_TAIL_CALLS` calls lie above the percentile. */\n readonly p50Ns: number | null;\n readonly p95Ns: number | null;\n readonly p99Ns: number | null;\n readonly maxNs: number;\n }\n | {\n readonly status: \"skipped\";\n readonly reason: \"latency-below-timer-resolution\";\n readonly calls: number;\n readonly timerOverheadNs: number;\n readonly p50Ns: number;\n };\n\n/**\n * A percentile, or `null` when too few calls lie above it to tell it from the maximum.\n *\n * @internal\n */\nexport function tailPercentile(times: readonly number[], q: number): number | null {\n return times.length * (1 - q) >= BENCH_CONTRACT.LATENCY_TAIL_CALLS ? nearestRank(times, q) : null;\n}\n\n/**\n * Time calls one at a time: at least `LATENCY_MIN_CALLS`, then until `LATENCY_BUDGET_MS` or\n * `LATENCY_MAX_CALLS`. Skips, loudly and typed, when the median call is under\n * `LATENCY_TIMER_MULTIPLE` clock reads: then the clock, not the call, is being measured.\n *\n * @internal\n */\nexport function measureLatency(call: (i: number) => void, ns: NsClock): LatencyReport {\n const overhead = timerOverheadNs(ns);\n const budgetNs = BigInt(BENCH_CONTRACT.LATENCY_BUDGET_MS) * 1_000_000n;\n const times: number[] = [];\n const start = ns();\n let now = start;\n while (\n times.length < BENCH_CONTRACT.LATENCY_MAX_CALLS &&\n (times.length < BENCH_CONTRACT.LATENCY_MIN_CALLS || now - start < budgetNs)\n ) {\n const a = ns();\n call(times.length);\n now = ns();\n times.push(Number(now - a));\n }\n const p50 = nearestRank(times, 0.5);\n if (p50 < BENCH_CONTRACT.LATENCY_TIMER_MULTIPLE * overhead) {\n return {\n status: \"skipped\",\n reason: \"latency-below-timer-resolution\",\n calls: times.length,\n timerOverheadNs: round(overhead, 1),\n p50Ns: p50,\n };\n }\n return {\n status: \"measured\",\n calls: times.length,\n timerOverheadNs: round(overhead, 1),\n p50Ns: tailPercentile(times, 0.5),\n p95Ns: tailPercentile(times, 0.95),\n p99Ns: tailPercentile(times, 0.99),\n maxNs: Math.max(...times),\n };\n}\n","/**\n * How a package describes its benchmarks.\n *\n * A suite is a module whose default export is {@link defineSuite}'s result. It is handed the module\n * under test (`bind`) rather than importing it, so one suite file measures the base build and the\n * head build of a comparison. Every corpus is built in code (`build`): the runner never takes a data\n * file, which is how its inputs stay synthetic by construction.\n *\n * @packageDocumentation\n */\n\n/** One corpus: the inputs one pass runs over, built outside every timed region. */\nexport interface BenchCorpus<TInput> {\n /** The inputs. One pass calls the measured function once on each. */\n readonly inputs: readonly TInput[];\n /** Bytes one pass handles, when it means something. Turns operations a second into bytes a second. */\n readonly bytes?: number;\n /** Called once the process has finished with this corpus, for example to remove a temporary directory. */\n readonly teardown?: () => void;\n}\n\n/** One input size of a benchmark: small, large, or anything between. */\nexport interface BenchSize<TInput> {\n /** How a reader names this size, for example `\"1 KiB\"`. Unique within its benchmark. */\n readonly label: string;\n /** Build the corpus. Called once per process, before anything is timed. */\n readonly build: () => BenchCorpus<TInput>;\n /**\n * Whether the base against head comparison runs this size. Defaults to `true`. Set `false` for a\n * size whose processes are too slow for a pull request's time budget; a reading still measures it.\n */\n readonly compare?: boolean;\n}\n\n/** One benchmark: a function measured at one or more input sizes. */\nexport interface Benchmark<TTarget, TInput, TResult> {\n /** Unique within the suite. */\n readonly name: string;\n /** The sizes, smallest first. Each size is measured on its own. */\n readonly sizes: readonly BenchSize<TInput>[];\n /** Take the imported module under test and return the function to measure. */\n readonly bind: (target: TTarget) => (input: TInput) => TResult;\n /**\n * Reduce a result to a number summed into a sink the runner checks, so the compiler cannot delete\n * the measured call. Defaults to 1 for each result that is not `null` or `undefined`; supply it\n * when the function returns nothing. The sum must grow over a measurement, so return a positive\n * number for at least some results; a run whose sink did not grow is refused.\n */\n readonly weigh?: (result: TResult) => number;\n /** The size label the long run (is the heap flat?) uses. Defaults to the first size. */\n readonly longRun?: string;\n}\n\n/** A benchmark with its input and result types erased, as a suite holds it. */\nexport type AnyBenchmark<TTarget> = Benchmark<TTarget, unknown, unknown>;\n\n/** A suite: the benchmarks of one package. */\nexport interface BenchSuite<TTarget> {\n /** Shown in every report, for example `\"@cosyte/script-utils phi-scan\"`. */\n readonly name: string;\n /** At least one. */\n readonly benchmarks: readonly AnyBenchmark<TTarget>[];\n}\n\n/**\n * Type one benchmark. Only a type helper: it returns its argument, with the input and result types\n * erased so benchmarks of different shapes can sit in one suite.\n *\n * @example\n * ```ts\n * import { defineBenchmark } from \"@cosyte/bench\";\n *\n * const b = defineBenchmark({\n * name: \"split\",\n * sizes: [{ label: \"1 KiB\", build: () => ({ inputs: [\"a|b\".repeat(341)] }) }],\n * bind: () => (s: string) => s.split(\"|\"),\n * });\n * b.name; // => \"split\"\n * ```\n */\nexport function defineBenchmark<TTarget, TInput, TResult>(\n benchmark: Benchmark<TTarget, TInput, TResult>,\n): AnyBenchmark<TTarget> {\n return benchmark as unknown as AnyBenchmark<TTarget>;\n}\n\n/**\n * Check a suite and freeze it. Throws a `TypeError` naming the problem: an empty name, no\n * benchmarks, a repeated name or label, a benchmark with no sizes, or a `longRun` naming no size.\n *\n * @example\n * ```ts\n * import { defineBenchmark, defineSuite } from \"@cosyte/bench\";\n *\n * export default defineSuite({\n * name: \"example\",\n * benchmarks: [\n * defineBenchmark({\n * name: \"split\",\n * sizes: [{ label: \"1 KiB\", build: () => ({ inputs: [\"a|b\".repeat(341)] }) }],\n * bind: () => (s: string) => s.split(\"|\"),\n * }),\n * ],\n * });\n * ```\n */\nexport function defineSuite<TTarget>(suite: BenchSuite<TTarget>): BenchSuite<TTarget> {\n return checkSuite(suite);\n}\n\nfunction fail(message: string): never {\n throw new TypeError(`@cosyte/bench: ${message}`);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction checkName(value: unknown, what: string): string {\n if (typeof value !== \"string\" || value.trim() === \"\") fail(`${what} must be a non-empty string`);\n return value;\n}\n\n/**\n * Check that a value is a suite, as a suite module's default export must be. The messages name\n * benchmarks and sizes, never an input.\n *\n * @internal\n */\nexport function checkSuite(value: unknown): BenchSuite<unknown> {\n if (!isRecord(value)) fail(\"a suite must be an object made by defineSuite\");\n const name = checkName(value[\"name\"], \"the suite name\");\n const benchmarks = value[\"benchmarks\"];\n if (!Array.isArray(benchmarks) || benchmarks.length === 0) {\n fail(`suite \"${name}\" must have at least one benchmark`);\n }\n const seen = new Set<string>();\n for (const b of benchmarks as unknown[]) {\n if (!isRecord(b)) fail(`suite \"${name}\" holds a benchmark that is not an object`);\n const bName = checkName(b[\"name\"], `a benchmark name in suite \"${name}\"`);\n if (seen.has(bName)) fail(`suite \"${name}\" has two benchmarks named \"${bName}\"`);\n seen.add(bName);\n if (typeof b[\"bind\"] !== \"function\") fail(`benchmark \"${bName}\" has no bind function`);\n if (b[\"weigh\"] !== undefined && typeof b[\"weigh\"] !== \"function\") {\n fail(`benchmark \"${bName}\" has a weigh that is not a function`);\n }\n const sizes = b[\"sizes\"];\n if (!Array.isArray(sizes) || sizes.length === 0) fail(`benchmark \"${bName}\" has no sizes`);\n const labels = new Set<string>();\n for (const s of sizes as unknown[]) {\n if (!isRecord(s)) fail(`benchmark \"${bName}\" holds a size that is not an object`);\n const label = checkName(s[\"label\"], `a size label in benchmark \"${bName}\"`);\n if (labels.has(label)) fail(`benchmark \"${bName}\" has two sizes labelled \"${label}\"`);\n labels.add(label);\n if (typeof s[\"build\"] !== \"function\")\n fail(`size \"${label}\" of \"${bName}\" has no build function`);\n if (s[\"compare\"] !== undefined && typeof s[\"compare\"] !== \"boolean\") {\n fail(`size \"${label}\" of \"${bName}\" has a compare that is not true or false`);\n }\n }\n if (b[\"longRun\"] !== undefined && !labels.has(b[\"longRun\"] as string)) {\n fail(`benchmark \"${bName}\" names a longRun size it does not have`);\n }\n }\n return Object.freeze(value) as unknown as BenchSuite<unknown>;\n}\n\n/** One case: a benchmark at one size. */\nexport interface CaseRef {\n readonly benchmark: string;\n readonly size: string;\n}\n\n/**\n * Every case in a suite, in suite order, optionally kept to the ones `only` names. A name in\n * `only` matches a whole benchmark (`\"scan\"`) or one case (`\"scan @ 1 KiB\"`). A name that matches\n * nothing throws, so a typo cannot quietly measure less than was asked. With `forComparison`, a\n * size marked `compare: false` is left out.\n *\n * @internal\n */\nexport function listCases(\n suite: BenchSuite<unknown>,\n only: readonly string[] = [],\n forComparison = false,\n): CaseRef[] {\n const all: CaseRef[] = [];\n for (const b of suite.benchmarks) {\n for (const s of b.sizes) {\n if (!forComparison || s.compare !== false) all.push({ benchmark: b.name, size: s.label });\n }\n }\n if (only.length === 0) return all;\n for (const o of only) {\n if (!all.some((c) => c.benchmark === o || caseId(c) === o)) {\n fail(`--only \"${o}\" names no benchmark or case in suite \"${suite.name}\"`);\n }\n }\n return all.filter((c) => only.includes(c.benchmark) || only.includes(caseId(c)));\n}\n\n/**\n * How a case is named in reports and in `--only`: `benchmark @ size`.\n *\n * @internal\n */\nexport function caseId(c: CaseRef): string {\n return `${c.benchmark} @ ${c.size}`;\n}\n\n/**\n * Find a case's benchmark and size in a suite. Throws when either is missing.\n *\n * @internal\n */\nexport function findCase(\n suite: BenchSuite<unknown>,\n c: CaseRef,\n): { benchmark: AnyBenchmark<unknown>; size: BenchSize<unknown> } {\n const benchmark = suite.benchmarks.find((b) => b.name === c.benchmark);\n const size = benchmark?.sizes.find((s) => s.label === c.size);\n // No benchmark means no size either, so one check covers both.\n if (size === undefined) fail(`suite \"${suite.name}\" has no case \"${caseId(c)}\"`);\n return { benchmark: benchmark as AnyBenchmark<unknown>, size };\n}\n","/**\n * What runs inside a measuring process: one job, one case, then exit.\n *\n * One case per process is deliberate. A process that measured several functions through one harness\n * loop would make that loop's call site polymorphic, and the cost of the second case would depend on\n * which case ran first. A fresh process also carries no compiled code, heap layout or cache warmth\n * from one side of a comparison to the other, which is what makes the rounds independent.\n *\n * @packageDocumentation\n */\n\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { performance } from \"node:perf_hooks\";\nimport { pathToFileURL } from \"node:url\";\n\nimport {\n measureAllocations,\n measureLongRun,\n measurePeak,\n realProbe,\n type AllocationReport,\n type HeapProbe,\n type LongRunReport,\n type PeakReport,\n} from \"./memory.js\";\nimport {\n measureLatency,\n sampleThroughput,\n warmUp,\n type Clock,\n type LatencyReport,\n type NsClock,\n type WarmupReport,\n} from \"./measure.js\";\nimport { median } from \"./stats.js\";\nimport { checkSuite, findCase, type CaseRef } from \"./suite.js\";\n\n/**\n * One job for one measuring process.\n *\n * @internal\n */\nexport type Job =\n | {\n readonly mode: \"timing\";\n readonly suite: string;\n readonly target: string;\n readonly case: CaseRef;\n /** Also time single calls for the latency percentiles. */\n readonly latency: boolean;\n /** A known slowdown, for calibration only. Zero in a real measurement. */\n readonly inject: number;\n }\n | {\n readonly mode: \"alloc\";\n readonly suite: string;\n readonly target: string;\n readonly case: CaseRef;\n }\n | {\n readonly mode: \"heap\";\n readonly suite: string;\n readonly target: string;\n readonly case: CaseRef;\n /** Also run the long run (is the heap flat?) on this case. */\n readonly longRun: boolean;\n };\n\n/** The shape of one corpus, as reported. Sizes and counts only, never content. */\nexport interface CorpusShape {\n readonly inputsPerPass: number;\n readonly bytesPerPass: number | null;\n}\n\n/**\n * What a timing job measured.\n *\n * @internal\n */\nexport type TimingResult = CorpusShape & {\n readonly mode: \"timing\";\n readonly case: CaseRef;\n readonly warmup: WarmupReport;\n} & (\n | {\n readonly status: \"measured\";\n /** Milliseconds per operation, one per sample, in the order taken. */\n readonly samplesMsPerOp: readonly number[];\n readonly medianMsPerOp: number;\n readonly latency: LatencyReport | null;\n }\n | { readonly status: \"skipped\"; readonly reason: \"warmup-unstable\" }\n );\n\n/**\n * What a memory job measured.\n *\n * @internal\n */\nexport type MemoryResult = CorpusShape & {\n readonly mode: \"alloc\" | \"heap\";\n readonly case: CaseRef;\n readonly allocations: AllocationReport | null;\n readonly peak: PeakReport | null;\n readonly longRun: LongRunReport | null;\n};\n\n/**\n * What any job measured.\n *\n * @internal\n */\nexport type JobResult = TimingResult | MemoryResult;\n\n/**\n * The clocks and the heap a job reads. Real ones in a measuring process; scripted ones in tests.\n *\n * @internal\n */\nexport interface JobDeps {\n readonly clock: Clock;\n readonly ns: NsClock;\n readonly probe: HeapProbe | null;\n}\n\nconst realDeps = (): JobDeps => ({\n clock: () => performance.now(),\n ns: () => process.hrtime.bigint(),\n probe: realProbe(),\n});\n\nconst defaultWeigh = (result: unknown): number => (result === undefined || result === null ? 0 : 1);\n\nconst SKIPPED_NO_GC = { status: \"skipped\", reason: \"gc-unavailable\" } as const;\n\n/**\n * Run suite code, and if it throws, throw again without its message. Suite code handles the inputs,\n * and a message may quote one: a report that copied it would put an input in a CI log. The error's\n * class name and the phase are kept, which is enough to know where to look.\n *\n * @internal\n */\nexport function guard<T>(c: CaseRef, phase: string, fn: () => T): T {\n try {\n return fn();\n } catch (error) {\n const kind = error instanceof Error ? error.name : typeof error;\n throw new Error(\n `@cosyte/bench: case \"${c.benchmark} @ ${c.size}\": the suite's ${phase} threw (${kind}). ` +\n \"Its message is not printed, because it may quote an input; run the case directly to read it.\",\n { cause: error },\n );\n }\n}\n\n/**\n * The warmup's last pace, in milliseconds per operation.\n *\n * @internal\n */\nexport function msPerOp(warmup: WarmupReport, shape: CorpusShape): number {\n return (warmup.batches[warmup.batches.length - 1] ?? 0) / shape.inputsPerPass;\n}\n\n/**\n * Measure one job once its corpus is built and its code warm.\n *\n * @internal\n */\nexport function measureJob(\n job: Job,\n shape: CorpusShape,\n warmup: WarmupReport,\n pass: () => void,\n call: (i: number) => void,\n deps: JobDeps,\n): JobResult {\n const base = { case: job.case, ...shape };\n if (job.mode === \"timing\") {\n if (!warmup.stable) {\n return { mode: \"timing\", ...base, warmup, status: \"skipped\", reason: \"warmup-unstable\" };\n }\n const samples = sampleThroughput(pass, shape.inputsPerPass, deps.clock, job.inject);\n return {\n mode: \"timing\",\n ...base,\n warmup,\n status: \"measured\",\n samplesMsPerOp: samples,\n medianMsPerOp: median(samples),\n latency: job.latency ? measureLatency(call, deps.ns) : null,\n };\n }\n const probe = deps.probe;\n if (job.mode === \"alloc\") {\n return {\n mode: \"alloc\",\n ...base,\n allocations:\n probe === null ? SKIPPED_NO_GC : measureAllocations(call, probe, msPerOp(warmup, shape)),\n peak: null,\n longRun: null,\n };\n }\n let longRun: LongRunReport | null = null;\n if (job.longRun) {\n longRun =\n probe === null ? SKIPPED_NO_GC : measureLongRun(pass, shape.inputsPerPass, probe, deps.clock);\n }\n return {\n mode: \"heap\",\n ...base,\n allocations: null,\n peak: probe === null ? SKIPPED_NO_GC : measurePeak(call, probe),\n longRun,\n };\n}\n\n/**\n * Whether a job measured anything, so its sink should have moved. A job that only skipped ran no\n * measured calls after its warmup.\n *\n * @internal\n */\nexport function measuredSomething(result: JobResult): boolean {\n if (result.mode === \"timing\") return result.status === \"measured\";\n return [result.allocations, result.peak, result.longRun].some((r) => r?.status === \"measured\");\n}\n\n/**\n * Run one job: import the suite and the target, build the case's corpus, measure, tear down.\n *\n * Throws when the suite is not a suite, the case is missing, the corpus is empty, or the measured\n * loop's sink did not grow (a loop the compiler may have deleted measures nothing). Messages name\n * the case, never an input.\n *\n * @internal\n */\nexport async function runJob(job: Job, deps: JobDeps = realDeps()): Promise<JobResult> {\n const suiteModule = (await import(pathToFileURL(job.suite).href)) as { default?: unknown };\n const suite = checkSuite(suiteModule.default);\n const target: unknown = await import(pathToFileURL(job.target).href);\n const { benchmark, size } = findCase(suite, job.case);\n const corpus = guard(job.case, \"build\", () => size.build());\n try {\n const inputs = corpus.inputs;\n if (!Array.isArray(inputs) || inputs.length === 0) {\n throw new Error(\n `@cosyte/bench: case \"${job.case.benchmark} @ ${job.case.size}\" built an empty corpus`,\n );\n }\n const fn = guard(job.case, \"bind\", () => benchmark.bind(target));\n const weigh = benchmark.weigh ?? defaultWeigh;\n const sink = { value: 0 };\n const pass = () => {\n let local = 0;\n for (const input of inputs) local += weigh(fn(input));\n sink.value += local;\n };\n const call = (i: number) => {\n sink.value += weigh(fn(inputs[i % inputs.length]));\n };\n const shape: CorpusShape = {\n inputsPerPass: inputs.length,\n bytesPerPass: typeof corpus.bytes === \"number\" ? corpus.bytes : null,\n };\n const warmup = guard(job.case, \"measured function\", () => warmUp(pass, deps.clock));\n const before = sink.value;\n const result = guard(job.case, \"measured function\", () =>\n measureJob(job, shape, warmup, pass, call, deps),\n );\n if (!(sink.value > before) && measuredSomething(result)) {\n throw new Error(\n `@cosyte/bench: case \"${job.case.benchmark} @ ${job.case.size}\": the sink did not grow, so ` +\n \"the measured loop may have been deleted by the compiler, or `weigh` gave nothing positive. \" +\n \"Supply a `weigh` that returns a positive number for a result.\",\n );\n }\n return result;\n } finally {\n corpus.teardown?.();\n }\n}\n\n/**\n * The measuring process's main: read the job file, run it, write `{ ok, result }` or\n * `{ ok, error }` to the out file. Returns the exit code: 0 measured, 2 could not.\n *\n * @internal\n */\nexport async function childMain(jobFile: string, outFile: string, deps?: JobDeps): Promise<number> {\n try {\n const job = JSON.parse(readFileSync(jobFile, \"utf8\")) as Job;\n const result = await runJob(job, deps);\n writeFileSync(outFile, JSON.stringify({ ok: true, result }));\n return 0;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n writeFileSync(outFile, JSON.stringify({ ok: false, error: message }));\n return 2;\n }\n}\n","/**\n * The measuring process. `spawn.ts` starts it with a job file and an out file; it runs the job and\n * exits 0 (measured) or 2 (could not). Its logic is `childMain`, which the tests run in-process; this\n * file is the two lines around it.\n *\n * @packageDocumentation\n */\n\nimport { childMain } from \"./child.js\";\n\nprocess.exitCode = await childMain(process.argv[2] ?? \"\", process.argv[3] ?? \"\");\n"]}
1
+ {"version":3,"sources":["../src/contract.ts","../src/stats.ts","../src/memory.ts","../../test-utils/src/perf/contract.ts","../src/measure.ts","../src/suite.ts","../src/child.ts","../src/child-entry.ts"],"names":["round","msPerOp","a","b"],"mappings":"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","file":"child-entry.mjs","sourcesContent":["/**\n * The benchmark constants, frozen.\n *\n * Every value here is fixed by the performance standard,\n * `documentation/decisions/0005-the-performance-standard.md` in the `config` repo, which says for\n * each one whether it was measured or chosen, and on what basis. The warmup rule is not here: it\n * is `PERF_CONTRACT` in `@cosyte/test-utils/perf` (ADR 0001 section 2), which `measure.ts` imports\n * and the build bundles, so the scaling gate and the benchmarks warm up by one rule kept in one\n * place.\n *\n * They are not tuning knobs and cannot be overridden per package. A comparison's false alarm rate\n * was measured with these values; change one and the measurement no longer describes the tool.\n *\n * @packageDocumentation\n */\n\n/** The benchmark constants. See {@link BENCH_CONTRACT}. */\nexport interface BenchContractShape {\n /** Timed samples per case per process. Each sample is whole passes over the corpus. */\n readonly SAMPLES: number;\n /** Minimum timed length of one sample, in milliseconds. A sample runs whole passes until it is reached. */\n readonly SAMPLE_MIN_MS: number;\n /** Rounds in one base against head comparison. Each round runs both sides once, in fresh processes. */\n readonly ROUNDS: number;\n /** One-sided error allowed at each end of the comparison's interval on the median ratio. */\n readonly ALPHA: number;\n /** The smallest slowdown a comparison will call a regression, as a fraction: 0.03 is 3%. */\n readonly THRESHOLD: number;\n /** Time budget for the latency calls of one case, in milliseconds. */\n readonly LATENCY_BUDGET_MS: number;\n /** Most latency calls taken for one case. */\n readonly LATENCY_MAX_CALLS: number;\n /** Fewest latency calls taken for one case, whatever the budget says. */\n readonly LATENCY_MIN_CALLS: number;\n /** A percentile q is reported only when at least this many calls lie above it: n(1 - q) >= 10. */\n readonly LATENCY_TAIL_CALLS: number;\n /** The median call must be at least this many timer reads long, or latency is skipped. */\n readonly LATENCY_TIMER_MULTIPLE: number;\n /** Most operations in the first allocation window. Halved while a garbage collection lands in a window. */\n readonly ALLOC_WINDOW_OPS: number;\n /** The first allocation window is cut to about this many milliseconds of operations, from the warmup's pace. */\n readonly ALLOC_WINDOW_MS: number;\n /** The young generation size the allocation process runs with, in MiB, so a window rarely collects. */\n readonly ALLOC_SEMI_SPACE_MIB: number;\n /** Calls measured one by one for the peak heap. */\n readonly PEAK_CALLS: number;\n /**\n * An input under this many bytes, on average, is budgeted on its median call's total peak above\n * the empty-call floor's median, rather than its highest raw peak. On a GitHub-hosted runner the\n * peak probe costs about 3.3 KiB on a typical empty call and 28 KiB on its highest (one call, the\n * first), so the highest of a small input's calls reads the probe more than the code: 20 times a\n * 1 KiB input is spent before the code runs. At 64 KiB even the probe's highest call is about 2%\n * of a 20 times budget, so above it the raw highest peak is read as before.\n */\n readonly FLOOR_INPUT_BYTES: number;\n /** Rounds in the long run that checks the heap stays flat. */\n readonly LONG_RUN_ROUNDS: number;\n /** Rounds at the start of the long run left out of the slope, while caches and compiled code settle. */\n readonly LONG_RUN_SETTLE_ROUNDS: number;\n /** Minimum length of one long run round, in milliseconds, fixed as a number of operations by the first. */\n readonly LONG_RUN_ROUND_MS: number;\n /** A long run whose fitted growth over the measured rounds reaches this many bytes is reported as growing. */\n readonly LONG_RUN_GROWTH_BYTES: number;\n /** A garbage collection round that reclaims less than this, in bytes, counts as settled (ADR 0001 section 8). */\n readonly GC_SETTLE_BYTES: number;\n /** Consecutive settled rounds needed (ADR 0001 section 8). */\n readonly GC_SETTLE_ROUNDS: number;\n /** Most garbage collection rounds before giving up (ADR 0001 section 8). */\n readonly GC_MAX_ROUNDS: number;\n /** Fresh processes timed for the cold import, for the target and for an empty module each. */\n readonly IMPORT_RUNS: number;\n /**\n * A case whose last three warmup batches each took at least this many milliseconds a pass runs on\n * the large-case plan: every batch is one pass, so the warmup rule's aggregation cannot happen.\n */\n readonly LARGE_PASS_MS: number;\n /** Passes in each of the two windows whose medians the large-case warmup compares. */\n readonly LARGE_WARMUP_WINDOW: number;\n /** Hard cap on the large-case warmup, in milliseconds, from the start of the warmup. */\n readonly LARGE_WARMUP_MAX_MS: number;\n /**\n * The long run also runs on a benchmark's largest size whose rounds fit in this many milliseconds,\n * a round being at least one pass.\n */\n readonly LONG_RUN_MAX_MS: number;\n /** The timing comparison may become a required check when A/A false alarms are under this rate. */\n readonly REQUIRED_FALSE_ALARM_RATE: number;\n /** The confidence that the false alarm rate is under the line, for the rule above. */\n readonly REQUIRED_CONFIDENCE: number;\n}\n\n/**\n * The frozen benchmark constants.\n *\n * @example\n * ```ts\n * import { BENCH_CONTRACT } from \"@cosyte/bench\";\n *\n * BENCH_CONTRACT.ROUNDS; // => 12\n * BENCH_CONTRACT.THRESHOLD; // => 0.03\n * ```\n */\nexport const BENCH_CONTRACT: BenchContractShape = Object.freeze({\n SAMPLES: 5,\n SAMPLE_MIN_MS: 50,\n ROUNDS: 12,\n ALPHA: 0.05,\n THRESHOLD: 0.03,\n LATENCY_BUDGET_MS: 2_000,\n LATENCY_MAX_CALLS: 5_000,\n LATENCY_MIN_CALLS: 20,\n LATENCY_TAIL_CALLS: 10,\n LATENCY_TIMER_MULTIPLE: 20,\n ALLOC_WINDOW_OPS: 256,\n ALLOC_WINDOW_MS: 100,\n ALLOC_SEMI_SPACE_MIB: 64,\n PEAK_CALLS: 20,\n FLOOR_INPUT_BYTES: 64 * 1024,\n LONG_RUN_ROUNDS: 30,\n LONG_RUN_SETTLE_ROUNDS: 5,\n LONG_RUN_ROUND_MS: 100,\n LONG_RUN_GROWTH_BYTES: 64 * 1024,\n GC_SETTLE_BYTES: 64 * 1024,\n GC_SETTLE_ROUNDS: 2,\n GC_MAX_ROUNDS: 7,\n IMPORT_RUNS: 20,\n LARGE_PASS_MS: 50,\n LARGE_WARMUP_WINDOW: 5,\n LARGE_WARMUP_MAX_MS: 30_000,\n LONG_RUN_MAX_MS: 60_000,\n REQUIRED_FALSE_ALARM_RATE: 0.01,\n REQUIRED_CONFIDENCE: 0.95,\n});\n","/**\n * The statistics the benchmarks report and judge by. Plain arithmetic with no dependencies, each\n * function small enough to check by hand against the worked numbers in its tests.\n *\n * @packageDocumentation\n */\n\n/**\n * A sorted copy, ascending. The input is not touched.\n *\n * @internal\n */\nexport function sortedCopy(xs: readonly number[]): number[] {\n return [...xs].sort((a, b) => a - b);\n}\n\n/**\n * The median: the middle value, or the mean of the two middle values. `NaN` for an empty vector,\n * so an empty measurement can never read as a fast one.\n *\n * @internal\n */\nexport function median(xs: readonly number[]): number {\n const s = sortedCopy(xs);\n const mid = Math.floor(s.length / 2);\n // An even length of at least 2 has both middle values. An empty vector takes the even branch and\n // reads two missing values, which is `NaN`: the empty answer, by arithmetic rather than a check.\n return s.length % 2 === 1\n ? (s[mid] as number)\n : ((s[mid - 1] as number) + (s[mid] as number)) / 2;\n}\n\n/**\n * A percentile by nearest rank: the smallest value with at least a fraction `q` of the values at or\n * below it. No interpolation, so every reported percentile is a value that was observed.\n *\n * @internal\n */\nexport function nearestRank(xs: readonly number[], q: number): number {\n if (xs.length === 0) return Number.NaN;\n const s = sortedCopy(xs);\n const rank = Math.max(1, Math.ceil(q * s.length));\n return s[rank - 1] as number;\n}\n\n/**\n * `P(X <= k)` for `X ~ Binomial(n, p)`, summed in log space so it stays finite for large `n`, for\n * `0 < p < 1` (every caller's range). A negative `k` sums nothing and is 0; a `k` past `n` sums all\n * of it and is 1.\n *\n * @internal\n */\nexport function binomialCdf(k: number, n: number, p: number): number {\n const logP = Math.log(p);\n const logQ = Math.log1p(-p);\n let logCoef = 0;\n let total = 0;\n for (let i = 0; i <= Math.min(k, n); i++) {\n if (i > 0) logCoef += Math.log(n - i + 1) - Math.log(i);\n total += Math.exp(logCoef + i * logP + (n - i) * logQ);\n }\n return Math.min(1, total);\n}\n\n/**\n * The sign test's rank for an interval on the median of `n` values: the largest `k` such that the\n * `k`-th smallest value lies below the true median with probability at most `alpha`, that is\n * `P(Binomial(n, 1/2) <= k - 1) <= alpha`. `0` when no rank qualifies (too few values).\n *\n * @internal\n */\nexport function signTestRank(n: number, alpha: number): number {\n let k = 0;\n while (k < n && binomialCdf(k, n, 0.5) <= alpha) k++;\n return k;\n}\n\n/** An interval on a median, from the sign test. */\nexport interface MedianInterval {\n /** The `rank`-th smallest value. */\n readonly low: number;\n /** The `rank`-th largest value. */\n readonly high: number;\n /** Which order statistics bound it. */\n readonly rank: number;\n /** The chance that each end alone misses the median, exactly: `P(Binomial(n, 1/2) <= rank - 1)`. */\n readonly tail: number;\n}\n\n/**\n * The distribution-free interval on the median of independent values: the `k`-th smallest and the\n * `k`-th largest, with `k` from {@link signTestRank}. It assumes nothing about the shape of the\n * noise, which on a shared runner is one-sided and heavy-tailed. `null` when there are too few\n * values for any rank to meet `alpha`.\n *\n * @internal\n */\nexport function signTestInterval(xs: readonly number[], alpha: number): MedianInterval | null {\n const rank = signTestRank(xs.length, alpha);\n if (rank === 0) return null;\n const s = sortedCopy(xs);\n // `rank` is at least 1 and at most `n`, so both order statistics exist.\n return {\n low: s[rank - 1] as number,\n high: s[s.length - rank] as number,\n rank,\n tail: binomialCdf(rank - 1, xs.length, 0.5),\n };\n}\n\n/**\n * The exact (Clopper-Pearson) one-sided upper confidence bound on a rate, from `k` events in `n`\n * trials: the largest rate `p` at which seeing `k` or fewer is still at least `1 - confidence`\n * likely. With no events this is `1 - (1 - confidence)^(1/n)`, about `3 / n` at 95%.\n *\n * @internal\n */\nexport function rateUpperBound(k: number, n: number, confidence: number): number {\n // No trials say nothing. Every trial an event bisects to 1 on its own.\n if (n === 0) return 1;\n const alpha = 1 - confidence;\n let lo = k / n;\n let hi = 1;\n // Stryker disable next-line EqualityOperator: a 101st halving moves the bound by under 2^-100, below a double's resolution.\n for (let i = 0; i < 100; i++) {\n const mid = (lo + hi) / 2;\n // Stryker disable next-line EqualityOperator: `>` and `>=` differ only where the tail equals alpha exactly, which bisection over doubles does not land on.\n if (binomialCdf(k, n, mid) > alpha) lo = mid;\n else hi = mid;\n }\n return hi;\n}\n\n/**\n * The fewest trials, all without an event, whose {@link rateUpperBound} is under `rate`: how long an\n * A/A calibration has to run before a clean record can show a false alarm rate under `rate`.\n *\n * @internal\n */\nexport function trialsForZeroEvents(rate: number, confidence: number): number {\n return Math.ceil(Math.log(1 - confidence) / Math.log1p(-rate));\n}\n\n/**\n * The Theil-Sen slope: the median of the slopes between every pair of points with distinct `x`.\n * One wild point moves it little, which matters for a heap reading that a late collection can\n * spike. `NaN` with fewer than two distinct `x`.\n *\n * @internal\n */\nexport function theilSenSlope(xs: readonly number[], ys: readonly number[]): number {\n // Pairs only exist where both vectors do; a caller passes vectors of one length.\n const slopes: number[] = [];\n xs.forEach((xi, i) => {\n for (let j = i + 1; j < xs.length; j++) {\n const dx = (xs[j] as number) - xi;\n if (dx !== 0) slopes.push(((ys[j] as number) - (ys[i] as number)) / dx);\n }\n });\n return median(slopes);\n}\n\n/**\n * Round to a number of decimal places, for readable reports. The full values stay in\n * the sample vectors.\n *\n * @internal\n */\nexport function round(n: number, places = 4): number {\n const f = 10 ** places;\n return Math.round(n * f) / f;\n}\n","/**\n * The memory measurements: allocations per operation, peak heap per call, and whether the heap stays\n * flat over a long run. Each runs in a measuring process started with `--expose-gc`, one case per\n * process.\n *\n * They read the heap through a {@link HeapProbe}, so their tests can drive them with a scripted heap.\n * The real probe follows ADR 0001 section 8: a zero-argument `gc()` only, settled by rule rather than\n * by a count. The measuring process also runs with `--no-concurrent-array-buffer-sweeping`, so a\n * collection frees the ArrayBuffers it found dead before it ends, and the external memory a\n * collection reports before and after is what it held and what it kept.\n *\n * @packageDocumentation\n */\n\nimport { getHeapStatistics, GCProfiler } from \"node:v8\";\n\nimport { BENCH_CONTRACT } from \"./contract.js\";\nimport type { Clock } from \"./measure.js\";\nimport { median, round, theilSenSlope } from \"./stats.js\";\n\n/**\n * What the memory measurements read the heap with.\n *\n * @internal\n */\nexport interface HeapProbe {\n /** A full collection. The real probe calls `gc()` with no argument, the only form ADR 0001 allows. */\n readonly gc: () => void;\n /** Bytes in use on the V8 heap. */\n readonly usedHeap: () => number;\n /** Bytes held outside the V8 heap by JavaScript objects (Buffers, ArrayBuffers). */\n readonly external: () => number;\n /**\n * Run `fn` and report the collections that ran during it, and the most heap in use just before any\n * of them. The heap only shrinks at a collection, so its high point is either there or at the end.\n */\n readonly profile: (fn: () => void) => ProfileReport;\n}\n\n/**\n * The collections that ran during a profiled call.\n *\n * @internal\n */\nexport interface ProfileReport {\n readonly gcs: number;\n /** The most V8 heap in use just before any of them; 0 with none. */\n readonly peakBeforeGc: number;\n /** The most external memory (Buffers, ArrayBuffers) just before any of them; 0 with none. */\n readonly peakExternalBeforeGc: number;\n /** The most of the two together just before any of them; 0 with none. */\n readonly peakTotalBeforeGc: number;\n /** V8 heap bytes the collections reclaimed: in use before each, less in use after it, summed. */\n readonly reclaimedHeap: number;\n /** External bytes the collections reclaimed, the same way. */\n readonly reclaimedExternal: number;\n}\n\n/**\n * The real probe, or `null` when the process was not started with `--expose-gc`.\n *\n * @internal\n */\nexport function realProbe(): HeapProbe | null {\n const gc: unknown = (globalThis as { gc?: unknown }).gc;\n if (typeof gc !== \"function\") return null;\n return {\n gc: () => {\n (gc as () => void)();\n },\n usedHeap: () => getHeapStatistics().used_heap_size,\n external: () => process.memoryUsage().external,\n profile: (fn) => {\n const profiler = new GCProfiler();\n profiler.start();\n fn();\n const stats = profiler.stop().statistics;\n const report = {\n gcs: stats.length,\n peakBeforeGc: 0,\n peakExternalBeforeGc: 0,\n peakTotalBeforeGc: 0,\n reclaimedHeap: 0,\n reclaimedExternal: 0,\n };\n for (const s of stats) {\n const before = s.beforeGC.heapStatistics;\n const after = s.afterGC.heapStatistics;\n report.peakBeforeGc = Math.max(report.peakBeforeGc, before.usedHeapSize);\n report.peakExternalBeforeGc = Math.max(report.peakExternalBeforeGc, before.externalMemory);\n report.peakTotalBeforeGc = Math.max(\n report.peakTotalBeforeGc,\n before.usedHeapSize + before.externalMemory,\n );\n report.reclaimedHeap += before.usedHeapSize - after.usedHeapSize;\n report.reclaimedExternal += before.externalMemory - after.externalMemory;\n }\n return report;\n },\n };\n}\n\n/** The settled heap, or the news that it would not settle. */\nexport interface SettleResult {\n readonly settled: boolean;\n readonly rounds: number;\n readonly heap: number;\n}\n\n/**\n * Collect until two rounds in a row each reclaim under `GC_SETTLE_BYTES`, at most `GC_MAX_ROUNDS`\n * rounds (ADR 0001 section 8).\n *\n * @internal\n */\nexport function settle(probe: HeapProbe): SettleResult {\n let quiet = 0;\n let before = probe.usedHeap();\n for (let round = 1; round <= BENCH_CONTRACT.GC_MAX_ROUNDS; round++) {\n probe.gc();\n const after = probe.usedHeap();\n quiet = before - after < BENCH_CONTRACT.GC_SETTLE_BYTES ? quiet + 1 : 0;\n before = after;\n if (quiet >= BENCH_CONTRACT.GC_SETTLE_ROUNDS)\n return { settled: true, rounds: round, heap: after };\n }\n return { settled: false, rounds: BENCH_CONTRACT.GC_MAX_ROUNDS, heap: before };\n}\n\n/**\n * Why a memory measurement has no number. `allocation-window-gc` is no longer written: a case whose\n * every window collects is now counted with its collections. It stays here because documents\n * written before carry it.\n */\nexport type MemorySkipReason = \"gc-unavailable\" | \"gc-unsettled\" | \"allocation-window-gc\";\n\n/**\n * How allocations were counted. `no-gc-window`: the heap's growth over windows in which no\n * collection ran. `gc-accounted`: every window collected, so the growth over each window plus what\n * its collections reclaimed.\n */\nexport type AllocationMethod = \"no-gc-window\" | \"gc-accounted\";\n\n/** Allocations per operation. */\nexport type AllocationReport =\n | {\n readonly status: \"measured\";\n /** How they were counted. Absent in a document written before it was recorded: `no-gc-window`. */\n readonly method?: AllocationMethod;\n /** Collections that ran in the two windows counted: 0 for `no-gc-window`. */\n readonly collections?: number;\n /** The smaller of the two windows, in operations. */\n readonly windowOps: number;\n /** Bytes allocated on the V8 heap per operation. */\n readonly heapBytesPerOp: number;\n /** Bytes allocated outside it (Buffers, ArrayBuffers) per operation. */\n readonly externalBytesPerOp: number;\n }\n | { readonly status: \"skipped\"; readonly reason: MemorySkipReason };\n\n/**\n * Allocations per operation, from the heap's growth across two windows of `n` and `2n` operations\n * in which no collection ran: `(growth(2n) - growth(n)) / n`, so the fixed cost of reading the heap\n * cancels. The first `n` is about `ALLOC_WINDOW_MS` of operations at `msPerOp`, at most\n * `ALLOC_WINDOW_OPS`. A collection in either window halves `n` and tries again. The process runs with a large\n * young generation (`ALLOC_SEMI_SPACE_MIB`) so that a window rarely collects.\n *\n * When a collection lands in every window, as it does when one call allocates more than the young\n * generation holds, the first and largest pair of windows is counted with its collections instead\n * (`gc-accounted`): what a window allocated is its growth plus what its collections reclaimed, read\n * from each collection's heap before and after it. No further calls are made. On a case whose windows\n * do not collect the two counts are the same number, since nothing was reclaimed.\n *\n * @internal\n */\nexport function measureAllocations(\n call: (i: number) => void,\n probe: HeapProbe,\n msPerOp: number,\n): AllocationReport {\n const window = (ops: number) => {\n const s = settle(probe);\n const h0 = probe.usedHeap();\n const e0 = probe.external();\n const p = probe.profile(() => {\n for (let i = 0; i < ops; i++) call(i);\n });\n return {\n settled: s.settled,\n gcs: p.gcs,\n heap: probe.usedHeap() - h0 + p.reclaimedHeap,\n ext: probe.external() - e0 + p.reclaimedExternal,\n };\n };\n const perOp = (\n method: AllocationMethod,\n n: number,\n a: ReturnType<typeof window>,\n b: ReturnType<typeof window>,\n ): AllocationReport => ({\n status: \"measured\",\n method,\n collections: a.gcs + b.gcs,\n windowOps: n,\n heapBytesPerOp: round((b.heap - a.heap) / n, 1),\n externalBytesPerOp: round((b.ext - a.ext) / n, 1),\n });\n const paced = Math.floor(BENCH_CONTRACT.ALLOC_WINDOW_MS / msPerOp);\n const first = Math.max(1, Math.min(BENCH_CONTRACT.ALLOC_WINDOW_OPS, paced));\n let largest: [ReturnType<typeof window>, ReturnType<typeof window>] | null = null;\n for (let n = first; n >= 1; n = Math.floor(n / 2)) {\n const a = window(n);\n const b = window(2 * n);\n if (!a.settled || !b.settled) return { status: \"skipped\", reason: \"gc-unsettled\" };\n if (a.gcs === 0 && b.gcs === 0) return perOp(\"no-gc-window\", n, a, b);\n largest ??= [a, b];\n }\n // The loop ran at least once (`first` is at least 1) and every pass that reached here collected.\n const [a, b] = largest as [ReturnType<typeof window>, ReturnType<typeof window>];\n return perOp(\"gc-accounted\", first, a, b);\n}\n\n/** Peak memory per call: the V8 heap, external memory, and the two together. */\nexport type PeakReport =\n | {\n readonly status: \"measured\";\n readonly calls: number;\n /** The highest V8 heap peak of any call, in bytes above the settled heap before it. */\n readonly peakHeapBytes: number;\n /** The median call's V8 heap peak. */\n readonly medianPeakHeapBytes: number;\n /**\n * The highest external peak of any call (Buffers, ArrayBuffers), above the external memory\n * held before it. Absent in a document written before external memory was read.\n */\n readonly peakExternalBytes?: number;\n /** The median call's external peak. */\n readonly medianPeakExternalBytes?: number;\n /**\n * The highest peak of the V8 heap and external memory together, above the two before the call:\n * the figure a memory budget reads. Not the sum of the two peaks above, which need not come at\n * one moment.\n */\n readonly peakTotalBytes?: number;\n /** The median call's peak of the two together. */\n readonly medianPeakTotalBytes?: number;\n /**\n * Each figure above, less the same figure of the run's empty-call floor (its median), and\n * never below zero. Absent when the run measured no floor, and in a document written before it.\n * See {@link aboveFloor}.\n */\n readonly peakHeapAboveFloorBytes?: number;\n readonly medianPeakHeapAboveFloorBytes?: number;\n readonly peakExternalAboveFloorBytes?: number;\n readonly medianPeakExternalAboveFloorBytes?: number;\n readonly peakTotalAboveFloorBytes?: number;\n readonly medianPeakTotalAboveFloorBytes?: number;\n }\n | { readonly status: \"skipped\"; readonly reason: MemorySkipReason };\n\n/**\n * The peak memory of single calls: settle, call once, and take the higher of the memory in use just\n * before any collection during the call and at its end, less what was in use before it, for the V8\n * heap, for external memory, and for the two together. Each only shrinks at a collection (the\n * process frees dead ArrayBuffers within the collection), so its high point is at one of those\n * moments. Run with V8's default heap sizing, so the collections that shape the peak are the ones a\n * user's process has.\n *\n * @internal\n */\nexport function measurePeak(call: (i: number) => void, probe: HeapProbe): PeakReport {\n const heap: number[] = [];\n const external: number[] = [];\n const total: number[] = [];\n for (let i = 0; i < BENCH_CONTRACT.PEAK_CALLS; i++) {\n const s = settle(probe);\n if (!s.settled) return { status: \"skipped\", reason: \"gc-unsettled\" };\n const e0 = probe.external();\n const p = probe.profile(() => {\n call(i);\n });\n const h1 = probe.usedHeap();\n const e1 = probe.external();\n heap.push(Math.max(p.peakBeforeGc, h1) - s.heap);\n external.push(Math.max(p.peakExternalBeforeGc, e1) - e0);\n total.push(Math.max(p.peakTotalBeforeGc, h1 + e1) - (s.heap + e0));\n }\n return {\n status: \"measured\",\n calls: heap.length,\n peakHeapBytes: Math.max(...heap),\n medianPeakHeapBytes: median(heap),\n peakExternalBytes: Math.max(...external),\n medianPeakExternalBytes: median(external),\n peakTotalBytes: Math.max(...total),\n medianPeakTotalBytes: median(total),\n };\n}\n\n/**\n * A case's peak less the empty-call floor. The probe itself costs the same few tens of KiB whatever\n * it measures (a settle, a profiler, a collection record), so the floor is the median of an empty\n * call's peaks and is taken off every figure of the case, the highest and the median alike, never\n * going below zero. A peak or floor that was skipped, or a floor lacking a figure, returns the peak\n * as it is.\n *\n * @internal\n */\nexport function aboveFloor(peak: PeakReport, floor: PeakReport | null): PeakReport {\n if (peak.status !== \"measured\" || floor === null || floor.status !== \"measured\") return peak;\n const less = (value: number | undefined, base: number | undefined): number | undefined =>\n value === undefined || base === undefined ? undefined : Math.max(0, value - base);\n const out = {\n ...peak,\n peakHeapAboveFloorBytes: less(peak.peakHeapBytes, floor.medianPeakHeapBytes),\n medianPeakHeapAboveFloorBytes: less(peak.medianPeakHeapBytes, floor.medianPeakHeapBytes),\n peakExternalAboveFloorBytes: less(peak.peakExternalBytes, floor.medianPeakExternalBytes),\n medianPeakExternalAboveFloorBytes: less(\n peak.medianPeakExternalBytes,\n floor.medianPeakExternalBytes,\n ),\n peakTotalAboveFloorBytes: less(peak.peakTotalBytes, floor.medianPeakTotalBytes),\n medianPeakTotalAboveFloorBytes: less(peak.medianPeakTotalBytes, floor.medianPeakTotalBytes),\n };\n // JSON drops an undefined; drop it here too so a document and its in-memory form are alike.\n return Object.fromEntries(\n Object.entries(out).filter(([, v]) => v !== undefined),\n ) as unknown as PeakReport;\n}\n\n/** Whether the heap stayed flat over a long run. */\nexport type LongRunReport =\n | {\n readonly status: \"measured\";\n /** `growing` when the fitted growth reaches `LONG_RUN_GROWTH_BYTES`. */\n readonly verdict: \"flat\" | \"growing\";\n /** Operations in all. */\n readonly ops: number;\n /** Operations per round, fixed by the first round. */\n readonly opsPerRound: number;\n /** The settled heap after each round, in bytes. */\n readonly settledHeap: readonly number[];\n /** Theil-Sen slope of the settled heap against operations, after the settling rounds. */\n readonly slopeBytesPerOp: number;\n /** The slope times the operations it was fitted over: the growth the verdict reads. */\n readonly growthBytes: number;\n /** The smallest leak, in bytes per operation, this run could call growing: the bound over the span. */\n readonly detectableBytesPerOp: number;\n }\n | { readonly status: \"skipped\"; readonly reason: MemorySkipReason };\n\n/**\n * Run the case for `LONG_RUN_ROUNDS` rounds, settle the heap after each, and fit the settled heap\n * against the operations run so far. A leak of `b` bytes an operation shows as a slope near `b`;\n * a heap that only grows while caches and compiled code settle is left out by\n * `LONG_RUN_SETTLE_ROUNDS`. The verdict reads the growth the slope implies over the fitted rounds,\n * not the slope alone: a settled heap reading moves by about a kilobyte with nothing running\n * (ADR 0001 section 8), so a run of few operations cannot tell a small slope from that.\n *\n * @internal\n */\nexport function measureLongRun(\n pass: () => void,\n opsPerPass: number,\n probe: HeapProbe,\n clock: Clock,\n): LongRunReport {\n const t0 = clock();\n let passesPerRound = 0;\n do {\n pass();\n passesPerRound++;\n } while (clock() - t0 < BENCH_CONTRACT.LONG_RUN_ROUND_MS);\n const opsPerRound = passesPerRound * opsPerPass;\n // Each round is the operations run so far and the heap settled after them. The sizing round\n // above is the first.\n const rounds: { ops: number; heap: number }[] = [];\n let ops = opsPerRound;\n for (;;) {\n const s = settle(probe);\n if (!s.settled) return { status: \"skipped\", reason: \"gc-unsettled\" };\n rounds.push({ ops, heap: s.heap });\n if (rounds.length === BENCH_CONTRACT.LONG_RUN_ROUNDS) break;\n for (let p = 0; p < passesPerRound; p++) pass();\n ops += opsPerRound;\n }\n const skip = BENCH_CONTRACT.LONG_RUN_SETTLE_ROUNDS;\n const fitted = rounds.slice(skip);\n const slope = theilSenSlope(\n fitted.map((r) => r.ops),\n fitted.map((r) => r.heap),\n );\n const span = (BENCH_CONTRACT.LONG_RUN_ROUNDS - 1 - skip) * opsPerRound;\n const growth = slope * span;\n return {\n status: \"measured\",\n // Stryker disable next-line EqualityOperator: growth is a fitted slope times 24 rounds, so exactly 65,536 bytes is not reached in floating point (tried in memory.test.ts history).\n verdict: growth >= BENCH_CONTRACT.LONG_RUN_GROWTH_BYTES ? \"growing\" : \"flat\",\n ops: BENCH_CONTRACT.LONG_RUN_ROUNDS * opsPerRound,\n opsPerRound,\n settledHeap: rounds.map((r) => r.heap),\n slopeBytesPerOp: round(slope, 4),\n growthBytes: Math.round(growth),\n detectableBytesPerOp: round(BENCH_CONTRACT.LONG_RUN_GROWTH_BYTES / span, 4),\n };\n}\n","/**\n * The frozen measurement constants.\n *\n * Every value here is fixed by **ADR 0001: the performance measurement contract**\n * (`documentation/decisions/0001-perf-measurement-contract.md` in the `config` repo), which in turn\n * derives them from the PERF-P0 calibration: 3,200 4N-vs-N ratios on a linear workload, 320 on a\n * deliberately O(n²) one, across two runner classes on Node 22.23.1 / V8 12.4.\n *\n * They are **not tuning knobs and are deliberately not overridable per package.** The ceiling was\n * set from the worst false alarm in that population and the weakest real signal at `hl7`'s own\n * fixture size, so it is conditional on the estimator, the sampling shape *and* the warmup rule:\n * changing any one of them moves the operating point the ceiling was measured against. The ADR's\n * review triggers are the process for changing them.\n *\n * @packageDocumentation\n */\n\n/**\n * The measurement constants, frozen. See {@link PERF_CONTRACT}.\n */\nexport interface PerfContractShape {\n /**\n * Upper bound on `min(scaled) / min(base)`. **8**: above the worst false alarm in 3,200 samples\n * (6.649, `min`, container leg) and below the weakest real O(n²) signal measured at `hl7`'s own\n * fixture size (8.84). Margins 1.20× / 1.10×. Measured.\n */\n readonly RATIO_CEILING: number;\n /**\n * Lower bound on the same ratio. **1.5**: below all 3,200 samples (worst 1.702) and above the ≈1\n * that a same-workload-on-both-phases bug produces. Margin 1.13×, the thinnest in the contract.\n * Measured.\n */\n readonly RATIO_FLOOR: number;\n /**\n * Timed repetitions per phase. **5**, measured by construction: the whole calibration was taken at\n * 5 reps, and `min` over a different rep count is a different statistic.\n */\n readonly REPS: number;\n /**\n * The multiplier between the base phase and the scaled phase. **4×**, measured by construction:\n * every ratio in the dataset is a 4× step, so the ideal linear ratio is 4.\n */\n readonly SCALE_STEP: number;\n /**\n * Floor on the base phase's `min` sample, in milliseconds. **4**: the fastest base phase in all\n * 3,200 samples was 4.14 ms; below that the ceiling is extrapolation, so the gate refuses to\n * answer. Measured.\n */\n readonly MIN_PHASE_MS: number;\n /**\n * Minimum total warmup time before the stability rule may fire, in milliseconds. **500**: one\n * further P0 trial (136 to 294 ms of the same workload) brought rep-to-rep drift from as much as\n * 1.23× inside ±5%; 500 ms is 1.7 to 3.7× that. Measured.\n */\n readonly WARMUP_MIN_MS: number;\n /**\n * Minimum timed duration of one warmup batch, in milliseconds. **50**: judgement forced by\n * measurement: a single ~4 to 9 ms pass is not stable enough to satisfy ±5% three times running\n * (1 to 92% of warm phases do, by axis), and a 50 ms batch aggregates 5 to 12 passes. Aggregation is\n * what shrinks that jitter, so the batch length is load-bearing rather than a free choice.\n */\n readonly WARMUP_BATCH_MS: number;\n /**\n * Hard cap on total warmup, in milliseconds. **5 000**: judgement, no measured basis. Bounds the\n * fleet cost and leaves ~100 batches of headroom, so the cap is reached only by a genuinely\n * unsettled runtime.\n */\n readonly WARMUP_MAX_MS: number;\n /**\n * Relative tolerance for the warmup stability rule, as a fraction. **0.05** (±5%): judgement\n * anchored on P0's rep1/rep5 ratio inside a warm 5-rep phase (0.96 to 1.05× GitHub, 0.97 to 1.02×\n * container), which is a different statistic at a different granularity.\n */\n readonly WARMUP_STABLE_TOL: number;\n /**\n * How many consecutive batches must lie inside the tolerance. **3**: two consecutive can be met\n * by a coincidental pair on a heavy-tailed distribution; a third costs 50 ms against a 5 s cap.\n */\n readonly WARMUP_STABLE_BATCHES: number;\n}\n\n/**\n * The frozen measurement constants, as decided by ADR 0001.\n *\n * Read them, print them in your own diagnostics, assert against them, but do not expect a knob to\n * override them. A package that cannot satisfy the contract at its fixture sizes has a fixture-size\n * problem, and {@link assertScalingGateFires} is what turns that into a build failure.\n *\n * @example\n * ```ts\n * import { PERF_CONTRACT } from \"@cosyte/test-utils/perf\";\n *\n * PERF_CONTRACT.RATIO_CEILING; // => 8\n * PERF_CONTRACT.SCALE_STEP; // => 4\n * ```\n */\nexport const PERF_CONTRACT: PerfContractShape = Object.freeze({\n RATIO_CEILING: 8,\n RATIO_FLOOR: 1.5,\n REPS: 5,\n SCALE_STEP: 4,\n MIN_PHASE_MS: 4,\n WARMUP_MIN_MS: 500,\n WARMUP_BATCH_MS: 50,\n WARMUP_MAX_MS: 5_000,\n WARMUP_STABLE_TOL: 0.05,\n WARMUP_STABLE_BATCHES: 3,\n});\n","/**\n * The timing primitives: the warmup, the throughput samples and the latency calls. They run inside\n * a measuring process, one case per process, and take their clocks as arguments so their tests can\n * drive them with exact arithmetic instead of a machine's noise.\n *\n * @packageDocumentation\n */\n\n// The one source of the warmup rule. A relative import of the scaling gate's own contract file, not\n// of the published `@cosyte/test-utils/perf`, so the build bundles it into `dist` and this package\n// keeps no runtime dependency (that one would bring `fast-check` in as a peer). A change to the rule\n// there changes this package's build, so it needs a changeset that bumps this package too.\nimport { PERF_CONTRACT } from \"../../test-utils/src/perf/contract.js\";\nimport { BENCH_CONTRACT } from \"./contract.js\";\nimport { median, nearestRank, round } from \"./stats.js\";\n\n/**\n * The warmup rule, read from `PERF_CONTRACT` in `@cosyte/test-utils/perf` (ADR 0001 section 2) and\n * renamed, so the scaling gate and the benchmarks warm up by one rule kept in one place.\n *\n * @internal\n */\nexport const WARMUP = Object.freeze({\n MIN_MS: PERF_CONTRACT.WARMUP_MIN_MS,\n BATCH_MS: PERF_CONTRACT.WARMUP_BATCH_MS,\n MAX_MS: PERF_CONTRACT.WARMUP_MAX_MS,\n STABLE_TOL: PERF_CONTRACT.WARMUP_STABLE_TOL,\n STABLE_BATCHES: PERF_CONTRACT.WARMUP_STABLE_BATCHES,\n});\n\n/**\n * A millisecond clock. `performance.now` in a real run.\n *\n * @internal\n */\nexport type Clock = () => number;\n\n/**\n * A nanosecond clock. `process.hrtime.bigint` in a real run.\n *\n * @internal\n */\nexport type NsClock = () => bigint;\n\n/**\n * Which warmup rule a process ended on. `standard` is ADR 0001 section 2's. `large` is the\n * large-case plan, for a case whose every batch is a single pass: see {@link warmUp}.\n */\nexport type WarmupPlan = \"standard\" | \"large\";\n\n/** What the warmup did. */\nexport interface WarmupReport {\n /**\n * Whether the warmup settled: on the standard plan, three batches in a row within 5% of their\n * median before 5 s ran out; on the large-case plan, two windows of passes with medians within 5%\n * of each other before 30 s ran out.\n */\n readonly stable: boolean;\n /** Wall time spent warming, in milliseconds. */\n readonly elapsedMs: number;\n /** Passes over the corpus. */\n readonly passes: number;\n /** Each batch's milliseconds per pass. On the large-case plan every batch after the switch is one pass. */\n readonly batches: readonly number[];\n /** The rule the warmup ended on. */\n readonly plan: WarmupPlan;\n}\n\n/**\n * Whether the last `STABLE_BATCHES` batches all lie within `STABLE_TOL` of their median.\n *\n * @internal\n */\nexport function batchesAreStable(batches: readonly number[]): boolean {\n if (batches.length < WARMUP.STABLE_BATCHES) return false;\n const window = batches.slice(-WARMUP.STABLE_BATCHES);\n const mid = median(window);\n // A zero median makes every relative distance `NaN`, which no tolerance accepts: refused.\n return window.every((b) => Math.abs(b - mid) / mid <= WARMUP.STABLE_TOL);\n}\n\n/**\n * Whether a case is large: its last `STABLE_BATCHES` batches each took at least `LARGE_PASS_MS` a\n * pass. A batch ends on the pass that reaches 50 ms, so such a batch is a single pass, and the\n * aggregation that ADR 0001 measured as what brings batch-to-batch jitter inside 5% cannot happen.\n *\n * @internal\n */\nexport function isLargeCase(batches: readonly number[]): boolean {\n if (batches.length < WARMUP.STABLE_BATCHES) return false;\n return batches.slice(-WARMUP.STABLE_BATCHES).every((b) => b >= BENCH_CONTRACT.LARGE_PASS_MS);\n}\n\n/**\n * The large-case plan's stability test: the median of the last `LARGE_WARMUP_WINDOW` passes lies\n * within `STABLE_TOL` of the median of the window before it. A median of 5 passes is moved by\n * neither of up to two stalled passes, where the standard rule needs every one of three to agree.\n *\n * @internal\n */\nexport function windowsAreStable(passes: readonly number[]): boolean {\n const w = BENCH_CONTRACT.LARGE_WARMUP_WINDOW;\n if (passes.length < 2 * w) return false;\n const last = median(passes.slice(-w));\n const before = median(passes.slice(-2 * w, -w));\n // A zero median makes the relative distance `NaN`, which no tolerance accepts: refused.\n return Math.abs(last - before) / before <= WARMUP.STABLE_TOL;\n}\n\n/**\n * Warm up by time, with a stability rule, never by a fixed count (ADR 0001 section 2): batches of\n * whole passes of at least 50 ms, at least 500 ms in all, until three batches in a row lie within\n * 5% of their median, or 5 s pass without that.\n *\n * A case whose passes are too long for that rule moves to the large-case plan: when 500 ms have\n * passed, the standard rule has not settled, and the last three batches were each a single pass of\n * at least `LARGE_PASS_MS` ({@link isLargeCase}). From there the warmup runs one pass at a time\n * until the median of the last `LARGE_WARMUP_WINDOW` passes is within 5% of the median of the\n * window before it ({@link windowsAreStable}), which takes at least twice the window in passes, or\n * until `LARGE_WARMUP_MAX_MS` from the start pass without that. A case that settles by the standard\n * rule never moves, so every case whose batches hold several passes warms up exactly as before.\n *\n * @internal\n */\nexport function warmUp(pass: () => void, clock: Clock): WarmupReport {\n const start = clock();\n const batches: number[] = [];\n let passes = 0;\n for (;;) {\n const b0 = clock();\n let inBatch = 0;\n let batchMs: number;\n do {\n pass();\n inBatch++;\n batchMs = clock() - b0;\n } while (batchMs < WARMUP.BATCH_MS);\n passes += inBatch;\n batches.push(batchMs / inBatch);\n const elapsedMs = clock() - start;\n if (elapsedMs >= WARMUP.MIN_MS) {\n if (batchesAreStable(batches)) {\n return { stable: true, elapsedMs, passes, batches, plan: \"standard\" };\n }\n if (isLargeCase(batches)) return warmUpLarge(pass, clock, start, batches, passes);\n }\n if (elapsedMs >= WARMUP.MAX_MS) {\n return { stable: false, elapsedMs, passes, batches, plan: \"standard\" };\n }\n }\n}\n\n/** The large-case plan, continued from the standard warmup's batches. See {@link warmUp}. */\nfunction warmUpLarge(\n pass: () => void,\n clock: Clock,\n start: number,\n batches: number[],\n passesSoFar: number,\n): WarmupReport {\n let passes = passesSoFar;\n for (;;) {\n const p0 = clock();\n pass();\n const t = clock();\n passes++;\n batches.push(t - p0);\n const elapsedMs = t - start;\n if (windowsAreStable(batches))\n return { stable: true, elapsedMs, passes, batches, plan: \"large\" };\n if (elapsedMs >= BENCH_CONTRACT.LARGE_WARMUP_MAX_MS) {\n return { stable: false, elapsedMs, passes, batches, plan: \"large\" };\n }\n }\n}\n\n/**\n * Take `SAMPLES` throughput samples. A sample runs whole passes until `SAMPLE_MIN_MS` has passed\n * and reports milliseconds per operation.\n *\n * `inject` plants a slowdown, for calibration only: after each pass the loop reads the clock until\n * `inject` times that pass's own measured time has passed, then starts the next pass, so each\n * sample's time includes the spin. That is all it does. It does not make the measured code slower\n * by exactly that fraction: the spin changes the loop the engine compiles and what the processor\n * does between passes, so the ratio a comparison then measures is not `1 + inject` and differs by\n * workload and machine. A calibration reports the ratio it observed for each case\n * (`CalibrationBody.observed`), and a detection rate is read against that, never against `inject`.\n * Zero in every real measurement.\n *\n * @internal\n */\nexport function sampleThroughput(\n pass: () => void,\n opsPerPass: number,\n clock: Clock,\n inject = 0,\n): number[] {\n const samples: number[] = [];\n for (let s = 0; s < BENCH_CONTRACT.SAMPLES; s++) {\n const t0 = clock();\n let t = t0;\n let passes = 0;\n do {\n const p0 = t;\n pass();\n t = clock();\n // Zero in a real measurement, so `until` is `t` and the clock is not read again.\n const until = t + inject * (t - p0);\n while (t < until) t = clock();\n passes++;\n } while (t - t0 < BENCH_CONTRACT.SAMPLE_MIN_MS);\n samples.push((t - t0) / (passes * opsPerPass));\n }\n return samples;\n}\n\n/**\n * The median gap between two back-to-back reads of the nanosecond clock: what one latency reading\n * costs before the call itself.\n *\n * @internal\n */\nexport function timerOverheadNs(ns: NsClock, reads = 1_000): number {\n const gaps: number[] = [];\n for (let i = 0; i < reads; i++) {\n const a = ns();\n const b = ns();\n gaps.push(Number(b - a));\n }\n return median(gaps);\n}\n\n/** Per-call latency, or why there is none. */\nexport type LatencyReport =\n | {\n readonly status: \"measured\";\n readonly calls: number;\n readonly timerOverheadNs: number;\n /** `null` when fewer than `LATENCY_TAIL_CALLS` calls lie above the percentile. */\n readonly p50Ns: number | null;\n readonly p95Ns: number | null;\n readonly p99Ns: number | null;\n readonly maxNs: number;\n }\n | {\n readonly status: \"skipped\";\n readonly reason: \"latency-below-timer-resolution\";\n readonly calls: number;\n readonly timerOverheadNs: number;\n readonly p50Ns: number;\n };\n\n/**\n * A percentile, or `null` when too few calls lie above it to tell it from the maximum.\n *\n * @internal\n */\nexport function tailPercentile(times: readonly number[], q: number): number | null {\n return times.length * (1 - q) >= BENCH_CONTRACT.LATENCY_TAIL_CALLS ? nearestRank(times, q) : null;\n}\n\n/**\n * Time calls one at a time: at least `LATENCY_MIN_CALLS`, then until `LATENCY_BUDGET_MS` or\n * `LATENCY_MAX_CALLS`. Skips, loudly and typed, when the median call is under\n * `LATENCY_TIMER_MULTIPLE` clock reads: then the clock, not the call, is being measured.\n *\n * @internal\n */\nexport function measureLatency(call: (i: number) => void, ns: NsClock): LatencyReport {\n const overhead = timerOverheadNs(ns);\n const budgetNs = BigInt(BENCH_CONTRACT.LATENCY_BUDGET_MS) * 1_000_000n;\n const times: number[] = [];\n const start = ns();\n let now = start;\n while (\n times.length < BENCH_CONTRACT.LATENCY_MAX_CALLS &&\n (times.length < BENCH_CONTRACT.LATENCY_MIN_CALLS || now - start < budgetNs)\n ) {\n const a = ns();\n call(times.length);\n now = ns();\n times.push(Number(now - a));\n }\n const p50 = nearestRank(times, 0.5);\n if (p50 < BENCH_CONTRACT.LATENCY_TIMER_MULTIPLE * overhead) {\n return {\n status: \"skipped\",\n reason: \"latency-below-timer-resolution\",\n calls: times.length,\n timerOverheadNs: round(overhead, 1),\n p50Ns: p50,\n };\n }\n return {\n status: \"measured\",\n calls: times.length,\n timerOverheadNs: round(overhead, 1),\n p50Ns: tailPercentile(times, 0.5),\n p95Ns: tailPercentile(times, 0.95),\n p99Ns: tailPercentile(times, 0.99),\n maxNs: Math.max(...times),\n };\n}\n","/**\n * How a package describes its benchmarks.\n *\n * A suite is a module whose default export is {@link defineSuite}'s result. It is handed the module\n * under test (`bind`) rather than importing it, so one suite file measures the base build and the\n * head build of a comparison. Every corpus is built in code (`build`): the runner never takes a data\n * file, which is how its inputs stay synthetic by construction.\n *\n * @packageDocumentation\n */\n\n/** One corpus: the inputs one pass runs over, built outside every timed region. */\nexport interface BenchCorpus<TInput> {\n /** The inputs. One pass calls the measured function once on each. */\n readonly inputs: readonly TInput[];\n /** Bytes one pass handles, when it means something. Turns operations a second into bytes a second. */\n readonly bytes?: number;\n /** Called once the process has finished with this corpus, for example to remove a temporary directory. */\n readonly teardown?: () => void;\n}\n\n/** One input size of a benchmark: small, large, or anything between. */\nexport interface BenchSize<TInput> {\n /** How a reader names this size, for example `\"1 KiB\"`. Unique within its benchmark. */\n readonly label: string;\n /** Build the corpus. Called once per process, before anything is timed. */\n readonly build: () => BenchCorpus<TInput>;\n /**\n * Whether the base against head comparison runs this size. Defaults to `true`. Set `false` for a\n * size whose processes are too slow for a pull request's time budget; a reading still measures it.\n */\n readonly compare?: boolean;\n}\n\n/** One benchmark: a function measured at one or more input sizes. */\nexport interface Benchmark<TTarget, TInput, TResult> {\n /** Unique within the suite. */\n readonly name: string;\n /** The sizes, smallest first. Each size is measured on its own. */\n readonly sizes: readonly BenchSize<TInput>[];\n /** Take the imported module under test and return the function to measure. */\n readonly bind: (target: TTarget) => (input: TInput) => TResult;\n /**\n * Reduce a result to a number summed into a sink the runner checks, so the compiler cannot delete\n * the measured call. Defaults to 1 for each result that is not `null` or `undefined`; supply it\n * when the function returns nothing. The sum must grow over a measurement, so return a positive\n * number for at least some results; a run whose sink did not grow is refused.\n */\n readonly weigh?: (result: TResult) => number;\n /** The size label the long run (is the heap flat?) uses. Defaults to the first size. */\n readonly longRun?: string;\n}\n\n/** A benchmark with its input and result types erased, as a suite holds it. */\nexport type AnyBenchmark<TTarget> = Benchmark<TTarget, unknown, unknown>;\n\n/** A suite: the benchmarks of one package. */\nexport interface BenchSuite<TTarget> {\n /** Shown in every report, for example `\"@cosyte/script-utils phi-scan\"`. */\n readonly name: string;\n /** At least one. */\n readonly benchmarks: readonly AnyBenchmark<TTarget>[];\n}\n\n/**\n * Type one benchmark. Only a type helper: it returns its argument, with the input and result types\n * erased so benchmarks of different shapes can sit in one suite.\n *\n * @example\n * ```ts\n * import { defineBenchmark } from \"@cosyte/bench\";\n *\n * const b = defineBenchmark({\n * name: \"split\",\n * sizes: [{ label: \"1 KiB\", build: () => ({ inputs: [\"a|b\".repeat(341)] }) }],\n * bind: () => (s: string) => s.split(\"|\"),\n * });\n * b.name; // => \"split\"\n * ```\n */\nexport function defineBenchmark<TTarget, TInput, TResult>(\n benchmark: Benchmark<TTarget, TInput, TResult>,\n): AnyBenchmark<TTarget> {\n return benchmark as unknown as AnyBenchmark<TTarget>;\n}\n\n/**\n * Check a suite and freeze it. Throws a `TypeError` naming the problem: an empty name, no\n * benchmarks, a repeated name or label, a benchmark with no sizes, or a `longRun` naming no size.\n *\n * @example\n * ```ts\n * import { defineBenchmark, defineSuite } from \"@cosyte/bench\";\n *\n * export default defineSuite({\n * name: \"example\",\n * benchmarks: [\n * defineBenchmark({\n * name: \"split\",\n * sizes: [{ label: \"1 KiB\", build: () => ({ inputs: [\"a|b\".repeat(341)] }) }],\n * bind: () => (s: string) => s.split(\"|\"),\n * }),\n * ],\n * });\n * ```\n */\nexport function defineSuite<TTarget>(suite: BenchSuite<TTarget>): BenchSuite<TTarget> {\n return checkSuite(suite);\n}\n\nfunction fail(message: string): never {\n throw new TypeError(`@cosyte/bench: ${message}`);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction checkName(value: unknown, what: string): string {\n if (typeof value !== \"string\" || value.trim() === \"\") fail(`${what} must be a non-empty string`);\n return value;\n}\n\n/**\n * Check that a value is a suite, as a suite module's default export must be. The messages name\n * benchmarks and sizes, never an input.\n *\n * @internal\n */\nexport function checkSuite(value: unknown): BenchSuite<unknown> {\n if (!isRecord(value)) fail(\"a suite must be an object made by defineSuite\");\n const name = checkName(value[\"name\"], \"the suite name\");\n const benchmarks = value[\"benchmarks\"];\n if (!Array.isArray(benchmarks) || benchmarks.length === 0) {\n fail(`suite \"${name}\" must have at least one benchmark`);\n }\n const seen = new Set<string>();\n for (const b of benchmarks as unknown[]) {\n if (!isRecord(b)) fail(`suite \"${name}\" holds a benchmark that is not an object`);\n const bName = checkName(b[\"name\"], `a benchmark name in suite \"${name}\"`);\n if (seen.has(bName)) fail(`suite \"${name}\" has two benchmarks named \"${bName}\"`);\n seen.add(bName);\n if (typeof b[\"bind\"] !== \"function\") fail(`benchmark \"${bName}\" has no bind function`);\n if (b[\"weigh\"] !== undefined && typeof b[\"weigh\"] !== \"function\") {\n fail(`benchmark \"${bName}\" has a weigh that is not a function`);\n }\n const sizes = b[\"sizes\"];\n if (!Array.isArray(sizes) || sizes.length === 0) fail(`benchmark \"${bName}\" has no sizes`);\n const labels = new Set<string>();\n for (const s of sizes as unknown[]) {\n if (!isRecord(s)) fail(`benchmark \"${bName}\" holds a size that is not an object`);\n const label = checkName(s[\"label\"], `a size label in benchmark \"${bName}\"`);\n if (labels.has(label)) fail(`benchmark \"${bName}\" has two sizes labelled \"${label}\"`);\n labels.add(label);\n if (typeof s[\"build\"] !== \"function\")\n fail(`size \"${label}\" of \"${bName}\" has no build function`);\n if (s[\"compare\"] !== undefined && typeof s[\"compare\"] !== \"boolean\") {\n fail(`size \"${label}\" of \"${bName}\" has a compare that is not true or false`);\n }\n }\n if (b[\"longRun\"] !== undefined && !labels.has(b[\"longRun\"] as string)) {\n fail(`benchmark \"${bName}\" names a longRun size it does not have`);\n }\n }\n return Object.freeze(value) as unknown as BenchSuite<unknown>;\n}\n\n/** One case: a benchmark at one size. */\nexport interface CaseRef {\n readonly benchmark: string;\n readonly size: string;\n}\n\n/**\n * Every case in a suite, in suite order, optionally kept to the ones `only` names. A name in\n * `only` matches a whole benchmark (`\"scan\"`) or one case (`\"scan @ 1 KiB\"`). A name that matches\n * nothing throws, so a typo cannot quietly measure less than was asked. With `forComparison`, a\n * size marked `compare: false` is left out.\n *\n * @internal\n */\nexport function listCases(\n suite: BenchSuite<unknown>,\n only: readonly string[] = [],\n forComparison = false,\n): CaseRef[] {\n const all: CaseRef[] = [];\n for (const b of suite.benchmarks) {\n for (const s of b.sizes) {\n if (!forComparison || s.compare !== false) all.push({ benchmark: b.name, size: s.label });\n }\n }\n if (only.length === 0) return all;\n for (const o of only) {\n if (!all.some((c) => c.benchmark === o || caseId(c) === o)) {\n fail(`--only \"${o}\" names no benchmark or case in suite \"${suite.name}\"`);\n }\n }\n return all.filter((c) => only.includes(c.benchmark) || only.includes(caseId(c)));\n}\n\n/**\n * How a case is named in reports and in `--only`: `benchmark @ size`.\n *\n * @internal\n */\nexport function caseId(c: CaseRef): string {\n return `${c.benchmark} @ ${c.size}`;\n}\n\n/**\n * Find a case's benchmark and size in a suite. Throws when either is missing.\n *\n * @internal\n */\nexport function findCase(\n suite: BenchSuite<unknown>,\n c: CaseRef,\n): { benchmark: AnyBenchmark<unknown>; size: BenchSize<unknown> } {\n const benchmark = suite.benchmarks.find((b) => b.name === c.benchmark);\n const size = benchmark?.sizes.find((s) => s.label === c.size);\n // No benchmark means no size either, so one check covers both.\n if (size === undefined) fail(`suite \"${suite.name}\" has no case \"${caseId(c)}\"`);\n return { benchmark: benchmark as AnyBenchmark<unknown>, size };\n}\n","/**\n * What runs inside a measuring process: one job, one case, then exit.\n *\n * One case per process is deliberate. A process that measured several functions through one harness\n * loop would make that loop's call site polymorphic, and the cost of the second case would depend on\n * which case ran first. A fresh process also carries no compiled code, heap layout or cache warmth\n * from one side of a comparison to the other, which is what makes the rounds independent.\n *\n * @packageDocumentation\n */\n\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { performance } from \"node:perf_hooks\";\nimport { pathToFileURL } from \"node:url\";\n\nimport {\n measureAllocations,\n measureLongRun,\n measurePeak,\n realProbe,\n type AllocationReport,\n type HeapProbe,\n type LongRunReport,\n type PeakReport,\n} from \"./memory.js\";\nimport {\n measureLatency,\n sampleThroughput,\n warmUp,\n type Clock,\n type LatencyReport,\n type NsClock,\n type WarmupReport,\n} from \"./measure.js\";\nimport { median } from \"./stats.js\";\nimport { checkSuite, findCase, type CaseRef } from \"./suite.js\";\n\n/**\n * One job for one measuring process.\n *\n * @internal\n */\nexport type Job =\n | {\n readonly mode: \"timing\";\n readonly suite: string;\n readonly target: string;\n readonly case: CaseRef;\n /** Also time single calls for the latency percentiles. */\n readonly latency: boolean;\n /** A known slowdown, for calibration only. Zero in a real measurement. */\n readonly inject: number;\n }\n | {\n readonly mode: \"alloc\";\n readonly suite: string;\n readonly target: string;\n readonly case: CaseRef;\n }\n | {\n readonly mode: \"heap\";\n readonly suite: string;\n readonly target: string;\n readonly case: CaseRef;\n /** Also run the long run (is the heap flat?) on this case. */\n readonly longRun: boolean;\n };\n\n/**\n * The empty-call floor: the peak of a call that does nothing, read through the same probe the cases\n * are. It names no suite, target or case, and runs once per reading.\n *\n * @internal\n */\nexport interface FloorJob {\n readonly mode: \"floor\";\n}\n\n/**\n * Any job a measuring process runs.\n *\n * @internal\n */\nexport type AnyJob = Job | FloorJob;\n\n/** The shape of one corpus, as reported. Sizes and counts only, never content. */\nexport interface CorpusShape {\n readonly inputsPerPass: number;\n readonly bytesPerPass: number | null;\n}\n\n/**\n * What a timing job measured.\n *\n * @internal\n */\nexport type TimingResult = CorpusShape & {\n readonly mode: \"timing\";\n readonly case: CaseRef;\n readonly warmup: WarmupReport;\n} & (\n | {\n readonly status: \"measured\";\n /** Milliseconds per operation, one per sample, in the order taken. */\n readonly samplesMsPerOp: readonly number[];\n readonly medianMsPerOp: number;\n readonly latency: LatencyReport | null;\n }\n | { readonly status: \"skipped\"; readonly reason: \"warmup-unstable\" }\n );\n\n/**\n * What a memory job measured.\n *\n * @internal\n */\nexport type MemoryResult = CorpusShape & {\n readonly mode: \"alloc\" | \"heap\";\n readonly case: CaseRef;\n readonly allocations: AllocationReport | null;\n readonly peak: PeakReport | null;\n readonly longRun: LongRunReport | null;\n};\n\n/**\n * What the floor job measured: the peak of an empty call, which is what the peak probe itself costs.\n *\n * @internal\n */\nexport interface FloorResult {\n readonly mode: \"floor\";\n readonly peak: PeakReport;\n}\n\n/**\n * What any job measured.\n *\n * @internal\n */\nexport type JobResult = TimingResult | MemoryResult;\n\n/**\n * What any job, the floor included, measured.\n *\n * @internal\n */\nexport type AnyJobResult = JobResult | FloorResult;\n\n/**\n * The clocks and the heap a job reads. Real ones in a measuring process; scripted ones in tests.\n *\n * @internal\n */\nexport interface JobDeps {\n readonly clock: Clock;\n readonly ns: NsClock;\n readonly probe: HeapProbe | null;\n}\n\nconst realDeps = (): JobDeps => ({\n clock: () => performance.now(),\n ns: () => process.hrtime.bigint(),\n probe: realProbe(),\n});\n\nconst defaultWeigh = (result: unknown): number => (result === undefined || result === null ? 0 : 1);\n\nconst SKIPPED_NO_GC = { status: \"skipped\", reason: \"gc-unavailable\" } as const;\n\n/**\n * Run suite code, and if it throws, throw again without its message. Suite code handles the inputs,\n * and a message may quote one: a report that copied it would put an input in a CI log. The error's\n * class name and the phase are kept, which is enough to know where to look.\n *\n * @internal\n */\nexport function guard<T>(c: CaseRef, phase: string, fn: () => T): T {\n try {\n return fn();\n } catch (error) {\n const kind = error instanceof Error ? error.name : typeof error;\n throw new Error(\n `@cosyte/bench: case \"${c.benchmark} @ ${c.size}\": the suite's ${phase} threw (${kind}). ` +\n \"Its message is not printed, because it may quote an input; run the case directly to read it.\",\n { cause: error },\n );\n }\n}\n\n/**\n * The warmup's last pace, in milliseconds per operation.\n *\n * @internal\n */\nexport function msPerOp(warmup: WarmupReport, shape: CorpusShape): number {\n return (warmup.batches[warmup.batches.length - 1] ?? 0) / shape.inputsPerPass;\n}\n\n/**\n * Measure one job once its corpus is built and its code warm.\n *\n * @internal\n */\nexport function measureJob(\n job: Job,\n shape: CorpusShape,\n warmup: WarmupReport,\n pass: () => void,\n call: (i: number) => void,\n deps: JobDeps,\n): JobResult {\n const base = { case: job.case, ...shape };\n if (job.mode === \"timing\") {\n if (!warmup.stable) {\n return { mode: \"timing\", ...base, warmup, status: \"skipped\", reason: \"warmup-unstable\" };\n }\n const samples = sampleThroughput(pass, shape.inputsPerPass, deps.clock, job.inject);\n return {\n mode: \"timing\",\n ...base,\n warmup,\n status: \"measured\",\n samplesMsPerOp: samples,\n medianMsPerOp: median(samples),\n latency: job.latency ? measureLatency(call, deps.ns) : null,\n };\n }\n const probe = deps.probe;\n if (job.mode === \"alloc\") {\n return {\n mode: \"alloc\",\n ...base,\n allocations:\n probe === null ? SKIPPED_NO_GC : measureAllocations(call, probe, msPerOp(warmup, shape)),\n peak: null,\n longRun: null,\n };\n }\n let longRun: LongRunReport | null = null;\n if (job.longRun) {\n longRun =\n probe === null ? SKIPPED_NO_GC : measureLongRun(pass, shape.inputsPerPass, probe, deps.clock);\n }\n return {\n mode: \"heap\",\n ...base,\n allocations: null,\n peak: probe === null ? SKIPPED_NO_GC : measurePeak(call, probe),\n longRun,\n };\n}\n\n/**\n * Whether a job measured anything, so its sink should have moved. A job that only skipped ran no\n * measured calls after its warmup.\n *\n * @internal\n */\nexport function measuredSomething(result: AnyJobResult): boolean {\n if (result.mode === \"floor\") return result.peak.status === \"measured\";\n if (result.mode === \"timing\") return result.status === \"measured\";\n return [result.allocations, result.peak, result.longRun].some((r) => r?.status === \"measured\");\n}\n\n/**\n * Run one job: import the suite and the target, build the case's corpus, measure, tear down.\n *\n * Throws when the suite is not a suite, the case is missing, the corpus is empty, or the measured\n * loop's sink did not grow (a loop the compiler may have deleted measures nothing). Messages name\n * the case, never an input.\n *\n * @internal\n */\nexport async function runJob(job: FloorJob, deps?: JobDeps): Promise<FloorResult>;\nexport async function runJob(job: Job, deps?: JobDeps): Promise<JobResult>;\nexport async function runJob(job: AnyJob, deps?: JobDeps): Promise<AnyJobResult>;\nexport async function runJob(job: AnyJob, deps: JobDeps = realDeps()): Promise<AnyJobResult> {\n if (job.mode === \"floor\") {\n return {\n mode: \"floor\",\n peak: deps.probe === null ? SKIPPED_NO_GC : measurePeak(() => undefined, deps.probe),\n };\n }\n const suiteModule = (await import(pathToFileURL(job.suite).href)) as { default?: unknown };\n const suite = checkSuite(suiteModule.default);\n const target: unknown = await import(pathToFileURL(job.target).href);\n const { benchmark, size } = findCase(suite, job.case);\n const corpus = guard(job.case, \"build\", () => size.build());\n try {\n const inputs = corpus.inputs;\n if (!Array.isArray(inputs) || inputs.length === 0) {\n throw new Error(\n `@cosyte/bench: case \"${job.case.benchmark} @ ${job.case.size}\" built an empty corpus`,\n );\n }\n const fn = guard(job.case, \"bind\", () => benchmark.bind(target));\n const weigh = benchmark.weigh ?? defaultWeigh;\n const sink = { value: 0 };\n const pass = () => {\n let local = 0;\n for (const input of inputs) local += weigh(fn(input));\n sink.value += local;\n };\n const call = (i: number) => {\n sink.value += weigh(fn(inputs[i % inputs.length]));\n };\n const shape: CorpusShape = {\n inputsPerPass: inputs.length,\n bytesPerPass: typeof corpus.bytes === \"number\" ? corpus.bytes : null,\n };\n const warmup = guard(job.case, \"measured function\", () => warmUp(pass, deps.clock));\n const before = sink.value;\n const result = guard(job.case, \"measured function\", () =>\n measureJob(job, shape, warmup, pass, call, deps),\n );\n if (!(sink.value > before) && measuredSomething(result)) {\n throw new Error(\n `@cosyte/bench: case \"${job.case.benchmark} @ ${job.case.size}\": the sink did not grow, so ` +\n \"the measured loop may have been deleted by the compiler, or `weigh` gave nothing positive. \" +\n \"Supply a `weigh` that returns a positive number for a result.\",\n );\n }\n return result;\n } finally {\n corpus.teardown?.();\n }\n}\n\n/**\n * The measuring process's main: read the job file, run it, write `{ ok, result }` or\n * `{ ok, error }` to the out file. Returns the exit code: 0 measured, 2 could not.\n *\n * @internal\n */\nexport async function childMain(jobFile: string, outFile: string, deps?: JobDeps): Promise<number> {\n try {\n const job = JSON.parse(readFileSync(jobFile, \"utf8\")) as AnyJob;\n const result = await runJob(job, deps);\n writeFileSync(outFile, JSON.stringify({ ok: true, result }));\n return 0;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n writeFileSync(outFile, JSON.stringify({ ok: false, error: message }));\n return 2;\n }\n}\n","/**\n * The measuring process. `spawn.ts` starts it with a job file and an out file; it runs the job and\n * exits 0 (measured) or 2 (could not). Its logic is `childMain`, which the tests run in-process; this\n * file is the two lines around it.\n *\n * @packageDocumentation\n */\n\nimport { childMain } from \"./child.js\";\n\nprocess.exitCode = await childMain(process.argv[2] ?? \"\", process.argv[3] ?? \"\");\n"]}