@cosyte/bench 0.1.1 → 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 +52 -0
- package/README.md +59 -17
- package/dist/child-entry.mjs +108 -26
- package/dist/child-entry.mjs.map +1 -1
- package/dist/cli.mjs +233 -63
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.ts +195 -9
- package/dist/index.mjs +63 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/schema/results-v1.schema.json +122 -33
package/dist/index.d.ts
CHANGED
|
@@ -43,6 +43,15 @@ interface BenchContractShape {
|
|
|
43
43
|
readonly ALLOC_SEMI_SPACE_MIB: number;
|
|
44
44
|
/** Calls measured one by one for the peak heap. */
|
|
45
45
|
readonly PEAK_CALLS: number;
|
|
46
|
+
/**
|
|
47
|
+
* An input under this many bytes, on average, is budgeted on its median call's total peak above
|
|
48
|
+
* the empty-call floor's median, rather than its highest raw peak. On a GitHub-hosted runner the
|
|
49
|
+
* peak probe costs about 3.3 KiB on a typical empty call and 28 KiB on its highest (one call, the
|
|
50
|
+
* first), so the highest of a small input's calls reads the probe more than the code: 20 times a
|
|
51
|
+
* 1 KiB input is spent before the code runs. At 64 KiB even the probe's highest call is about 2%
|
|
52
|
+
* of a 20 times budget, so above it the raw highest peak is read as before.
|
|
53
|
+
*/
|
|
54
|
+
readonly FLOOR_INPUT_BYTES: number;
|
|
46
55
|
/** Rounds in the long run that checks the heap stays flat. */
|
|
47
56
|
readonly LONG_RUN_ROUNDS: number;
|
|
48
57
|
/** Rounds at the start of the long run left out of the slope, while caches and compiled code settle. */
|
|
@@ -59,6 +68,20 @@ interface BenchContractShape {
|
|
|
59
68
|
readonly GC_MAX_ROUNDS: number;
|
|
60
69
|
/** Fresh processes timed for the cold import, for the target and for an empty module each. */
|
|
61
70
|
readonly IMPORT_RUNS: number;
|
|
71
|
+
/**
|
|
72
|
+
* A case whose last three warmup batches each took at least this many milliseconds a pass runs on
|
|
73
|
+
* the large-case plan: every batch is one pass, so the warmup rule's aggregation cannot happen.
|
|
74
|
+
*/
|
|
75
|
+
readonly LARGE_PASS_MS: number;
|
|
76
|
+
/** Passes in each of the two windows whose medians the large-case warmup compares. */
|
|
77
|
+
readonly LARGE_WARMUP_WINDOW: number;
|
|
78
|
+
/** Hard cap on the large-case warmup, in milliseconds, from the start of the warmup. */
|
|
79
|
+
readonly LARGE_WARMUP_MAX_MS: number;
|
|
80
|
+
/**
|
|
81
|
+
* The long run also runs on a benchmark's largest size whose rounds fit in this many milliseconds,
|
|
82
|
+
* a round being at least one pass.
|
|
83
|
+
*/
|
|
84
|
+
readonly LONG_RUN_MAX_MS: number;
|
|
62
85
|
/** The timing comparison may become a required check when A/A false alarms are under this rate. */
|
|
63
86
|
readonly REQUIRED_FALSE_ALARM_RATE: number;
|
|
64
87
|
/** The confidence that the false alarm rate is under the line, for the rule above. */
|
|
@@ -199,16 +222,27 @@ declare const WARMUP: Readonly<{
|
|
|
199
222
|
STABLE_TOL: number;
|
|
200
223
|
STABLE_BATCHES: number;
|
|
201
224
|
}>;
|
|
225
|
+
/**
|
|
226
|
+
* Which warmup rule a process ended on. `standard` is ADR 0001 section 2's. `large` is the
|
|
227
|
+
* large-case plan, for a case whose every batch is a single pass: see {@link warmUp}.
|
|
228
|
+
*/
|
|
229
|
+
type WarmupPlan = "standard" | "large";
|
|
202
230
|
/** What the warmup did. */
|
|
203
231
|
interface WarmupReport {
|
|
204
|
-
/**
|
|
232
|
+
/**
|
|
233
|
+
* Whether the warmup settled: on the standard plan, three batches in a row within 5% of their
|
|
234
|
+
* median before 5 s ran out; on the large-case plan, two windows of passes with medians within 5%
|
|
235
|
+
* of each other before 30 s ran out.
|
|
236
|
+
*/
|
|
205
237
|
readonly stable: boolean;
|
|
206
238
|
/** Wall time spent warming, in milliseconds. */
|
|
207
239
|
readonly elapsedMs: number;
|
|
208
240
|
/** Passes over the corpus. */
|
|
209
241
|
readonly passes: number;
|
|
210
|
-
/** Each batch's milliseconds per pass. */
|
|
242
|
+
/** Each batch's milliseconds per pass. On the large-case plan every batch after the switch is one pass. */
|
|
211
243
|
readonly batches: readonly number[];
|
|
244
|
+
/** The rule the warmup ended on. */
|
|
245
|
+
readonly plan: WarmupPlan;
|
|
212
246
|
}
|
|
213
247
|
/** Per-call latency, or why there is none. */
|
|
214
248
|
type LatencyReport = {
|
|
@@ -235,16 +269,32 @@ type LatencyReport = {
|
|
|
235
269
|
*
|
|
236
270
|
* They read the heap through a {@link HeapProbe}, so their tests can drive them with a scripted heap.
|
|
237
271
|
* The real probe follows ADR 0001 section 8: a zero-argument `gc()` only, settled by rule rather than
|
|
238
|
-
* by a count.
|
|
272
|
+
* by a count. The measuring process also runs with `--no-concurrent-array-buffer-sweeping`, so a
|
|
273
|
+
* collection frees the ArrayBuffers it found dead before it ends, and the external memory a
|
|
274
|
+
* collection reports before and after is what it held and what it kept.
|
|
239
275
|
*
|
|
240
276
|
* @packageDocumentation
|
|
241
277
|
*/
|
|
242
278
|
|
|
243
|
-
/**
|
|
279
|
+
/**
|
|
280
|
+
* Why a memory measurement has no number. `allocation-window-gc` is no longer written: a case whose
|
|
281
|
+
* every window collects is now counted with its collections. It stays here because documents
|
|
282
|
+
* written before carry it.
|
|
283
|
+
*/
|
|
244
284
|
type MemorySkipReason = "gc-unavailable" | "gc-unsettled" | "allocation-window-gc";
|
|
285
|
+
/**
|
|
286
|
+
* How allocations were counted. `no-gc-window`: the heap's growth over windows in which no
|
|
287
|
+
* collection ran. `gc-accounted`: every window collected, so the growth over each window plus what
|
|
288
|
+
* its collections reclaimed.
|
|
289
|
+
*/
|
|
290
|
+
type AllocationMethod = "no-gc-window" | "gc-accounted";
|
|
245
291
|
/** Allocations per operation. */
|
|
246
292
|
type AllocationReport = {
|
|
247
293
|
readonly status: "measured";
|
|
294
|
+
/** How they were counted. Absent in a document written before it was recorded: `no-gc-window`. */
|
|
295
|
+
readonly method?: AllocationMethod;
|
|
296
|
+
/** Collections that ran in the two windows counted: 0 for `no-gc-window`. */
|
|
297
|
+
readonly collections?: number;
|
|
248
298
|
/** The smaller of the two windows, in operations. */
|
|
249
299
|
readonly windowOps: number;
|
|
250
300
|
/** Bytes allocated on the V8 heap per operation. */
|
|
@@ -255,14 +305,40 @@ type AllocationReport = {
|
|
|
255
305
|
readonly status: "skipped";
|
|
256
306
|
readonly reason: MemorySkipReason;
|
|
257
307
|
};
|
|
258
|
-
/** Peak
|
|
308
|
+
/** Peak memory per call: the V8 heap, external memory, and the two together. */
|
|
259
309
|
type PeakReport = {
|
|
260
310
|
readonly status: "measured";
|
|
261
311
|
readonly calls: number;
|
|
262
|
-
/** The highest peak of any call, in bytes above the settled heap before it. */
|
|
312
|
+
/** The highest V8 heap peak of any call, in bytes above the settled heap before it. */
|
|
263
313
|
readonly peakHeapBytes: number;
|
|
264
|
-
/** The median call's peak. */
|
|
314
|
+
/** The median call's V8 heap peak. */
|
|
265
315
|
readonly medianPeakHeapBytes: number;
|
|
316
|
+
/**
|
|
317
|
+
* The highest external peak of any call (Buffers, ArrayBuffers), above the external memory
|
|
318
|
+
* held before it. Absent in a document written before external memory was read.
|
|
319
|
+
*/
|
|
320
|
+
readonly peakExternalBytes?: number;
|
|
321
|
+
/** The median call's external peak. */
|
|
322
|
+
readonly medianPeakExternalBytes?: number;
|
|
323
|
+
/**
|
|
324
|
+
* The highest peak of the V8 heap and external memory together, above the two before the call:
|
|
325
|
+
* the figure a memory budget reads. Not the sum of the two peaks above, which need not come at
|
|
326
|
+
* one moment.
|
|
327
|
+
*/
|
|
328
|
+
readonly peakTotalBytes?: number;
|
|
329
|
+
/** The median call's peak of the two together. */
|
|
330
|
+
readonly medianPeakTotalBytes?: number;
|
|
331
|
+
/**
|
|
332
|
+
* Each figure above, less the same figure of the run's empty-call floor (its median), and
|
|
333
|
+
* never below zero. Absent when the run measured no floor, and in a document written before it.
|
|
334
|
+
* See {@link aboveFloor}.
|
|
335
|
+
*/
|
|
336
|
+
readonly peakHeapAboveFloorBytes?: number;
|
|
337
|
+
readonly medianPeakHeapAboveFloorBytes?: number;
|
|
338
|
+
readonly peakExternalAboveFloorBytes?: number;
|
|
339
|
+
readonly medianPeakExternalAboveFloorBytes?: number;
|
|
340
|
+
readonly peakTotalAboveFloorBytes?: number;
|
|
341
|
+
readonly medianPeakTotalAboveFloorBytes?: number;
|
|
266
342
|
} | {
|
|
267
343
|
readonly status: "skipped";
|
|
268
344
|
readonly reason: MemorySkipReason;
|
|
@@ -356,6 +432,13 @@ interface CaseComparison extends CaseRef {
|
|
|
356
432
|
readonly baseSamples: readonly (readonly number[])[];
|
|
357
433
|
/** Each round's head samples, whole. */
|
|
358
434
|
readonly headSamples: readonly (readonly number[])[];
|
|
435
|
+
/**
|
|
436
|
+
* Each round's base process: the warmup rule it ended on. Absent in a document written before it
|
|
437
|
+
* was recorded.
|
|
438
|
+
*/
|
|
439
|
+
readonly basePlans?: readonly WarmupPlan[];
|
|
440
|
+
/** Each round's head process: the warmup rule it ended on. */
|
|
441
|
+
readonly headPlans?: readonly WarmupPlan[];
|
|
359
442
|
/** Each round's `head / base`. */
|
|
360
443
|
readonly ratios: readonly number[];
|
|
361
444
|
readonly medianRatio: number | null;
|
|
@@ -474,6 +557,8 @@ type Throughput = {
|
|
|
474
557
|
readonly processes: readonly {
|
|
475
558
|
readonly samplesMsPerOp: readonly number[];
|
|
476
559
|
readonly medianMsPerOp: number;
|
|
560
|
+
/** The warmup rule the process ended on. Absent in a document written before it was recorded. */
|
|
561
|
+
readonly plan?: WarmupPlan;
|
|
477
562
|
}[];
|
|
478
563
|
/** The median of the process medians. */
|
|
479
564
|
readonly medianMsPerOp: number;
|
|
@@ -484,6 +569,19 @@ type Throughput = {
|
|
|
484
569
|
readonly status: "skipped";
|
|
485
570
|
readonly reason: string;
|
|
486
571
|
};
|
|
572
|
+
/**
|
|
573
|
+
* A part of a run that could not be measured: a case's timing or memory, or the empty-call floor.
|
|
574
|
+
* The message is the first line of what went wrong. Only the first line is kept, because the lines
|
|
575
|
+
* after it may be a measuring process's stderr, which may quote an input; the command line prints
|
|
576
|
+
* the whole of it to its own stderr.
|
|
577
|
+
*/
|
|
578
|
+
interface RunFailure {
|
|
579
|
+
readonly phase: "timing" | "memory" | "floor";
|
|
580
|
+
/** The case, or `null` for the floor. */
|
|
581
|
+
readonly benchmark: string | null;
|
|
582
|
+
readonly size: string | null;
|
|
583
|
+
readonly message: string;
|
|
584
|
+
}
|
|
487
585
|
/** Everything a reading measured for one case. */
|
|
488
586
|
interface CaseReading extends CaseRef {
|
|
489
587
|
readonly inputsPerPass: number;
|
|
@@ -499,9 +597,59 @@ interface ReadingBody {
|
|
|
499
597
|
/** Fresh processes timed per case. */
|
|
500
598
|
readonly processes: number;
|
|
501
599
|
readonly cases: readonly CaseReading[];
|
|
600
|
+
/**
|
|
601
|
+
* The peak of an empty call, measured once per run through the same probe as the cases: what the
|
|
602
|
+
* probe itself costs. Each case's peak also gives its figures above this one. `null` when memory
|
|
603
|
+
* was not measured or the floor could not be. Absent in a document written before it.
|
|
604
|
+
*/
|
|
605
|
+
readonly memoryFloor?: PeakReport | null;
|
|
606
|
+
/**
|
|
607
|
+
* What could not be measured. A case whose timing failed is here and not in `cases`; one whose
|
|
608
|
+
* memory failed is in both, with the memory it could not read as `null`. Every other case is
|
|
609
|
+
* written as usual. Empty when everything measured; absent in a document written before it.
|
|
610
|
+
*/
|
|
611
|
+
readonly failures?: readonly RunFailure[];
|
|
502
612
|
/** `null` when import cost was not asked for. */
|
|
503
613
|
readonly importCost: ImportCostReport | null;
|
|
504
614
|
}
|
|
615
|
+
/**
|
|
616
|
+
* The peak, in bytes, a memory budget reads for a case, or `null` when the case has none. A case
|
|
617
|
+
* whose inputs average under `FLOOR_INPUT_BYTES` (its bytes a pass over its inputs a pass) reads its
|
|
618
|
+
* median call's total peak above the run's empty-call floor (`medianPeakTotalAboveFloorBytes`); any
|
|
619
|
+
* other case, and one whose suite gave no byte count, reads the highest raw total peak (the V8 heap
|
|
620
|
+
* and external memory together). The probe costs a few KiB on a typical call and tens of KiB on its
|
|
621
|
+
* highest, which is most of the budget of a small input and nothing the code under test did, and a
|
|
622
|
+
* rounding error on a large one. A document without a floor reads the raw highest.
|
|
623
|
+
*
|
|
624
|
+
* @example
|
|
625
|
+
* ```ts
|
|
626
|
+
* import { budgetPeakBytes } from "@cosyte/bench";
|
|
627
|
+
*
|
|
628
|
+
* // 300 bytes an input, and a floor whose median call is 3,400 bytes: the highest call read
|
|
629
|
+
* // 30,000 bytes, most of it the probe, and the median call 6,000 above the floor.
|
|
630
|
+
* budgetPeakBytes({
|
|
631
|
+
* benchmark: "parse",
|
|
632
|
+
* size: "300 B",
|
|
633
|
+
* inputsPerPass: 1,
|
|
634
|
+
* bytesPerPass: 300,
|
|
635
|
+
* throughput: { status: "skipped", reason: "warmup-unstable" },
|
|
636
|
+
* latency: null,
|
|
637
|
+
* allocations: null,
|
|
638
|
+
* peak: {
|
|
639
|
+
* status: "measured",
|
|
640
|
+
* calls: 20,
|
|
641
|
+
* peakHeapBytes: 30_000,
|
|
642
|
+
* medianPeakHeapBytes: 9_400,
|
|
643
|
+
* peakTotalBytes: 30_000,
|
|
644
|
+
* medianPeakTotalBytes: 9_400,
|
|
645
|
+
* peakTotalAboveFloorBytes: 26_600,
|
|
646
|
+
* medianPeakTotalAboveFloorBytes: 6_000,
|
|
647
|
+
* },
|
|
648
|
+
* longRun: null,
|
|
649
|
+
* }); // => 6000
|
|
650
|
+
* ```
|
|
651
|
+
*/
|
|
652
|
+
declare function budgetPeakBytes(c: CaseReading): number | null;
|
|
505
653
|
|
|
506
654
|
/**
|
|
507
655
|
* The machine a measurement was taken on. Every results document carries it, because a number
|
|
@@ -832,6 +980,18 @@ declare const RESULTS_SCHEMA: {
|
|
|
832
980
|
readonly medianRatio: {
|
|
833
981
|
readonly type: readonly ["number", "null"];
|
|
834
982
|
};
|
|
983
|
+
readonly basePlans: {
|
|
984
|
+
readonly type: "array";
|
|
985
|
+
readonly items: {
|
|
986
|
+
readonly enum: readonly ["standard", "large"];
|
|
987
|
+
};
|
|
988
|
+
};
|
|
989
|
+
readonly headPlans: {
|
|
990
|
+
readonly type: "array";
|
|
991
|
+
readonly items: {
|
|
992
|
+
readonly enum: readonly ["standard", "large"];
|
|
993
|
+
};
|
|
994
|
+
};
|
|
835
995
|
readonly interval: {
|
|
836
996
|
readonly $ref: "#/$defs/interval";
|
|
837
997
|
};
|
|
@@ -959,6 +1119,25 @@ declare const RESULTS_SCHEMA: {
|
|
|
959
1119
|
};
|
|
960
1120
|
};
|
|
961
1121
|
};
|
|
1122
|
+
readonly peak: Schema;
|
|
1123
|
+
readonly failure: {
|
|
1124
|
+
readonly type: "object";
|
|
1125
|
+
readonly required: readonly ["phase", "benchmark", "size", "message"];
|
|
1126
|
+
readonly properties: {
|
|
1127
|
+
readonly phase: {
|
|
1128
|
+
readonly enum: readonly ["timing", "memory", "floor"];
|
|
1129
|
+
};
|
|
1130
|
+
readonly benchmark: {
|
|
1131
|
+
readonly type: readonly ["string", "null"];
|
|
1132
|
+
};
|
|
1133
|
+
readonly size: {
|
|
1134
|
+
readonly type: readonly ["string", "null"];
|
|
1135
|
+
};
|
|
1136
|
+
readonly message: {
|
|
1137
|
+
readonly type: "string";
|
|
1138
|
+
};
|
|
1139
|
+
};
|
|
1140
|
+
};
|
|
962
1141
|
readonly reading: {
|
|
963
1142
|
readonly type: "object";
|
|
964
1143
|
readonly required: readonly ["processes", "cases", "importCost"];
|
|
@@ -973,6 +1152,13 @@ declare const RESULTS_SCHEMA: {
|
|
|
973
1152
|
readonly $ref: "#/$defs/caseReading";
|
|
974
1153
|
};
|
|
975
1154
|
};
|
|
1155
|
+
readonly memoryFloor: Schema;
|
|
1156
|
+
readonly failures: {
|
|
1157
|
+
readonly type: "array";
|
|
1158
|
+
readonly items: {
|
|
1159
|
+
readonly $ref: "#/$defs/failure";
|
|
1160
|
+
};
|
|
1161
|
+
};
|
|
976
1162
|
readonly importCost: Schema;
|
|
977
1163
|
};
|
|
978
1164
|
};
|
|
@@ -1039,5 +1225,5 @@ declare class BenchError extends Error {
|
|
|
1039
1225
|
readonly name = "BenchError";
|
|
1040
1226
|
}
|
|
1041
1227
|
|
|
1042
|
-
export { BENCH_CONTRACT, BenchError, RESULTS_SCHEMA, SCHEMA_VERSION, defineBenchmark, defineSuite, judge, readResults };
|
|
1043
|
-
export type { AllocationReport, AnyBenchmark, BenchContractShape, BenchCorpus, BenchSize, BenchSuite, Benchmark, BuildRef, CalibrationBody, CaseComparison, CaseReading, CaseRef, ComparisonBody, CountedRate, Environment, ImportCostReport, LatencyReport, LoadedFile, LongRunReport, MemorySkipReason, PeakReport, ReadingBody, ResultsDocument, Runner, Throughput, Verdict, WarmupReport };
|
|
1228
|
+
export { BENCH_CONTRACT, BenchError, RESULTS_SCHEMA, SCHEMA_VERSION, budgetPeakBytes, defineBenchmark, defineSuite, judge, readResults };
|
|
1229
|
+
export type { AllocationReport, AnyBenchmark, BenchContractShape, BenchCorpus, BenchSize, BenchSuite, Benchmark, BuildRef, CalibrationBody, CaseComparison, CaseReading, CaseRef, ComparisonBody, CountedRate, Environment, ImportCostReport, LatencyReport, LoadedFile, LongRunReport, MemorySkipReason, PeakReport, ReadingBody, ResultsDocument, RunFailure, Runner, Throughput, Verdict, WarmupReport };
|
package/dist/index.mjs
CHANGED
|
@@ -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,
|
|
@@ -29,6 +30,10 @@ var BENCH_CONTRACT = Object.freeze({
|
|
|
29
30
|
GC_SETTLE_ROUNDS: 2,
|
|
30
31
|
GC_MAX_ROUNDS: 7,
|
|
31
32
|
IMPORT_RUNS: 20,
|
|
33
|
+
LARGE_PASS_MS: 50,
|
|
34
|
+
LARGE_WARMUP_WINDOW: 5,
|
|
35
|
+
LARGE_WARMUP_MAX_MS: 3e4,
|
|
36
|
+
LONG_RUN_MAX_MS: 6e4,
|
|
32
37
|
REQUIRED_FALSE_ALARM_RATE: 0.01,
|
|
33
38
|
REQUIRED_CONFIDENCE: 0.95
|
|
34
39
|
});
|
|
@@ -138,6 +143,19 @@ function judge(ratios, threshold = BENCH_CONTRACT.THRESHOLD, alpha = BENCH_CONTR
|
|
|
138
143
|
else verdict = "inconclusive";
|
|
139
144
|
return { verdict, interval, medianRatio };
|
|
140
145
|
}
|
|
146
|
+
|
|
147
|
+
// src/reading.ts
|
|
148
|
+
function budgetPeakBytes(c) {
|
|
149
|
+
const p = c.peak;
|
|
150
|
+
return p === null || p.status !== "measured" ? null : budgetOf(c, p);
|
|
151
|
+
}
|
|
152
|
+
function budgetOf(c, p) {
|
|
153
|
+
const raw = p.peakTotalBytes ?? p.peakHeapBytes;
|
|
154
|
+
if (c.bytesPerPass === null) return raw;
|
|
155
|
+
const perInput = c.bytesPerPass / c.inputsPerPass;
|
|
156
|
+
if (perInput >= BENCH_CONTRACT.FLOOR_INPUT_BYTES) return raw;
|
|
157
|
+
return p.medianPeakTotalAboveFloorBytes ?? raw;
|
|
158
|
+
}
|
|
141
159
|
var SCHEMA_VERSION = 1;
|
|
142
160
|
var num = { type: "number" };
|
|
143
161
|
var int = { type: "integer", minimum: 0 };
|
|
@@ -151,6 +169,7 @@ var skipped = {
|
|
|
151
169
|
properties: { status: { const: "skipped" }, reason: str }
|
|
152
170
|
};
|
|
153
171
|
var measuredOrSkipped = (measured) => ({ oneOf: [measured, skipped] });
|
|
172
|
+
var plan = { enum: ["standard", "large"] };
|
|
154
173
|
var orNull = (s) => ({ oneOf: [{ type: "null" }, s] });
|
|
155
174
|
var RESULTS_SCHEMA = {
|
|
156
175
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -267,6 +286,9 @@ var RESULTS_SCHEMA = {
|
|
|
267
286
|
headMsPerOp: nums,
|
|
268
287
|
ratios: nums,
|
|
269
288
|
medianRatio: numOrNull,
|
|
289
|
+
// Added within version 1, so not required: a document written before them is still valid.
|
|
290
|
+
basePlans: { type: "array", items: plan },
|
|
291
|
+
headPlans: { type: "array", items: plan },
|
|
270
292
|
interval: { $ref: "#/$defs/interval" },
|
|
271
293
|
verdict: {
|
|
272
294
|
enum: ["regression", "improvement", "no-regression", "inconclusive", "skipped"]
|
|
@@ -328,12 +350,46 @@ var RESULTS_SCHEMA = {
|
|
|
328
350
|
}
|
|
329
351
|
}
|
|
330
352
|
},
|
|
353
|
+
peak: measuredOrSkipped({
|
|
354
|
+
type: "object",
|
|
355
|
+
required: ["status", "calls", "peakHeapBytes", "medianPeakHeapBytes"],
|
|
356
|
+
properties: {
|
|
357
|
+
status: { const: "measured" },
|
|
358
|
+
calls: int,
|
|
359
|
+
peakHeapBytes: num,
|
|
360
|
+
medianPeakHeapBytes: num,
|
|
361
|
+
// Added within version 1, so not required.
|
|
362
|
+
peakExternalBytes: num,
|
|
363
|
+
medianPeakExternalBytes: num,
|
|
364
|
+
peakTotalBytes: num,
|
|
365
|
+
medianPeakTotalBytes: num,
|
|
366
|
+
peakHeapAboveFloorBytes: num,
|
|
367
|
+
medianPeakHeapAboveFloorBytes: num,
|
|
368
|
+
peakExternalAboveFloorBytes: num,
|
|
369
|
+
medianPeakExternalAboveFloorBytes: num,
|
|
370
|
+
peakTotalAboveFloorBytes: num,
|
|
371
|
+
medianPeakTotalAboveFloorBytes: num
|
|
372
|
+
}
|
|
373
|
+
}),
|
|
374
|
+
failure: {
|
|
375
|
+
type: "object",
|
|
376
|
+
required: ["phase", "benchmark", "size", "message"],
|
|
377
|
+
properties: {
|
|
378
|
+
phase: { enum: ["timing", "memory", "floor"] },
|
|
379
|
+
benchmark: strOrNull,
|
|
380
|
+
size: strOrNull,
|
|
381
|
+
message: str
|
|
382
|
+
}
|
|
383
|
+
},
|
|
331
384
|
reading: {
|
|
332
385
|
type: "object",
|
|
333
386
|
required: ["processes", "cases", "importCost"],
|
|
334
387
|
properties: {
|
|
335
388
|
processes: int,
|
|
336
389
|
cases: { type: "array", items: { $ref: "#/$defs/caseReading" } },
|
|
390
|
+
// Added within version 1, so not required.
|
|
391
|
+
memoryFloor: orNull({ $ref: "#/$defs/peak" }),
|
|
392
|
+
failures: { type: "array", items: { $ref: "#/$defs/failure" } },
|
|
337
393
|
importCost: orNull({
|
|
338
394
|
type: "object",
|
|
339
395
|
required: [
|
|
@@ -396,7 +452,8 @@ var RESULTS_SCHEMA = {
|
|
|
396
452
|
items: {
|
|
397
453
|
type: "object",
|
|
398
454
|
required: ["samplesMsPerOp", "medianMsPerOp"],
|
|
399
|
-
|
|
455
|
+
// `plan` was added within version 1, so it is not required.
|
|
456
|
+
properties: { samplesMsPerOp: nums, medianMsPerOp: num, plan }
|
|
400
457
|
}
|
|
401
458
|
},
|
|
402
459
|
medianMsPerOp: num,
|
|
@@ -423,24 +480,16 @@ var RESULTS_SCHEMA = {
|
|
|
423
480
|
required: ["status", "windowOps", "heapBytesPerOp", "externalBytesPerOp"],
|
|
424
481
|
properties: {
|
|
425
482
|
status: { const: "measured" },
|
|
483
|
+
// Added within version 1, so not required.
|
|
484
|
+
method: { enum: ["no-gc-window", "gc-accounted"] },
|
|
485
|
+
collections: int,
|
|
426
486
|
windowOps: int,
|
|
427
487
|
heapBytesPerOp: num,
|
|
428
488
|
externalBytesPerOp: num
|
|
429
489
|
}
|
|
430
490
|
})
|
|
431
491
|
),
|
|
432
|
-
peak: orNull(
|
|
433
|
-
measuredOrSkipped({
|
|
434
|
-
type: "object",
|
|
435
|
-
required: ["status", "calls", "peakHeapBytes", "medianPeakHeapBytes"],
|
|
436
|
-
properties: {
|
|
437
|
-
status: { const: "measured" },
|
|
438
|
-
calls: int,
|
|
439
|
-
peakHeapBytes: num,
|
|
440
|
-
medianPeakHeapBytes: num
|
|
441
|
-
}
|
|
442
|
-
})
|
|
443
|
-
),
|
|
492
|
+
peak: orNull({ $ref: "#/$defs/peak" }),
|
|
444
493
|
longRun: orNull(
|
|
445
494
|
measuredOrSkipped({
|
|
446
495
|
type: "object",
|
|
@@ -550,6 +599,6 @@ Object.freeze({
|
|
|
550
599
|
importer: fileURLToPath(new URL("./import-entry.mjs", import.meta.url))
|
|
551
600
|
});
|
|
552
601
|
|
|
553
|
-
export { BENCH_CONTRACT, BenchError, RESULTS_SCHEMA, SCHEMA_VERSION, defineBenchmark, defineSuite, judge, readResults };
|
|
602
|
+
export { BENCH_CONTRACT, BenchError, RESULTS_SCHEMA, SCHEMA_VERSION, budgetPeakBytes, defineBenchmark, defineSuite, judge, readResults };
|
|
554
603
|
//# sourceMappingURL=index.mjs.map
|
|
555
604
|
//# sourceMappingURL=index.mjs.map
|