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