@cosyte/bench 0.1.1 → 0.1.2
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 +26 -0
- package/README.md +25 -11
- package/dist/child-entry.mjs +100 -26
- package/dist/child-entry.mjs.map +1 -1
- package/dist/cli.mjs +94 -45
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.ts +84 -7
- package/dist/index.mjs +19 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/schema/results-v1.schema.json +34 -0
package/dist/index.d.ts
CHANGED
|
@@ -59,6 +59,20 @@ interface BenchContractShape {
|
|
|
59
59
|
readonly GC_MAX_ROUNDS: number;
|
|
60
60
|
/** Fresh processes timed for the cold import, for the target and for an empty module each. */
|
|
61
61
|
readonly IMPORT_RUNS: number;
|
|
62
|
+
/**
|
|
63
|
+
* A case whose last three warmup batches each took at least this many milliseconds a pass runs on
|
|
64
|
+
* the large-case plan: every batch is one pass, so the warmup rule's aggregation cannot happen.
|
|
65
|
+
*/
|
|
66
|
+
readonly LARGE_PASS_MS: number;
|
|
67
|
+
/** Passes in each of the two windows whose medians the large-case warmup compares. */
|
|
68
|
+
readonly LARGE_WARMUP_WINDOW: number;
|
|
69
|
+
/** Hard cap on the large-case warmup, in milliseconds, from the start of the warmup. */
|
|
70
|
+
readonly LARGE_WARMUP_MAX_MS: number;
|
|
71
|
+
/**
|
|
72
|
+
* The long run also runs on a benchmark's largest size whose rounds fit in this many milliseconds,
|
|
73
|
+
* a round being at least one pass.
|
|
74
|
+
*/
|
|
75
|
+
readonly LONG_RUN_MAX_MS: number;
|
|
62
76
|
/** The timing comparison may become a required check when A/A false alarms are under this rate. */
|
|
63
77
|
readonly REQUIRED_FALSE_ALARM_RATE: number;
|
|
64
78
|
/** The confidence that the false alarm rate is under the line, for the rule above. */
|
|
@@ -199,16 +213,27 @@ declare const WARMUP: Readonly<{
|
|
|
199
213
|
STABLE_TOL: number;
|
|
200
214
|
STABLE_BATCHES: number;
|
|
201
215
|
}>;
|
|
216
|
+
/**
|
|
217
|
+
* Which warmup rule a process ended on. `standard` is ADR 0001 section 2's. `large` is the
|
|
218
|
+
* large-case plan, for a case whose every batch is a single pass: see {@link warmUp}.
|
|
219
|
+
*/
|
|
220
|
+
type WarmupPlan = "standard" | "large";
|
|
202
221
|
/** What the warmup did. */
|
|
203
222
|
interface WarmupReport {
|
|
204
|
-
/**
|
|
223
|
+
/**
|
|
224
|
+
* Whether the warmup settled: on the standard plan, three batches in a row within 5% of their
|
|
225
|
+
* median before 5 s ran out; on the large-case plan, two windows of passes with medians within 5%
|
|
226
|
+
* of each other before 30 s ran out.
|
|
227
|
+
*/
|
|
205
228
|
readonly stable: boolean;
|
|
206
229
|
/** Wall time spent warming, in milliseconds. */
|
|
207
230
|
readonly elapsedMs: number;
|
|
208
231
|
/** Passes over the corpus. */
|
|
209
232
|
readonly passes: number;
|
|
210
|
-
/** Each batch's milliseconds per pass. */
|
|
233
|
+
/** Each batch's milliseconds per pass. On the large-case plan every batch after the switch is one pass. */
|
|
211
234
|
readonly batches: readonly number[];
|
|
235
|
+
/** The rule the warmup ended on. */
|
|
236
|
+
readonly plan: WarmupPlan;
|
|
212
237
|
}
|
|
213
238
|
/** Per-call latency, or why there is none. */
|
|
214
239
|
type LatencyReport = {
|
|
@@ -235,16 +260,32 @@ type LatencyReport = {
|
|
|
235
260
|
*
|
|
236
261
|
* They read the heap through a {@link HeapProbe}, so their tests can drive them with a scripted heap.
|
|
237
262
|
* The real probe follows ADR 0001 section 8: a zero-argument `gc()` only, settled by rule rather than
|
|
238
|
-
* by a count.
|
|
263
|
+
* by a count. The measuring process also runs with `--no-concurrent-array-buffer-sweeping`, so a
|
|
264
|
+
* collection frees the ArrayBuffers it found dead before it ends, and the external memory a
|
|
265
|
+
* collection reports before and after is what it held and what it kept.
|
|
239
266
|
*
|
|
240
267
|
* @packageDocumentation
|
|
241
268
|
*/
|
|
242
269
|
|
|
243
|
-
/**
|
|
270
|
+
/**
|
|
271
|
+
* Why a memory measurement has no number. `allocation-window-gc` is no longer written: a case whose
|
|
272
|
+
* every window collects is now counted with its collections. It stays here because documents
|
|
273
|
+
* written before carry it.
|
|
274
|
+
*/
|
|
244
275
|
type MemorySkipReason = "gc-unavailable" | "gc-unsettled" | "allocation-window-gc";
|
|
276
|
+
/**
|
|
277
|
+
* How allocations were counted. `no-gc-window`: the heap's growth over windows in which no
|
|
278
|
+
* collection ran. `gc-accounted`: every window collected, so the growth over each window plus what
|
|
279
|
+
* its collections reclaimed.
|
|
280
|
+
*/
|
|
281
|
+
type AllocationMethod = "no-gc-window" | "gc-accounted";
|
|
245
282
|
/** Allocations per operation. */
|
|
246
283
|
type AllocationReport = {
|
|
247
284
|
readonly status: "measured";
|
|
285
|
+
/** How they were counted. Absent in a document written before it was recorded: `no-gc-window`. */
|
|
286
|
+
readonly method?: AllocationMethod;
|
|
287
|
+
/** Collections that ran in the two windows counted: 0 for `no-gc-window`. */
|
|
288
|
+
readonly collections?: number;
|
|
248
289
|
/** The smaller of the two windows, in operations. */
|
|
249
290
|
readonly windowOps: number;
|
|
250
291
|
/** Bytes allocated on the V8 heap per operation. */
|
|
@@ -255,14 +296,29 @@ type AllocationReport = {
|
|
|
255
296
|
readonly status: "skipped";
|
|
256
297
|
readonly reason: MemorySkipReason;
|
|
257
298
|
};
|
|
258
|
-
/** Peak
|
|
299
|
+
/** Peak memory per call: the V8 heap, external memory, and the two together. */
|
|
259
300
|
type PeakReport = {
|
|
260
301
|
readonly status: "measured";
|
|
261
302
|
readonly calls: number;
|
|
262
|
-
/** The highest peak of any call, in bytes above the settled heap before it. */
|
|
303
|
+
/** The highest V8 heap peak of any call, in bytes above the settled heap before it. */
|
|
263
304
|
readonly peakHeapBytes: number;
|
|
264
|
-
/** The median call's peak. */
|
|
305
|
+
/** The median call's V8 heap peak. */
|
|
265
306
|
readonly medianPeakHeapBytes: number;
|
|
307
|
+
/**
|
|
308
|
+
* The highest external peak of any call (Buffers, ArrayBuffers), above the external memory
|
|
309
|
+
* held before it. Absent in a document written before external memory was read.
|
|
310
|
+
*/
|
|
311
|
+
readonly peakExternalBytes?: number;
|
|
312
|
+
/** The median call's external peak. */
|
|
313
|
+
readonly medianPeakExternalBytes?: number;
|
|
314
|
+
/**
|
|
315
|
+
* The highest peak of the V8 heap and external memory together, above the two before the call:
|
|
316
|
+
* the figure a memory budget reads. Not the sum of the two peaks above, which need not come at
|
|
317
|
+
* one moment.
|
|
318
|
+
*/
|
|
319
|
+
readonly peakTotalBytes?: number;
|
|
320
|
+
/** The median call's peak of the two together. */
|
|
321
|
+
readonly medianPeakTotalBytes?: number;
|
|
266
322
|
} | {
|
|
267
323
|
readonly status: "skipped";
|
|
268
324
|
readonly reason: MemorySkipReason;
|
|
@@ -356,6 +412,13 @@ interface CaseComparison extends CaseRef {
|
|
|
356
412
|
readonly baseSamples: readonly (readonly number[])[];
|
|
357
413
|
/** Each round's head samples, whole. */
|
|
358
414
|
readonly headSamples: readonly (readonly number[])[];
|
|
415
|
+
/**
|
|
416
|
+
* Each round's base process: the warmup rule it ended on. Absent in a document written before it
|
|
417
|
+
* was recorded.
|
|
418
|
+
*/
|
|
419
|
+
readonly basePlans?: readonly WarmupPlan[];
|
|
420
|
+
/** Each round's head process: the warmup rule it ended on. */
|
|
421
|
+
readonly headPlans?: readonly WarmupPlan[];
|
|
359
422
|
/** Each round's `head / base`. */
|
|
360
423
|
readonly ratios: readonly number[];
|
|
361
424
|
readonly medianRatio: number | null;
|
|
@@ -474,6 +537,8 @@ type Throughput = {
|
|
|
474
537
|
readonly processes: readonly {
|
|
475
538
|
readonly samplesMsPerOp: readonly number[];
|
|
476
539
|
readonly medianMsPerOp: number;
|
|
540
|
+
/** The warmup rule the process ended on. Absent in a document written before it was recorded. */
|
|
541
|
+
readonly plan?: WarmupPlan;
|
|
477
542
|
}[];
|
|
478
543
|
/** The median of the process medians. */
|
|
479
544
|
readonly medianMsPerOp: number;
|
|
@@ -832,6 +897,18 @@ declare const RESULTS_SCHEMA: {
|
|
|
832
897
|
readonly medianRatio: {
|
|
833
898
|
readonly type: readonly ["number", "null"];
|
|
834
899
|
};
|
|
900
|
+
readonly basePlans: {
|
|
901
|
+
readonly type: "array";
|
|
902
|
+
readonly items: {
|
|
903
|
+
readonly enum: readonly ["standard", "large"];
|
|
904
|
+
};
|
|
905
|
+
};
|
|
906
|
+
readonly headPlans: {
|
|
907
|
+
readonly type: "array";
|
|
908
|
+
readonly items: {
|
|
909
|
+
readonly enum: readonly ["standard", "large"];
|
|
910
|
+
};
|
|
911
|
+
};
|
|
835
912
|
readonly interval: {
|
|
836
913
|
readonly $ref: "#/$defs/interval";
|
|
837
914
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -29,6 +29,10 @@ var BENCH_CONTRACT = Object.freeze({
|
|
|
29
29
|
GC_SETTLE_ROUNDS: 2,
|
|
30
30
|
GC_MAX_ROUNDS: 7,
|
|
31
31
|
IMPORT_RUNS: 20,
|
|
32
|
+
LARGE_PASS_MS: 50,
|
|
33
|
+
LARGE_WARMUP_WINDOW: 5,
|
|
34
|
+
LARGE_WARMUP_MAX_MS: 3e4,
|
|
35
|
+
LONG_RUN_MAX_MS: 6e4,
|
|
32
36
|
REQUIRED_FALSE_ALARM_RATE: 0.01,
|
|
33
37
|
REQUIRED_CONFIDENCE: 0.95
|
|
34
38
|
});
|
|
@@ -151,6 +155,7 @@ var skipped = {
|
|
|
151
155
|
properties: { status: { const: "skipped" }, reason: str }
|
|
152
156
|
};
|
|
153
157
|
var measuredOrSkipped = (measured) => ({ oneOf: [measured, skipped] });
|
|
158
|
+
var plan = { enum: ["standard", "large"] };
|
|
154
159
|
var orNull = (s) => ({ oneOf: [{ type: "null" }, s] });
|
|
155
160
|
var RESULTS_SCHEMA = {
|
|
156
161
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -267,6 +272,9 @@ var RESULTS_SCHEMA = {
|
|
|
267
272
|
headMsPerOp: nums,
|
|
268
273
|
ratios: nums,
|
|
269
274
|
medianRatio: numOrNull,
|
|
275
|
+
// Added within version 1, so not required: a document written before them is still valid.
|
|
276
|
+
basePlans: { type: "array", items: plan },
|
|
277
|
+
headPlans: { type: "array", items: plan },
|
|
270
278
|
interval: { $ref: "#/$defs/interval" },
|
|
271
279
|
verdict: {
|
|
272
280
|
enum: ["regression", "improvement", "no-regression", "inconclusive", "skipped"]
|
|
@@ -396,7 +404,8 @@ var RESULTS_SCHEMA = {
|
|
|
396
404
|
items: {
|
|
397
405
|
type: "object",
|
|
398
406
|
required: ["samplesMsPerOp", "medianMsPerOp"],
|
|
399
|
-
|
|
407
|
+
// `plan` was added within version 1, so it is not required.
|
|
408
|
+
properties: { samplesMsPerOp: nums, medianMsPerOp: num, plan }
|
|
400
409
|
}
|
|
401
410
|
},
|
|
402
411
|
medianMsPerOp: num,
|
|
@@ -423,6 +432,9 @@ var RESULTS_SCHEMA = {
|
|
|
423
432
|
required: ["status", "windowOps", "heapBytesPerOp", "externalBytesPerOp"],
|
|
424
433
|
properties: {
|
|
425
434
|
status: { const: "measured" },
|
|
435
|
+
// Added within version 1, so not required.
|
|
436
|
+
method: { enum: ["no-gc-window", "gc-accounted"] },
|
|
437
|
+
collections: int,
|
|
426
438
|
windowOps: int,
|
|
427
439
|
heapBytesPerOp: num,
|
|
428
440
|
externalBytesPerOp: num
|
|
@@ -437,7 +449,12 @@ var RESULTS_SCHEMA = {
|
|
|
437
449
|
status: { const: "measured" },
|
|
438
450
|
calls: int,
|
|
439
451
|
peakHeapBytes: num,
|
|
440
|
-
medianPeakHeapBytes: num
|
|
452
|
+
medianPeakHeapBytes: num,
|
|
453
|
+
// Added within version 1, so not required.
|
|
454
|
+
peakExternalBytes: num,
|
|
455
|
+
medianPeakExternalBytes: num,
|
|
456
|
+
peakTotalBytes: num,
|
|
457
|
+
medianPeakTotalBytes: num
|
|
441
458
|
}
|
|
442
459
|
})
|
|
443
460
|
),
|