@ac-bench/measure-resource 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2024-2026 Anthony Champagne and contributors
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # @ac-bench/measure-resource
2
+
3
+ What an operation costs in bytes and CPU time, rather than in wall time.
4
+
5
+ Wall time alone cannot tell you why something is fast. An implementation that
6
+ wins by allocating freely looks identical to one that wins by doing less work —
7
+ until the garbage collector runs under load. This reports CPU time, peak heap,
8
+ bytes allocated, and garbage-collection count and pause time, so the two are
9
+ distinguishable.
10
+
11
+ ```ts
12
+ import { resourceCase, resourceCondition } from "@ac-bench/measure-resource";
13
+
14
+ resourceCondition("parse 10k records", () => {
15
+ resourceCase("streaming", () => parseStreaming(input));
16
+ resourceCase("buffer whole document", () => parseBuffered(input));
17
+ });
18
+ ```
19
+
20
+ Results are reported in their own units alongside a duration measure, never
21
+ converted into one.
22
+
23
+ Host-bound: it reads Node's resource-usage and GC instrumentation.
24
+
25
+ Run suites with the `measure` CLI from `@ac-bench/cli`.
@@ -0,0 +1,71 @@
1
+ /** One reading of the counters every resource figure is derived from. */
2
+ export type ResourceCounters = {
3
+ cpuUserUs: number;
4
+ cpuSystemUs: number;
5
+ heapUsedBytes: number;
6
+ externalBytes: number;
7
+ arrayBuffersBytes: number;
8
+ rssBytes: number;
9
+ };
10
+ /**
11
+ * Read twice per batch, so it allocates as little as the platform allows:
12
+ * `v8.getHeapStatistics()` is deliberately not called here — it builds the
13
+ * largest result of the three, and the only field wanted from it is a constant.
14
+ * See {@link readHeapLimitBytes}.
15
+ */
16
+ export declare function readResourceCounters(): ResourceCounters;
17
+ /** The heap ceiling this process was given, fixed for its lifetime. */
18
+ export declare function readHeapLimitBytes(): number;
19
+ /**
20
+ * Bytes a batch asked the allocator for, as the growth in everything the heap
21
+ * accounts for.
22
+ *
23
+ * Negative when a collection ran mid-batch and freed more than was allocated,
24
+ * which is why {@link forceCollection} is called before a batch rather than
25
+ * during it. Clamped at zero: a batch cannot allocate a negative amount, and
26
+ * reporting one would make a mean meaningless.
27
+ */
28
+ export declare function allocatedBytes(before: ResourceCounters, after: ResourceCounters): number;
29
+ export declare function cpuTimeMs(before: ResourceCounters, after: ResourceCounters): number;
30
+ /** Whether the run was started with `--expose-gc`. */
31
+ export declare function hasExposedCollector(): boolean;
32
+ /**
33
+ * Collects, so a batch starts from a known floor and its growth is what it
34
+ * allocated rather than what the collector happened not to have reclaimed yet.
35
+ *
36
+ * A no-op without `--expose-gc`; callers warn rather than fail, since the
37
+ * numbers are still comparable between cases in the same run.
38
+ */
39
+ export declare function forceCollection(): void;
40
+ export type GcTotals = {
41
+ count: number;
42
+ pauseMs: number;
43
+ };
44
+ /**
45
+ * Whether this runtime actually delivers `"gc"` performance entries.
46
+ *
47
+ * `PerformanceObserver.supportedEntryTypes` lists `"gc"` on builds that never
48
+ * emit one — Node 26.8 does, and reports nothing even for a forced collection.
49
+ * Reporting the resulting zero as "no collections ran" would be a lie, so the
50
+ * capability is probed rather than assumed.
51
+ */
52
+ export declare function canObserveGc(): boolean;
53
+ /**
54
+ * Running totals of the collections observed since construction, or `null`
55
+ * where {@link canObserveGc} says the runtime cannot report them.
56
+ *
57
+ * A class because it owns a subscription that outlives any one reading: the
58
+ * observer must stay attached across every batch, and callers difference two
59
+ * readings rather than restarting it.
60
+ */
61
+ export declare class GcRecorder {
62
+ private readonly observer;
63
+ private count_;
64
+ private pauseMs_;
65
+ constructor(observable: boolean);
66
+ read(): GcTotals | null;
67
+ [Symbol.dispose](): void;
68
+ private absorb_;
69
+ }
70
+ export declare function gcSince(before: GcTotals | null, after: GcTotals | null): GcTotals | null;
71
+ //# sourceMappingURL=_counters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_counters.d.ts","sourceRoot":"","sources":["../src/_counters.ts"],"names":[],"mappings":"AAGA,yEAAyE;AACzE,MAAM,MAAM,gBAAgB,GAAG;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,gBAAgB,CAYvD;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC7B,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,gBAAgB,GACrB,MAAM,CAQR;AAED,wBAAgB,SAAS,CACxB,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,gBAAgB,GACrB,MAAM,CAOR;AAED,sDAAsD;AACtD,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAED,MAAM,MAAM,QAAQ,GAAG;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,YAAY,IAAI,OAAO,CActC;AAED;;;;;;;GAOG;AACH,qBAAa,UAAU;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAK;IAErB,YAAY,UAAU,EAAE,OAAO,EAU9B;IAED,IAAI,IAAI,QAAQ,GAAG,IAAI,CAWtB;IAED,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAEvB;IAED,OAAO,CAAC,OAAO;CAMf;AAED,wBAAgB,OAAO,CACtB,MAAM,EAAE,QAAQ,GAAG,IAAI,EACvB,KAAK,EAAE,QAAQ,GAAG,IAAI,GACpB,QAAQ,GAAG,IAAI,CASjB"}
@@ -0,0 +1,134 @@
1
+ import { PerformanceObserver } from "node:perf_hooks";
2
+ import v8 from "node:v8";
3
+ /**
4
+ * Read twice per batch, so it allocates as little as the platform allows:
5
+ * `v8.getHeapStatistics()` is deliberately not called here — it builds the
6
+ * largest result of the three, and the only field wanted from it is a constant.
7
+ * See {@link readHeapLimitBytes}.
8
+ */
9
+ export function readResourceCounters() {
10
+ const cpu = process.cpuUsage();
11
+ const memory = process.memoryUsage();
12
+ return {
13
+ cpuUserUs: cpu.user,
14
+ cpuSystemUs: cpu.system,
15
+ heapUsedBytes: memory.heapUsed,
16
+ externalBytes: memory.external,
17
+ arrayBuffersBytes: memory.arrayBuffers,
18
+ rssBytes: memory.rss,
19
+ };
20
+ }
21
+ /** The heap ceiling this process was given, fixed for its lifetime. */
22
+ export function readHeapLimitBytes() {
23
+ return v8.getHeapStatistics().heap_size_limit;
24
+ }
25
+ /**
26
+ * Bytes a batch asked the allocator for, as the growth in everything the heap
27
+ * accounts for.
28
+ *
29
+ * Negative when a collection ran mid-batch and freed more than was allocated,
30
+ * which is why {@link forceCollection} is called before a batch rather than
31
+ * during it. Clamped at zero: a batch cannot allocate a negative amount, and
32
+ * reporting one would make a mean meaningless.
33
+ */
34
+ export function allocatedBytes(before, after) {
35
+ const growth = after.heapUsedBytes -
36
+ before.heapUsedBytes +
37
+ (after.externalBytes - before.externalBytes) +
38
+ (after.arrayBuffersBytes - before.arrayBuffersBytes);
39
+ return Math.max(0, growth);
40
+ }
41
+ export function cpuTimeMs(before, after) {
42
+ return ((after.cpuUserUs -
43
+ before.cpuUserUs +
44
+ (after.cpuSystemUs - before.cpuSystemUs)) /
45
+ 1000);
46
+ }
47
+ /** Whether the run was started with `--expose-gc`. */
48
+ export function hasExposedCollector() {
49
+ return typeof globalThis.gc === "function";
50
+ }
51
+ /**
52
+ * Collects, so a batch starts from a known floor and its growth is what it
53
+ * allocated rather than what the collector happened not to have reclaimed yet.
54
+ *
55
+ * A no-op without `--expose-gc`; callers warn rather than fail, since the
56
+ * numbers are still comparable between cases in the same run.
57
+ */
58
+ export function forceCollection() {
59
+ globalThis.gc?.();
60
+ }
61
+ /**
62
+ * Whether this runtime actually delivers `"gc"` performance entries.
63
+ *
64
+ * `PerformanceObserver.supportedEntryTypes` lists `"gc"` on builds that never
65
+ * emit one — Node 26.8 does, and reports nothing even for a forced collection.
66
+ * Reporting the resulting zero as "no collections ran" would be a lie, so the
67
+ * capability is probed rather than assumed.
68
+ */
69
+ export function canObserveGc() {
70
+ if (!PerformanceObserver.supportedEntryTypes.includes("gc")) {
71
+ return false;
72
+ }
73
+ const probe = new PerformanceObserver(() => { });
74
+ try {
75
+ probe.observe({ entryTypes: ["gc"] });
76
+ forceCollection();
77
+ return probe.takeRecords().length > 0;
78
+ }
79
+ finally {
80
+ probe.disconnect();
81
+ }
82
+ }
83
+ /**
84
+ * Running totals of the collections observed since construction, or `null`
85
+ * where {@link canObserveGc} says the runtime cannot report them.
86
+ *
87
+ * A class because it owns a subscription that outlives any one reading: the
88
+ * observer must stay attached across every batch, and callers difference two
89
+ * readings rather than restarting it.
90
+ */
91
+ export class GcRecorder {
92
+ observer;
93
+ count_ = 0;
94
+ pauseMs_ = 0;
95
+ constructor(observable) {
96
+ if (!observable) {
97
+ this.observer = null;
98
+ return;
99
+ }
100
+ this.observer = new PerformanceObserver((list) => {
101
+ this.absorb_(list.getEntries());
102
+ });
103
+ this.observer.observe({ entryTypes: ["gc"] });
104
+ }
105
+ read() {
106
+ if (!this.observer) {
107
+ return null;
108
+ }
109
+ // The observer's callback runs on the microtask queue, and a synchronous
110
+ // case never yields to it — so without draining here, a batch that
111
+ // collected repeatedly would report no collections at all.
112
+ this.absorb_(this.observer.takeRecords());
113
+ return { count: this.count_, pauseMs: this.pauseMs_ };
114
+ }
115
+ [Symbol.dispose]() {
116
+ this.observer?.disconnect();
117
+ }
118
+ absorb_(entries) {
119
+ for (let index = 0; index < entries.length; index++) {
120
+ this.count_++;
121
+ this.pauseMs_ += entries[index].duration;
122
+ }
123
+ }
124
+ }
125
+ export function gcSince(before, after) {
126
+ if (before === null || after === null) {
127
+ return null;
128
+ }
129
+ return {
130
+ count: after.count - before.count,
131
+ pauseMs: after.pauseMs - before.pauseMs,
132
+ };
133
+ }
134
+ //# sourceMappingURL=_counters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_counters.js","sourceRoot":"","sources":["../src/_counters.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { BenchCaseRunContext } from "@ac-bench/core/runner";
2
+ import { MaybeAsyncCallable } from "@ac-kit/core";
3
+ import { ResourceCaseOptions, ResourceConditionOptions } from "./options.js";
4
+ export type ResourceCaseFn = MaybeAsyncCallable<[signal: AbortSignal]>;
5
+ export type RunResourceCaseOptions = ResourceCaseOptions & {
6
+ conditionOptions?: ResourceConditionOptions;
7
+ };
8
+ export declare function runResourceCase(title: string, fn: ResourceCaseFn, context: BenchCaseRunContext, options?: RunResourceCaseOptions): Promise<void>;
9
+ //# sourceMappingURL=_run-case.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_run-case.d.ts","sourceRoot":"","sources":["../src/_run-case.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,mBAAmB,EAKnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAIN,kBAAkB,EAClB,MAAM,cAAc,CAAC;AAgBtB,OAAO,EAEN,mBAAmB,EACnB,wBAAwB,EAExB,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,cAAc,GAAG,kBAAkB,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;AAEvE,MAAM,MAAM,sBAAsB,GAAG,mBAAmB,GAAG;IAC1D,gBAAgB,CAAC,EAAE,wBAAwB,CAAC;CAC5C,CAAC;AA0DF,wBAAsB,eAAe,CACpC,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,cAAc,EAClB,OAAO,EAAE,mBAAmB,EAC5B,OAAO,CAAC,EAAE,sBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA6Gf"}
@@ -0,0 +1,189 @@
1
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
+ if (value !== null && value !== void 0) {
3
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
+ var dispose, inner;
5
+ if (async) {
6
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
+ dispose = value[Symbol.asyncDispose];
8
+ }
9
+ if (dispose === void 0) {
10
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
+ dispose = value[Symbol.dispose];
12
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
+ env.stack.push({ value: value, dispose: dispose, async: async });
17
+ }
18
+ else if (async) {
19
+ env.stack.push({ async: true });
20
+ }
21
+ return value;
22
+ };
23
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
+ return function (env) {
25
+ function fail(e) {
26
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
+ env.hasError = true;
28
+ }
29
+ var r, s = 0;
30
+ function next() {
31
+ while (r = env.stack.pop()) {
32
+ try {
33
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
+ if (r.dispose) {
35
+ var result = r.dispose.call(r.value);
36
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
+ }
38
+ else s |= 1;
39
+ }
40
+ catch (e) {
41
+ fail(e);
42
+ }
43
+ }
44
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
+ if (env.hasError) throw env.error;
46
+ }
47
+ return next();
48
+ };
49
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ });
53
+ import { allOfPolicies, createProjectedPolicy, createRelativeErrorPolicy, createStabilityPolicy, Sampler, } from "@ac-bench/core/runner";
54
+ import { defaults, formatError, isThenable, } from "@ac-kit/core";
55
+ import { allocatedBytes, canObserveGc, cpuTimeMs, forceCollection, GcRecorder, gcSince, hasExposedCollector, readHeapLimitBytes, readResourceCounters, } from "./_counters.js";
56
+ import { composeResourceBenchStatistics } from "./_statistics.js";
57
+ import { detectResourceBenchWarnings } from "./_warnings.js";
58
+ import { RESOURCE_SAMPLING_DEFAULTS, } from "./options.js";
59
+ /**
60
+ * Runs one batch and returns what it cost, per operation.
61
+ *
62
+ * Nothing inside the timed loop may allocate, or the measure reports its own
63
+ * bookkeeping as the case's: `process.memoryUsage()` builds a result object per
64
+ * call, so the peak is tracked through `memoryUsage.rss()`, which returns a
65
+ * number, and the heap counters are read at the boundaries only.
66
+ *
67
+ * The collection before the batch is deliberately outside the measured window:
68
+ * it establishes the floor the growth is measured from, and its own cost is not
69
+ * part of what the case allocated.
70
+ */
71
+ async function measureBatch_(fn, iterations, gc, signal) {
72
+ forceCollection();
73
+ const before = readResourceCounters();
74
+ const gcBefore = gc.read();
75
+ let rssPeakBytes = before.rssBytes;
76
+ for (let index = 0; index < iterations; index++) {
77
+ signal.throwIfAborted();
78
+ // Awaiting unconditionally would allocate promise machinery per iteration
79
+ // for a synchronous case, which is allocation this measure would then
80
+ // attribute to the case.
81
+ const returned = fn(signal);
82
+ if (isThenable(returned)) {
83
+ await returned;
84
+ }
85
+ const rss = process.memoryUsage.rss();
86
+ if (rss > rssPeakBytes) {
87
+ rssPeakBytes = rss;
88
+ }
89
+ }
90
+ const after = readResourceCounters();
91
+ const gcDelta = gcSince(gcBefore, gc.read());
92
+ return {
93
+ operations: iterations,
94
+ cpuTimeMs: cpuTimeMs(before, after) / iterations,
95
+ allocatedBytes: allocatedBytes(before, after) / iterations,
96
+ heapPeakBytes: Math.max(before.heapUsedBytes, after.heapUsedBytes),
97
+ rssBytes: rssPeakBytes,
98
+ gcCount: gcDelta === null ? null : gcDelta.count / iterations,
99
+ gcPauseMs: gcDelta === null ? null : gcDelta.pauseMs / iterations,
100
+ };
101
+ }
102
+ export async function runResourceCase(title, fn, context, options) {
103
+ const { signal } = context;
104
+ signal.throwIfAborted();
105
+ const sampling = defaults(options?.conditionOptions?.sampling ?? {}, RESOURCE_SAMPLING_DEFAULTS);
106
+ const tags = options?.tags ?? {};
107
+ const forcedCollection = hasExposedCollector();
108
+ const samples = [];
109
+ const heapLimitBytes = readHeapLimitBytes();
110
+ try {
111
+ const env_1 = { stack: [], error: void 0, hasError: false };
112
+ try {
113
+ await options?.setup?.(signal);
114
+ const gc = __addDisposableResource(env_1, new GcRecorder(canObserveGc()), false);
115
+ for (let iteration = 0; iteration < sampling.warmup; iteration++) {
116
+ signal.throwIfAborted();
117
+ await fn(signal);
118
+ }
119
+ const sampler = new Sampler(allOfPolicies([
120
+ createStabilityPolicy((sample) => [sample.allocatedBytes], {
121
+ window: sampling.stableBatches,
122
+ tolerance: sampling.allocationTolerance,
123
+ }),
124
+ createProjectedPolicy((sample) => sample.cpuTimeMs, createRelativeErrorPolicy({
125
+ minRuns: sampling.minRuns,
126
+ relativeError: sampling.relativeError,
127
+ confidenceLevel: sampling.confidenceLevel,
128
+ estimator: "median",
129
+ })),
130
+ ]), {
131
+ bounds: {
132
+ minRuns: sampling.minRuns,
133
+ maxRuns: sampling.maxRuns,
134
+ minTimeMs: sampling.minTimeMs,
135
+ maxTimeMs: sampling.maxTimeMs,
136
+ },
137
+ signal,
138
+ // One sample is one batch, and the sampler sizes the next batch in
139
+ // iterations — so this is the cost of one operation, not of the batch
140
+ // the sample came from.
141
+ costOf: (sample) => sample.cpuTimeMs,
142
+ targetBatchMs: sampling.targetBatchMs,
143
+ });
144
+ for (let size = sampler.nextBatch(); size !== null; size = sampler.nextBatch()) {
145
+ const startedAt = performance.now();
146
+ const sample = await measureBatch_(fn, size, gc, signal);
147
+ samples.push(sample);
148
+ sampler.observe([sample], performance.now() - startedAt);
149
+ context.progress(samples.length);
150
+ }
151
+ }
152
+ catch (e_1) {
153
+ env_1.error = e_1;
154
+ env_1.hasError = true;
155
+ }
156
+ finally {
157
+ __disposeResources(env_1);
158
+ }
159
+ }
160
+ catch (error) {
161
+ let failureMessage;
162
+ try {
163
+ await options?.teardown?.(signal);
164
+ failureMessage = formatError(error);
165
+ }
166
+ catch (tearDownError) {
167
+ failureMessage = formatError(new SuppressedError(tearDownError, error, "teardown failed after case failure"));
168
+ }
169
+ context.setCaseResult({
170
+ name: title,
171
+ tags,
172
+ statistics: null,
173
+ forcedCollection,
174
+ warnings: [],
175
+ failure: failureMessage,
176
+ });
177
+ return;
178
+ }
179
+ await options?.teardown?.(signal);
180
+ const statistics = composeResourceBenchStatistics(samples, heapLimitBytes);
181
+ context.setCaseResult({
182
+ name: title,
183
+ tags,
184
+ statistics,
185
+ forcedCollection,
186
+ warnings: detectResourceBenchWarnings(statistics, forcedCollection),
187
+ });
188
+ }
189
+ //# sourceMappingURL=_run-case.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_run-case.js","sourceRoot":"","sources":["../src/_run-case.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { ResourceBenchSample, ResourceBenchStatistics } from "./_types.js";
2
+ /**
3
+ * Summarise per-batch resource samples.
4
+ *
5
+ * Rates are medians because a batch that happened to absorb a collection is a
6
+ * heavy right tail, exactly as with timings. Peaks are maxima because the
7
+ * question they answer is what had to fit in memory, not what typically did.
8
+ *
9
+ * @param samples Per-operation costs, one entry per batch.
10
+ * @param heapLimitBytes The heap ceiling this process was given.
11
+ * @returns The computed statistics.
12
+ * @throws {RangeError} If `samples` is empty.
13
+ */
14
+ export declare function composeResourceBenchStatistics(samples: readonly ResourceBenchSample[], heapLimitBytes: number): ResourceBenchStatistics;
15
+ //# sourceMappingURL=_statistics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_statistics.d.ts","sourceRoot":"","sources":["../src/_statistics.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAkB3E;;;;;;;;;;;GAWG;AACH,wBAAgB,8BAA8B,CAC7C,OAAO,EAAE,SAAS,mBAAmB,EAAE,EACvC,cAAc,EAAE,MAAM,GACpB,uBAAuB,CAwCzB"}
@@ -0,0 +1,54 @@
1
+ import { maxBy } from "@ac-kit/algo";
2
+ import { median, sumBy } from "@ac-kit/math-stats";
3
+ function medianBy_(samples, valueOf) {
4
+ return median(Array.from(samples, valueOf));
5
+ }
6
+ function maxValueBy_(samples, valueOf) {
7
+ const peak = maxBy(samples, valueOf);
8
+ return peak === null ? 0 : valueOf(peak);
9
+ }
10
+ /**
11
+ * Summarise per-batch resource samples.
12
+ *
13
+ * Rates are medians because a batch that happened to absorb a collection is a
14
+ * heavy right tail, exactly as with timings. Peaks are maxima because the
15
+ * question they answer is what had to fit in memory, not what typically did.
16
+ *
17
+ * @param samples Per-operation costs, one entry per batch.
18
+ * @param heapLimitBytes The heap ceiling this process was given.
19
+ * @returns The computed statistics.
20
+ * @throws {RangeError} If `samples` is empty.
21
+ */
22
+ export function composeResourceBenchStatistics(samples, heapLimitBytes) {
23
+ if (samples.length === 0) {
24
+ throw new RangeError("Cannot summarise an empty sample set");
25
+ }
26
+ const cpuTimeMs = medianBy_(samples, (sample) => sample.cpuTimeMs);
27
+ const heapPeakBytes = maxValueBy_(samples, (sample) => sample.heapPeakBytes);
28
+ // One `null` means the runtime never reports collections, so the whole
29
+ // series is absent rather than zero.
30
+ const observedGc = samples[0]?.gcPauseMs !== null;
31
+ const gcPauseMs = observedGc
32
+ ? medianBy_(samples, (sample) => sample.gcPauseMs ?? 0)
33
+ : null;
34
+ const gcCount = observedGc
35
+ ? medianBy_(samples, (sample) => sample.gcCount ?? 0)
36
+ : null;
37
+ // Ratios come from the totals rather than from the medians, so a collection
38
+ // that landed in one batch is not divided by another batch's CPU time.
39
+ const totalCpuTimeMs = sumBy(samples, (sample) => sample.cpuTimeMs);
40
+ const totalGcPauseMs = sumBy(samples, (sample) => sample.gcPauseMs ?? 0);
41
+ return {
42
+ samples: samples.length,
43
+ operations: sumBy(samples, (sample) => sample.operations),
44
+ cpuTimeMs,
45
+ allocatedBytes: medianBy_(samples, (sample) => sample.allocatedBytes),
46
+ gcPauseMs,
47
+ gcCount,
48
+ heapPeakBytes,
49
+ rssPeakBytes: maxValueBy_(samples, (sample) => sample.rssBytes),
50
+ heapLimitRatio: heapLimitBytes > 0 ? heapPeakBytes / heapLimitBytes : 0,
51
+ gcTimeRatio: observedGc && totalCpuTimeMs > 0 ? totalGcPauseMs / totalCpuTimeMs : null,
52
+ };
53
+ }
54
+ //# sourceMappingURL=_statistics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_statistics.js","sourceRoot":"","sources":["../src/_statistics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAErC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAInD,SAAS,SAAS,CACjB,OAAuC,EACvC,OAAgD;IAEhD,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,WAAW,CACnB,OAAuC,EACvC,OAAgD;IAEhD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAErC,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,8BAA8B,CAC7C,OAAuC,EACvC,cAAsB;IAEtB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,UAAU,CAAC,sCAAsC,CAAC,CAAC;IAC9D,CAAC;IAED,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACnE,MAAM,aAAa,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IAE7E,uEAAuE;IACvE,qCAAqC;IACrC,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,CAAC;IAElD,MAAM,SAAS,GAAG,UAAU;QAC3B,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;QACvD,CAAC,CAAC,IAAI,CAAC;IACR,MAAM,OAAO,GAAG,UAAU;QACzB,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC;QACrD,CAAC,CAAC,IAAI,CAAC;IAER,4EAA4E;IAC5E,uEAAuE;IACvE,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpE,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC;IAEzE,OAAO;QACN,OAAO,EAAE,OAAO,CAAC,MAAM;QACvB,UAAU,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC;QAEzD,SAAS;QACT,cAAc,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC;QACrE,SAAS;QACT,OAAO;QAEP,aAAa;QACb,YAAY,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC;QAE/D,cAAc,EAAE,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;QACvE,WAAW,EACV,UAAU,IAAI,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,GAAG,cAAc,CAAC,CAAC,CAAC,IAAI;KAC1E,CAAC;AACH,CAAC"}
@@ -0,0 +1,91 @@
1
+ import * as z from "zod/mini";
2
+ /**
3
+ * One batch's cost, already divided by the operations it ran, so every sample
4
+ * is directly comparable regardless of how the sampler sized its batches.
5
+ */
6
+ export declare const resourceBenchSampleSchema: z.ZodMiniObject<{
7
+ operations: z.ZodMiniNumber<number>;
8
+ cpuTimeMs: z.ZodMiniNumber<number>;
9
+ allocatedBytes: z.ZodMiniNumber<number>;
10
+ heapPeakBytes: z.ZodMiniNumber<number>;
11
+ rssBytes: z.ZodMiniNumber<number>;
12
+ gcCount: z.ZodMiniNullable<z.ZodMiniNumber<number>>;
13
+ gcPauseMs: z.ZodMiniNullable<z.ZodMiniNumber<number>>;
14
+ }, z.core.$strip>;
15
+ export type ResourceBenchSample = z.infer<typeof resourceBenchSampleSchema>;
16
+ declare const resourceBenchStatisticsSchema: z.ZodMiniObject<{
17
+ samples: z.ZodMiniNumber<number>;
18
+ operations: z.ZodMiniNumber<number>;
19
+ cpuTimeMs: z.ZodMiniNumber<number>;
20
+ allocatedBytes: z.ZodMiniNumber<number>;
21
+ gcPauseMs: z.ZodMiniNullable<z.ZodMiniNumber<number>>;
22
+ gcCount: z.ZodMiniNullable<z.ZodMiniNumber<number>>;
23
+ heapPeakBytes: z.ZodMiniNumber<number>;
24
+ rssPeakBytes: z.ZodMiniNumber<number>;
25
+ heapLimitRatio: z.ZodMiniNumber<number>;
26
+ gcTimeRatio: z.ZodMiniNullable<z.ZodMiniNumber<number>>;
27
+ }, z.core.$strip>;
28
+ export type ResourceBenchStatistics = z.infer<typeof resourceBenchStatisticsSchema>;
29
+ export declare const resourceBenchRunCaseResultSchema: z.ZodMiniObject<{
30
+ name: z.ZodMiniString<string>;
31
+ tags: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
32
+ statistics: z.ZodMiniNullable<z.ZodMiniObject<{
33
+ samples: z.ZodMiniNumber<number>;
34
+ operations: z.ZodMiniNumber<number>;
35
+ cpuTimeMs: z.ZodMiniNumber<number>;
36
+ allocatedBytes: z.ZodMiniNumber<number>;
37
+ gcPauseMs: z.ZodMiniNullable<z.ZodMiniNumber<number>>;
38
+ gcCount: z.ZodMiniNullable<z.ZodMiniNumber<number>>;
39
+ heapPeakBytes: z.ZodMiniNumber<number>;
40
+ rssPeakBytes: z.ZodMiniNumber<number>;
41
+ heapLimitRatio: z.ZodMiniNumber<number>;
42
+ gcTimeRatio: z.ZodMiniNullable<z.ZodMiniNumber<number>>;
43
+ }, z.core.$strip>>;
44
+ forcedCollection: z.ZodMiniBoolean<boolean>;
45
+ warnings: z.ZodMiniArray<z.ZodMiniObject<{
46
+ kind: z.ZodMiniEnum<{
47
+ "below-resolution": "below-resolution";
48
+ "gc-bound": "gc-bound";
49
+ "heap-pressure": "heap-pressure";
50
+ "no-forced-collection": "no-forced-collection";
51
+ "too-few-samples": "too-few-samples";
52
+ }>;
53
+ message: z.ZodMiniString<string>;
54
+ }, z.core.$strip>>;
55
+ failure: z.ZodMiniOptional<z.ZodMiniString<string>>;
56
+ }, z.core.$strip>;
57
+ export type ResourceBenchRunCaseResult = z.infer<typeof resourceBenchRunCaseResultSchema>;
58
+ export declare const resourceBenchRunConditionResultSchema: z.ZodMiniObject<{
59
+ title: z.ZodMiniString<string>;
60
+ results: z.ZodMiniArray<z.ZodMiniObject<{
61
+ name: z.ZodMiniString<string>;
62
+ tags: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
63
+ statistics: z.ZodMiniNullable<z.ZodMiniObject<{
64
+ samples: z.ZodMiniNumber<number>;
65
+ operations: z.ZodMiniNumber<number>;
66
+ cpuTimeMs: z.ZodMiniNumber<number>;
67
+ allocatedBytes: z.ZodMiniNumber<number>;
68
+ gcPauseMs: z.ZodMiniNullable<z.ZodMiniNumber<number>>;
69
+ gcCount: z.ZodMiniNullable<z.ZodMiniNumber<number>>;
70
+ heapPeakBytes: z.ZodMiniNumber<number>;
71
+ rssPeakBytes: z.ZodMiniNumber<number>;
72
+ heapLimitRatio: z.ZodMiniNumber<number>;
73
+ gcTimeRatio: z.ZodMiniNullable<z.ZodMiniNumber<number>>;
74
+ }, z.core.$strip>>;
75
+ forcedCollection: z.ZodMiniBoolean<boolean>;
76
+ warnings: z.ZodMiniArray<z.ZodMiniObject<{
77
+ kind: z.ZodMiniEnum<{
78
+ "below-resolution": "below-resolution";
79
+ "gc-bound": "gc-bound";
80
+ "heap-pressure": "heap-pressure";
81
+ "no-forced-collection": "no-forced-collection";
82
+ "too-few-samples": "too-few-samples";
83
+ }>;
84
+ message: z.ZodMiniString<string>;
85
+ }, z.core.$strip>>;
86
+ failure: z.ZodMiniOptional<z.ZodMiniString<string>>;
87
+ }, z.core.$strip>>;
88
+ }, z.core.$strip>;
89
+ export type ResourceBenchRunConditionResult = z.infer<typeof resourceBenchRunConditionResultSchema>;
90
+ export {};
91
+ //# sourceMappingURL=_types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_types.d.ts","sourceRoot":"","sources":["../src/_types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC;AAI9B;;;GAGG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;iBASpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,QAAA,MAAM,6BAA6B;;;;;;;;;;;iBAmBjC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAC5C,OAAO,6BAA6B,CACpC,CAAC;AAEF,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAc3C,CAAC;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAC/C,OAAO,gCAAgC,CACvC,CAAC;AAEF,eAAO,MAAM,qCAAqC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAGhD,CAAC;AACH,MAAM,MAAM,+BAA+B,GAAG,CAAC,CAAC,KAAK,CACpD,OAAO,qCAAqC,CAC5C,CAAC"}
package/dist/_types.js ADDED
@@ -0,0 +1,48 @@
1
+ import * as z from "zod/mini";
2
+ import { resourceBenchWarningSchema } from "./_warnings.js";
3
+ /**
4
+ * One batch's cost, already divided by the operations it ran, so every sample
5
+ * is directly comparable regardless of how the sampler sized its batches.
6
+ */
7
+ export const resourceBenchSampleSchema = z.object({
8
+ operations: z.number(),
9
+ cpuTimeMs: z.number(),
10
+ allocatedBytes: z.number(),
11
+ heapPeakBytes: z.number(),
12
+ rssBytes: z.number(),
13
+ /** `null` where the runtime does not report collections. */
14
+ gcCount: z.nullable(z.number()),
15
+ gcPauseMs: z.nullable(z.number()),
16
+ });
17
+ const resourceBenchStatisticsSchema = z.object({
18
+ samples: z.number(),
19
+ operations: z.number(),
20
+ /** Medians, per operation, except the peaks which are maxima over the run. */
21
+ cpuTimeMs: z.number(),
22
+ allocatedBytes: z.number(),
23
+ /** `null` where the runtime does not report collections. */
24
+ gcPauseMs: z.nullable(z.number()),
25
+ gcCount: z.nullable(z.number()),
26
+ heapPeakBytes: z.number(),
27
+ rssPeakBytes: z.number(),
28
+ /** Share of the heap limit the peak reached, in [0, 1]. */
29
+ heapLimitRatio: z.number(),
30
+ /** Fraction of the CPU time spent collecting, in [0, 1], or `null`. */
31
+ gcTimeRatio: z.nullable(z.number()),
32
+ });
33
+ export const resourceBenchRunCaseResultSchema = z.object({
34
+ name: z.string(),
35
+ tags: z.record(z.string(), z.string()),
36
+ /** `null` when the case failed. */
37
+ statistics: z.nullable(resourceBenchStatisticsSchema),
38
+ /** Whether `--expose-gc` was available, so the numbers can be read fairly. */
39
+ forcedCollection: z.boolean(),
40
+ warnings: z.array(resourceBenchWarningSchema),
41
+ /** Present when the case threw. */
42
+ failure: z.optional(z.string()),
43
+ });
44
+ export const resourceBenchRunConditionResultSchema = z.object({
45
+ title: z.string(),
46
+ results: z.array(resourceBenchRunCaseResultSchema),
47
+ });
48
+ //# sourceMappingURL=_types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_types.js","sourceRoot":"","sources":["../src/_types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC;AAE9B,OAAO,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAE5D;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE;IAC1B,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;IACzB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,4DAA4D;IAC5D,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC/B,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CACjC,CAAC,CAAC;AAGH,MAAM,6BAA6B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IAEtB,8EAA8E;IAC9E,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE;IAC1B,4DAA4D;IAC5D,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACjC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAE/B,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;IACzB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IAExB,2DAA2D;IAC3D,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE;IAE1B,uEAAuE;IACvE,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CACnC,CAAC,CAAC;AAKH,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,CAAC,MAAM,CAAC;IACxD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;IAEtC,mCAAmC;IACnC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,6BAA6B,CAAC;IAErD,8EAA8E;IAC9E,gBAAgB,EAAE,CAAC,CAAC,OAAO,EAAE;IAE7B,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,0BAA0B,CAAC;IAE7C,mCAAmC;IACnC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAC/B,CAAC,CAAC;AAKH,MAAM,CAAC,MAAM,qCAAqC,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7D,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,gCAAgC,CAAC;CAClD,CAAC,CAAC"}
@@ -0,0 +1,23 @@
1
+ import * as z from "zod/mini";
2
+ import { ResourceBenchStatistics } from "./_types.js";
3
+ export declare const resourceBenchWarningKindSchema: z.ZodMiniEnum<{
4
+ "below-resolution": "below-resolution";
5
+ "gc-bound": "gc-bound";
6
+ "heap-pressure": "heap-pressure";
7
+ "no-forced-collection": "no-forced-collection";
8
+ "too-few-samples": "too-few-samples";
9
+ }>;
10
+ export type ResourceBenchWarningKind = z.infer<typeof resourceBenchWarningKindSchema>;
11
+ export declare const resourceBenchWarningSchema: z.ZodMiniObject<{
12
+ kind: z.ZodMiniEnum<{
13
+ "below-resolution": "below-resolution";
14
+ "gc-bound": "gc-bound";
15
+ "heap-pressure": "heap-pressure";
16
+ "no-forced-collection": "no-forced-collection";
17
+ "too-few-samples": "too-few-samples";
18
+ }>;
19
+ message: z.ZodMiniString<string>;
20
+ }, z.core.$strip>;
21
+ export type ResourceBenchWarning = z.infer<typeof resourceBenchWarningSchema>;
22
+ export declare function detectResourceBenchWarnings(statistics: ResourceBenchStatistics, forcedCollection: boolean): ResourceBenchWarning[];
23
+ //# sourceMappingURL=_warnings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_warnings.d.ts","sourceRoot":"","sources":["../src/_warnings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC;AAE9B,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAWtD,eAAO,MAAM,8BAA8B;;;;;;EAMzC,CAAC;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAC7C,OAAO,8BAA8B,CACrC,CAAC;AAEF,eAAO,MAAM,0BAA0B;;;;;;;;;iBAGrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,wBAAgB,2BAA2B,CAC1C,UAAU,EAAE,uBAAuB,EACnC,gBAAgB,EAAE,OAAO,GACvB,oBAAoB,EAAE,CA4CxB"}
@@ -0,0 +1,54 @@
1
+ import * as z from "zod/mini";
2
+ /** Share of the heap limit above which a peak is worth flagging. */
3
+ const HEAP_PRESSURE_RATIO_ = 0.8;
4
+ /** Share of CPU time spent collecting above which the case is GC-bound. */
5
+ const GC_BOUND_RATIO_ = 0.25;
6
+ /** Below this many bytes per operation, allocation is below what GC resolves. */
7
+ const ALLOCATION_RESOLUTION_BYTES_ = 8;
8
+ export const resourceBenchWarningKindSchema = z.enum([
9
+ "no-forced-collection",
10
+ "heap-pressure",
11
+ "gc-bound",
12
+ "below-resolution",
13
+ "too-few-samples",
14
+ ]);
15
+ export const resourceBenchWarningSchema = z.object({
16
+ kind: resourceBenchWarningKindSchema,
17
+ message: z.string(),
18
+ });
19
+ export function detectResourceBenchWarnings(statistics, forcedCollection) {
20
+ const warnings = [];
21
+ if (!forcedCollection) {
22
+ warnings.push({
23
+ kind: "no-forced-collection",
24
+ message: "run without --expose-gc: allocation includes whatever the collector had not yet reclaimed, so compare cases within this run only",
25
+ });
26
+ }
27
+ if (statistics.samples < 5) {
28
+ warnings.push({
29
+ kind: "too-few-samples",
30
+ message: `only ${statistics.samples} batches; the allocation figure has not been shown to settle`,
31
+ });
32
+ }
33
+ if (statistics.heapLimitRatio >= HEAP_PRESSURE_RATIO_) {
34
+ warnings.push({
35
+ kind: "heap-pressure",
36
+ message: `peak heap reached ${(statistics.heapLimitRatio * 100).toFixed(0)}% of the limit; the collector was working against the case and the timing reflects that`,
37
+ });
38
+ }
39
+ if (statistics.gcTimeRatio !== null &&
40
+ statistics.gcTimeRatio >= GC_BOUND_RATIO_) {
41
+ warnings.push({
42
+ kind: "gc-bound",
43
+ message: `${(statistics.gcTimeRatio * 100).toFixed(0)}% of CPU time was spent collecting; this case is bound by what it allocates, not by what it computes`,
44
+ });
45
+ }
46
+ if (statistics.allocatedBytes < ALLOCATION_RESOLUTION_BYTES_) {
47
+ warnings.push({
48
+ kind: "below-resolution",
49
+ message: "allocation per operation is at the limit of what the heap counters resolve; treat as zero",
50
+ });
51
+ }
52
+ return warnings;
53
+ }
54
+ //# sourceMappingURL=_warnings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_warnings.js","sourceRoot":"","sources":["../src/_warnings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC;AAI9B,oEAAoE;AACpE,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAEjC,2EAA2E;AAC3E,MAAM,eAAe,GAAG,IAAI,CAAC;AAE7B,iFAAiF;AACjF,MAAM,4BAA4B,GAAG,CAAC,CAAC;AAEvC,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC,IAAI,CAAC;IACpD,sBAAsB;IACtB,eAAe;IACf,UAAU;IACV,kBAAkB;IAClB,iBAAiB;CACjB,CAAC,CAAC;AAKH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,IAAI,EAAE,8BAA8B;IACpC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACnB,CAAC,CAAC;AAGH,MAAM,UAAU,2BAA2B,CAC1C,UAAmC,EACnC,gBAAyB;IAEzB,MAAM,QAAQ,GAA2B,EAAE,CAAC;IAE5C,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACvB,QAAQ,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,sBAAsB;YAC5B,OAAO,EACN,kIAAkI;SACnI,CAAC,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,iBAAiB;YACvB,OAAO,EAAE,QAAQ,UAAU,CAAC,OAAO,8DAA8D;SACjG,CAAC,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,CAAC,cAAc,IAAI,oBAAoB,EAAE,CAAC;QACvD,QAAQ,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,qBAAqB,CAAC,UAAU,CAAC,cAAc,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,yFAAyF;SACnK,CAAC,CAAC;IACJ,CAAC;IAED,IACC,UAAU,CAAC,WAAW,KAAK,IAAI;QAC/B,UAAU,CAAC,WAAW,IAAI,eAAe,EACxC,CAAC;QACF,QAAQ,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,GAAG,CAAC,UAAU,CAAC,WAAW,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,sGAAsG;SAC3J,CAAC,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,CAAC,cAAc,GAAG,4BAA4B,EAAE,CAAC;QAC9D,QAAQ,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,kBAAkB;YACxB,OAAO,EACN,2FAA2F;SAC5F,CAAC,CAAC;IACJ,CAAC;IAED,OAAO,QAAQ,CAAC;AACjB,CAAC"}
package/dist/decl.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { MaybeAsyncCallableNoArgs } from "@ac-kit/core";
2
+ import { ResourceCaseFn } from "./_run-case.js";
3
+ import { ResourceCaseOptions, ResourceConditionOptions } from "./options.js";
4
+ export declare function resourceCondition(title: string, fn: MaybeAsyncCallableNoArgs): void;
5
+ export declare function resourceCondition(title: string, options: ResourceConditionOptions, fn: MaybeAsyncCallableNoArgs): void;
6
+ export declare function resourceCase(title: string, fn: ResourceCaseFn): void;
7
+ export declare function resourceCase(title: string, options: ResourceCaseOptions, fn: ResourceCaseFn): void;
8
+ //# sourceMappingURL=decl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decl.d.ts","sourceRoot":"","sources":["../src/decl.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,EAAE,cAAc,EAAmB,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAE7E,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,wBAAwB,GAC1B,IAAI,CAAC;AACR,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,wBAAwB,EACjC,EAAE,EAAE,wBAAwB,GAC1B,IAAI,CAAC;AAgBR,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,GAAG,IAAI,CAAC;AACtE,wBAAgB,YAAY,CAC3B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,mBAAmB,EAC5B,EAAE,EAAE,cAAc,GAChB,IAAI,CAAC"}
package/dist/decl.js ADDED
@@ -0,0 +1,20 @@
1
+ import { registerCase, registerCondition } from "@ac-bench/core/runner";
2
+ import { runResourceCase } from "./_run-case.js";
3
+ export function resourceCondition(title, optionsOrFn, fn) {
4
+ const [options, callback] = typeof optionsOrFn === "function"
5
+ ? [undefined, optionsOrFn]
6
+ : [optionsOrFn, fn];
7
+ registerCondition("resource", title, callback, {
8
+ additionalRunArgs: [options],
9
+ });
10
+ }
11
+ export function resourceCase(title, optionsOrFn, fn) {
12
+ const isOptionsForm = typeof optionsOrFn !== "function";
13
+ const options = isOptionsForm ? optionsOrFn : undefined;
14
+ const caseFn = isOptionsForm ? fn : optionsOrFn;
15
+ registerCase("resource", title, (context, conditionOptions) => runResourceCase(title, caseFn, context, {
16
+ ...options,
17
+ conditionOptions,
18
+ }));
19
+ }
20
+ //# sourceMappingURL=decl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decl.js","sourceRoot":"","sources":["../src/decl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAGxE,OAAO,EAAkB,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAYjE,MAAM,UAAU,iBAAiB,CAChC,KAAa,EACb,WAAgE,EAChE,EAA6B;IAE7B,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,GACxB,OAAO,WAAW,KAAK,UAAU;QAChC,CAAC,CAAC,CAAC,SAAS,EAAE,WAAW,CAAC;QAC1B,CAAC,CAAC,CAAC,WAAW,EAAE,EAAG,CAAC,CAAC;IAEvB,iBAAiB,CAAC,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE;QAC9C,iBAAiB,EAAE,CAAC,OAAO,CAAC;KAC5B,CAAC,CAAC;AACJ,CAAC;AAQD,MAAM,UAAU,YAAY,CAC3B,KAAa,EACb,WAAiD,EACjD,EAAmB;IAEnB,MAAM,aAAa,GAAG,OAAO,WAAW,KAAK,UAAU,CAAC;IACxD,MAAM,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;IACxD,MAAM,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,EAAG,CAAC,CAAC,CAAC,WAAW,CAAC;IAEjD,YAAY,CACX,UAAU,EACV,KAAK,EACL,CAAC,OAAO,EAAE,gBAAgB,EAAE,EAAE,CAC7B,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE;QACvC,GAAG,OAAO;QACV,gBAAgB;KAChB,CAAC,CACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { afterAll, afterEach, beforeAll, beforeEach, } from "@ac-bench/core/runner";
2
+ export * from "./decl.js";
3
+ export * from "./options.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,QAAQ,EACR,SAAS,EACT,SAAS,EACT,UAAU,GACV,MAAM,uBAAuB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { afterAll, afterEach, beforeAll, beforeEach, } from "@ac-bench/core/runner";
2
+ export * from "./decl.js";
3
+ export * from "./options.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,QAAQ,EACR,SAAS,EACT,SAAS,EACT,UAAU,GACV,MAAM,uBAAuB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { CaseCommonOptions, SamplingBounds } from "@ac-bench/core/runner";
2
+ export type ResourceSampling = SamplingBounds & {
3
+ /** Batches run and discarded first, letting the JIT settle. Defaults to 2. */
4
+ warmup: number;
5
+ /**
6
+ * How tight the interval around CPU time must be before sampling stops.
7
+ *
8
+ * The byte counters are judged by settling instead, since a count that never
9
+ * varies has no interval worth tightening.
10
+ */
11
+ relativeError: number | "auto" | "off";
12
+ /** Confidence level for the CPU-time interval. Defaults to 0.95. */
13
+ confidenceLevel: number;
14
+ /**
15
+ * Relative spread within which the byte counters count as settled. Defaults
16
+ * to 0.02 — a heap delta repeats to within a fraction of a percent, not to
17
+ * the byte.
18
+ */
19
+ allocationTolerance: number;
20
+ /** Batches whose byte counters must agree before stopping. Defaults to 3. */
21
+ stableBatches: number;
22
+ /**
23
+ * Work one batch should amount to, in milliseconds. Defaults to 50 — far
24
+ * longer than a duration measure wants, because the counters are read once
25
+ * per batch and that cost is divided by the operations in it. A batch of one
26
+ * reports the measure's own bookkeeping as the case's allocation.
27
+ */
28
+ targetBatchMs: number;
29
+ };
30
+ export declare const RESOURCE_SAMPLING_DEFAULTS: {
31
+ readonly warmup: 2;
32
+ readonly minRuns: 10;
33
+ readonly maxRuns: 2000;
34
+ readonly minTimeMs: 250;
35
+ readonly maxTimeMs: 10000;
36
+ readonly relativeError: "auto";
37
+ readonly confidenceLevel: 0.95;
38
+ readonly allocationTolerance: 0.02;
39
+ readonly stableBatches: 3;
40
+ readonly targetBatchMs: 50;
41
+ };
42
+ export type ResourceConditionOptions = {
43
+ sampling?: Partial<ResourceSampling>;
44
+ };
45
+ export type ResourceCaseOptions = CaseCommonOptions;
46
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE1E,MAAM,MAAM,gBAAgB,GAAG,cAAc,GAAG;IAC/C,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC;IAEf;;;;;OAKG;IACH,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;IAEvC,oEAAoE;IACpE,eAAe,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAE5B,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;IAEtB;;;;;OAKG;IACH,aAAa,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,0BAA0B;qBAC9B,CAAC;sBACA,EAAE;sBACF,IAAI;wBACF,GAAG;wBACH,KAAM;4BACF,MAAM;8BACJ,IAAI;kCACA,IAAI;4BACV,CAAC;4BACD,EAAE;CACmB,CAAC;AAEtC,MAAM,MAAM,wBAAwB,GAAG;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,iBAAiB,CAAC"}
@@ -0,0 +1,13 @@
1
+ export const RESOURCE_SAMPLING_DEFAULTS = {
2
+ warmup: 2,
3
+ minRuns: 10,
4
+ maxRuns: 2000,
5
+ minTimeMs: 250,
6
+ maxTimeMs: 10000,
7
+ relativeError: "auto",
8
+ confidenceLevel: 0.95,
9
+ allocationTolerance: 0.02,
10
+ stableBatches: 3,
11
+ targetBatchMs: 50,
12
+ };
13
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAoCA,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACzC,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,EAAE;IACX,OAAO,EAAE,IAAI;IACb,SAAS,EAAE,GAAG;IACd,SAAS,EAAE,KAAM;IACjB,aAAa,EAAE,MAAM;IACrB,eAAe,EAAE,IAAI;IACrB,mBAAmB,EAAE,IAAI;IACzB,aAAa,EAAE,CAAC;IAChB,aAAa,EAAE,EAAE;CACmB,CAAC"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@ac-bench/core/plugin").MeasurePlugin;
2
+ export default _default;
3
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":""}
package/dist/plugin.js ADDED
@@ -0,0 +1,151 @@
1
+ import { defineMeasurePlugin, MeasureResultValidationError, } from "@ac-bench/core/plugin";
2
+ import { BYTE, MILLISECOND } from "@ac-kit/model-dataset";
3
+ import * as z from "zod/mini";
4
+ import { resourceBenchRunCaseResultSchema, resourceBenchRunConditionResultSchema, } from "./_types.js";
5
+ const MEASURE_ID = "resource";
6
+ function parseCaseResult_(value) {
7
+ const result = resourceBenchRunCaseResultSchema.safeParse(value);
8
+ if (!result.success) {
9
+ throw new MeasureResultValidationError(MEASURE_ID, z.prettifyError(result.error));
10
+ }
11
+ return result.data;
12
+ }
13
+ function parseConditionResult_(value) {
14
+ const result = resourceBenchRunConditionResultSchema.safeParse(value);
15
+ if (!result.success) {
16
+ throw new MeasureResultValidationError(MEASURE_ID, z.prettifyError(result.error));
17
+ }
18
+ return result.data;
19
+ }
20
+ const BYTE_FORMAT = { maximumFractionDigits: 0 };
21
+ const MS_FORMAT = { minimumFractionDigits: 4, maximumFractionDigits: 4 };
22
+ const RATIO_FORMAT = { style: "percent", maximumFractionDigits: 1 };
23
+ const FIELDS = [
24
+ { name: "case", kind: "nominal", title: "case" },
25
+ {
26
+ name: "allocatedBytes",
27
+ kind: "quantitative",
28
+ title: "alloc/op",
29
+ unit: BYTE,
30
+ direction: "lower-is-better",
31
+ format: BYTE_FORMAT,
32
+ },
33
+ {
34
+ name: "cpuTimeMs",
35
+ kind: "quantitative",
36
+ title: "cpu/op",
37
+ unit: MILLISECOND,
38
+ direction: "lower-is-better",
39
+ format: MS_FORMAT,
40
+ },
41
+ {
42
+ name: "heapPeakBytes",
43
+ kind: "quantitative",
44
+ title: "heap peak",
45
+ unit: BYTE,
46
+ direction: "lower-is-better",
47
+ format: BYTE_FORMAT,
48
+ },
49
+ {
50
+ name: "rssPeakBytes",
51
+ kind: "quantitative",
52
+ title: "rss peak",
53
+ unit: BYTE,
54
+ direction: "lower-is-better",
55
+ format: BYTE_FORMAT,
56
+ },
57
+ {
58
+ name: "gcPauseMs",
59
+ kind: "quantitative",
60
+ title: "gc/op",
61
+ unit: MILLISECOND,
62
+ direction: "lower-is-better",
63
+ format: MS_FORMAT,
64
+ },
65
+ {
66
+ name: "gcTimeRatio",
67
+ kind: "quantitative",
68
+ title: "gc share",
69
+ direction: "lower-is-better",
70
+ format: RATIO_FORMAT,
71
+ },
72
+ {
73
+ name: "samples",
74
+ kind: "quantitative",
75
+ title: "n",
76
+ format: { maximumFractionDigits: 0 },
77
+ },
78
+ { name: "status", kind: "nominal", title: "status" },
79
+ ];
80
+ function caseRow_(result) {
81
+ const stats = result.statistics;
82
+ if (!stats) {
83
+ return [
84
+ result.name,
85
+ null,
86
+ null,
87
+ null,
88
+ null,
89
+ null,
90
+ null,
91
+ null,
92
+ `FAILED: ${result.failure ?? "unknown"}`,
93
+ ];
94
+ }
95
+ return [
96
+ result.name,
97
+ stats.allocatedBytes,
98
+ stats.cpuTimeMs,
99
+ stats.heapPeakBytes,
100
+ stats.rssPeakBytes,
101
+ stats.gcPauseMs,
102
+ stats.gcTimeRatio,
103
+ stats.samples,
104
+ result.forcedCollection ? null : "no --expose-gc",
105
+ ];
106
+ }
107
+ export default defineMeasurePlugin({
108
+ id: MEASURE_ID,
109
+ label: "Resource",
110
+ parseCaseResult: parseCaseResult_,
111
+ parseConditionResult: parseConditionResult_,
112
+ fields: FIELDS,
113
+ caseRow: caseRow_,
114
+ // Bytes allocated is the figure this measure exists to compare, and it is
115
+ // deterministic enough to order cases by without an interval.
116
+ comparison: { field: "allocatedBytes" },
117
+ warnings: (result) => {
118
+ const diagnostics = [];
119
+ if (result.failure) {
120
+ diagnostics.push({
121
+ severity: "error",
122
+ code: "case-failed",
123
+ message: result.failure,
124
+ });
125
+ }
126
+ for (const warning of result.warnings) {
127
+ diagnostics.push({
128
+ severity: "warning",
129
+ code: warning.kind,
130
+ message: warning.message,
131
+ });
132
+ }
133
+ return diagnostics;
134
+ },
135
+ // A shared process makes heap attribution impossible — case A's garbage is
136
+ // in case B's heap — so each case gets its own, and interleaving rounds
137
+ // would defeat the same thing at a coarser grain.
138
+ scheduling: {
139
+ rounds: "one",
140
+ grouping: "case",
141
+ execArgv: ["--expose-gc"],
142
+ },
143
+ // Each execution measured its own process's heap. Pooling them would average
144
+ // two heaps that never coexisted, so the first is reported as it stands.
145
+ pool: (executions) => executions[0].result,
146
+ toJsonCase: (result) => result,
147
+ toJsonCondition: (result) => ({
148
+ resourceConditions: [{ kind: "resource", ...result }],
149
+ }),
150
+ });
151
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@ac-bench/measure-resource",
3
+ "version": "0.1.0",
4
+ "description": "Resource measure for @ac-bench: CPU time, peak heap, bytes allocated, and garbage-collection count and pause time.",
5
+ "keywords": [
6
+ "allocation",
7
+ "benchmark",
8
+ "cpu",
9
+ "gc",
10
+ "memory"
11
+ ],
12
+ "homepage": "https://github.com/anthochamp/node-essentials/tree/main/bench/measure-resource#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/anthochamp/node-essentials/issues"
15
+ },
16
+ "license": "BSD-3-Clause",
17
+ "author": "Anthony Champagne <dev@anthonychampagne.fr>",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/anthochamp/node-essentials.git",
21
+ "directory": "bench/measure-resource"
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "type": "module",
27
+ "sideEffects": false,
28
+ "main": "./dist/index.js",
29
+ "exports": {
30
+ ".": "./dist/index.js",
31
+ "./plugin": "./dist/plugin.js"
32
+ },
33
+ "publishConfig": {
34
+ "main": "./dist/index.js",
35
+ "exports": {
36
+ ".": "./dist/index.js",
37
+ "./plugin": "./dist/plugin.js"
38
+ },
39
+ "access": "public"
40
+ },
41
+ "scripts": {
42
+ "compile": "tsc -p ./tsconfig.build.json --outDir dist",
43
+ "compile:check": "tsc -noEmit -p ./tsconfig.json",
44
+ "clean": "rimraf dist",
45
+ "build": "yarn clean && yarn compile",
46
+ "test": "yarn run -T vitest run",
47
+ "prepack": "yarn build"
48
+ },
49
+ "dependencies": {
50
+ "@ac-bench/core": "^0.1.0",
51
+ "@ac-kit/algo": "^0.1.0",
52
+ "@ac-kit/app-report": "^0.1.0",
53
+ "@ac-kit/core": "^0.1.0",
54
+ "@ac-kit/math-stats": "^0.1.0",
55
+ "@ac-kit/model-dataset": "^0.1.0",
56
+ "zod": "^4.5.4"
57
+ },
58
+ "devDependencies": {
59
+ "@ac-kit/tsconfig": "0.1.0",
60
+ "@ac-kit/typedoc-config": "0.1.0",
61
+ "@ac-kit/vitest-config": "0.1.0",
62
+ "@types/node": "26.5.1",
63
+ "@typescript/native": "typescript@7.0.2",
64
+ "rimraf": "6.1.3",
65
+ "typedoc": "0.28.20",
66
+ "typescript": "@typescript/typescript6@6.0.2",
67
+ "vite": "8.3.0",
68
+ "vitest": "5.0.0"
69
+ },
70
+ "engines": {
71
+ "node": ">=24"
72
+ }
73
+ }