benchforge 0.1.9 → 0.2.4
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 +20 -0
- package/README.md +99 -260
- package/bin/benchforge +1 -2
- package/dist/AnalyzeArchive-8NCJhmhS.mjs +145 -0
- package/dist/AnalyzeArchive-8NCJhmhS.mjs.map +1 -0
- package/dist/BenchMatrix-BZVrBB_h.mjs +1050 -0
- package/dist/BenchMatrix-BZVrBB_h.mjs.map +1 -0
- package/dist/BenchRunner-DglX1NOn.d.mts +302 -0
- package/dist/CoverageSampler-D5T9DRqe.mjs +27 -0
- package/dist/CoverageSampler-D5T9DRqe.mjs.map +1 -0
- package/dist/Formatters-BWj3d4sv.mjs +95 -0
- package/dist/Formatters-BWj3d4sv.mjs.map +1 -0
- package/dist/{HeapSampler-B8dtKHn1.mjs → HeapSampler-Dq-hpXem.mjs} +4 -4
- package/dist/HeapSampler-Dq-hpXem.mjs.map +1 -0
- package/dist/RunBenchCLI-C17DrJz8.mjs +3075 -0
- package/dist/RunBenchCLI-C17DrJz8.mjs.map +1 -0
- package/dist/StatisticalUtils-BD92crgM.mjs +255 -0
- package/dist/StatisticalUtils-BD92crgM.mjs.map +1 -0
- package/dist/TimeSampler-Ds8n7l2B.mjs +29 -0
- package/dist/TimeSampler-Ds8n7l2B.mjs.map +1 -0
- package/dist/ViewerServer-BJhdnxlN.mjs +639 -0
- package/dist/ViewerServer-BJhdnxlN.mjs.map +1 -0
- package/dist/ViewerServer-CuMNdNBz.mjs +2 -0
- package/dist/bin/benchforge.mjs +4 -5
- package/dist/bin/benchforge.mjs.map +1 -1
- package/dist/index.d.mts +731 -522
- package/dist/index.mjs +98 -3
- package/dist/index.mjs.map +1 -0
- package/dist/runners/WorkerScript.d.mts +12 -4
- package/dist/runners/WorkerScript.mjs +92 -120
- package/dist/runners/WorkerScript.mjs.map +1 -1
- package/dist/viewer/assets/CIPlot-BkOvMoMa.js +1 -0
- package/dist/viewer/assets/HistogramKde-CmSyUFY0.js +1 -0
- package/dist/viewer/assets/LegendUtils-BJpbn_jr.js +55 -0
- package/dist/viewer/assets/SampleTimeSeries-C4VBhXr3.js +1 -0
- package/dist/viewer/assets/index-Br9bp_cX.js +153 -0
- package/dist/viewer/assets/index-NzXXe_CC.css +1 -0
- package/dist/viewer/index.html +19 -0
- package/dist/viewer/speedscope/LICENSE +21 -0
- package/dist/viewer/speedscope/SourceCodePro-Regular.ttf-ILST5JV6.woff2 +0 -0
- package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js +2 -0
- package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js.map +7 -0
- package/dist/viewer/speedscope/favicon-16x16-VSI62OPJ.png +0 -0
- package/dist/viewer/speedscope/favicon-32x32-3EB2YCUY.png +0 -0
- package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js +2 -0
- package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js.map +7 -0
- package/dist/viewer/speedscope/favicon-FOKUP5Y5.ico +0 -0
- package/dist/viewer/speedscope/favicon-M34RF7BI.js +2 -0
- package/dist/viewer/speedscope/favicon-M34RF7BI.js.map +7 -0
- package/dist/viewer/speedscope/file-format-schema.json +274 -0
- package/dist/viewer/speedscope/index.html +19 -0
- package/dist/viewer/speedscope/jfrview_bg-BLJXNNQB.wasm +0 -0
- package/dist/viewer/speedscope/perf-vertx-stacks-01-collapsed-all-ZNUIGAJL.txt +199 -0
- package/dist/viewer/speedscope/release.txt +3 -0
- package/dist/viewer/speedscope/source-code-pro.LICENSE.md +93 -0
- package/dist/viewer/speedscope/speedscope-GHPHNKXC.css +2 -0
- package/dist/viewer/speedscope/speedscope-GHPHNKXC.css.map +7 -0
- package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js +212 -0
- package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js.map +7 -0
- package/package.json +52 -26
- package/src/bin/benchforge.ts +2 -2
- package/src/cli/AnalyzeArchive.ts +232 -0
- package/src/cli/BrowserBench.ts +322 -0
- package/src/cli/CliArgs.ts +164 -48
- package/src/cli/CliExport.ts +179 -0
- package/src/cli/CliOptions.ts +147 -0
- package/src/cli/CliReport.ts +197 -0
- package/src/cli/FilterBenchmarks.ts +18 -30
- package/src/cli/RunBenchCLI.ts +138 -844
- package/src/cli/SuiteRunner.ts +160 -0
- package/src/cli/ViewerServer.ts +282 -0
- package/src/export/AllocExport.ts +121 -0
- package/src/export/ArchiveExport.ts +146 -0
- package/src/export/ArchiveFormat.ts +50 -0
- package/src/export/CoverageExport.ts +148 -0
- package/src/export/EditorUri.ts +10 -0
- package/src/export/PerfettoExport.ts +91 -126
- package/src/export/SpeedscopeTypes.ts +98 -0
- package/src/export/TimeExport.ts +115 -0
- package/src/index.ts +87 -62
- package/src/matrix/BenchMatrix.ts +230 -0
- package/src/matrix/CaseLoader.ts +8 -6
- package/src/matrix/MatrixDirRunner.ts +153 -0
- package/src/matrix/MatrixFilter.ts +55 -53
- package/src/matrix/MatrixInlineRunner.ts +50 -0
- package/src/matrix/MatrixReport.ts +94 -254
- package/src/matrix/VariantLoader.ts +9 -9
- package/src/profiling/browser/BenchLoop.ts +51 -0
- package/src/profiling/browser/BrowserCDP.ts +133 -0
- package/src/profiling/browser/BrowserGcStats.ts +33 -0
- package/src/profiling/browser/BrowserProfiler.ts +160 -0
- package/src/profiling/browser/CdpClient.ts +82 -0
- package/src/profiling/browser/CdpPage.ts +138 -0
- package/src/profiling/browser/ChromeLauncher.ts +158 -0
- package/src/profiling/browser/ChromeTraceEvent.ts +28 -0
- package/src/profiling/browser/PageLoadMode.ts +61 -0
- package/src/profiling/node/CoverageSampler.ts +27 -0
- package/src/profiling/node/CoverageTypes.ts +23 -0
- package/src/profiling/node/HeapSampleReport.ts +261 -0
- package/src/{heap-sample → profiling/node}/HeapSampler.ts +55 -13
- package/src/profiling/node/ResolvedProfile.ts +98 -0
- package/src/profiling/node/TimeSampler.ts +57 -0
- package/src/report/BenchmarkReport.ts +146 -0
- package/src/report/Colors.ts +9 -0
- package/src/report/Formatters.ts +110 -0
- package/src/report/GcSections.ts +151 -0
- package/src/{GitUtils.ts → report/GitUtils.ts} +18 -19
- package/src/report/HtmlReport.ts +223 -0
- package/src/report/ParseStats.ts +73 -0
- package/src/report/StandardSections.ts +147 -0
- package/src/report/ViewerSections.ts +286 -0
- package/src/report/text/TableReport.ts +253 -0
- package/src/report/text/TextReport.ts +123 -0
- package/src/runners/AdaptiveWrapper.ts +167 -287
- package/src/runners/BenchRunner.ts +27 -22
- package/src/{Benchmark.ts → runners/BenchmarkSpec.ts} +5 -6
- package/src/runners/CreateRunner.ts +5 -7
- package/src/runners/GcStats.ts +58 -61
- package/src/{MeasuredResults.ts → runners/MeasuredResults.ts} +43 -37
- package/src/runners/MergeBatches.ts +123 -0
- package/src/{NodeGC.ts → runners/NodeGC.ts} +2 -3
- package/src/runners/RunnerOrchestrator.ts +180 -296
- package/src/runners/RunnerUtils.ts +75 -1
- package/src/runners/SampleStats.ts +100 -0
- package/src/runners/TimingRunner.ts +244 -0
- package/src/runners/TimingUtils.ts +3 -2
- package/src/runners/WorkerScript.ts +162 -178
- package/src/stats/BootstrapDifference.ts +282 -0
- package/src/{PermutationTest.ts → stats/PermutationTest.ts} +31 -40
- package/src/stats/StatisticalUtils.ts +445 -0
- package/src/{tests → test}/AdaptiveConvergence.test.ts +10 -10
- package/src/test/AdaptiveRunner.test.ts +39 -41
- package/src/{tests → test}/AdaptiveSampling.test.ts +9 -9
- package/src/test/AdaptiveStatistics.integration.ts +9 -41
- package/src/{tests → test}/BenchMatrix.test.ts +31 -28
- package/src/test/BenchmarkReport.test.ts +63 -13
- package/src/test/BrowserBench.e2e.test.ts +186 -17
- package/src/test/BrowserBench.test.ts +10 -5
- package/src/test/BuildTimeSection.test.ts +130 -0
- package/src/test/CapSamples.test.ts +82 -0
- package/src/test/CoverageExport.test.ts +115 -0
- package/src/test/CoverageSampler.test.ts +33 -0
- package/src/test/HeapAttribution.test.ts +51 -0
- package/src/{tests → test}/MatrixFilter.test.ts +16 -16
- package/src/{tests → test}/MatrixReport.test.ts +1 -1
- package/src/test/PermutationTest.test.ts +1 -1
- package/src/{tests → test}/RealDataValidation.test.ts +6 -6
- package/src/test/RunBenchCLI.test.ts +57 -56
- package/src/test/RunnerOrchestrator.test.ts +12 -12
- package/src/test/StatisticalUtils.test.ts +48 -12
- package/src/{table-util/test → test}/TableReport.test.ts +2 -2
- package/src/test/TestUtils.ts +35 -30
- package/src/test/TimeExport.test.ts +139 -0
- package/src/test/TimeSampler.test.ts +37 -0
- package/src/test/ViewerLive.e2e.test.ts +159 -0
- package/src/test/ViewerStatic.static.e2e.test.ts +137 -0
- package/src/{tests → test}/fixtures/baseline/impl.ts +1 -1
- package/src/{tests → test}/fixtures/bevy30-samples.ts +3 -1
- package/src/test/fixtures/cases/asyncCases.ts +9 -0
- package/src/{tests → test}/fixtures/cases/cases.ts +5 -2
- package/src/test/fixtures/cases/variants/product.ts +2 -0
- package/src/test/fixtures/cases/variants/sum.ts +2 -0
- package/src/test/fixtures/discover/fast.ts +1 -0
- package/src/{tests → test}/fixtures/discover/slow.ts +1 -1
- package/src/test/fixtures/invalid/bad.ts +1 -0
- package/src/test/fixtures/loader/fast.ts +1 -0
- package/src/{tests → test}/fixtures/loader/slow.ts +1 -1
- package/src/test/fixtures/loader/stateful.ts +2 -0
- package/src/test/fixtures/stateful/stateful.ts +2 -0
- package/src/test/fixtures/variants/extra.ts +1 -0
- package/src/test/fixtures/variants/impl.ts +1 -0
- package/src/test/fixtures/worker/fast.ts +1 -0
- package/src/{tests → test}/fixtures/worker/slow.ts +1 -1
- package/src/viewer/DateFormat.ts +30 -0
- package/src/viewer/Helpers.ts +23 -0
- package/src/viewer/LineData.ts +120 -0
- package/src/viewer/Providers.ts +191 -0
- package/src/viewer/ReportData.ts +123 -0
- package/src/viewer/State.ts +49 -0
- package/src/viewer/Theme.ts +15 -0
- package/src/viewer/components/App.tsx +73 -0
- package/src/viewer/components/DropZone.tsx +71 -0
- package/src/viewer/components/LazyPlot.ts +33 -0
- package/src/viewer/components/SamplesPanel.tsx +214 -0
- package/src/viewer/components/Shell.tsx +26 -0
- package/src/viewer/components/SourcePanel.tsx +216 -0
- package/src/viewer/components/SummaryPanel.tsx +332 -0
- package/src/viewer/components/TabBar.tsx +131 -0
- package/src/viewer/components/TabContent.tsx +46 -0
- package/src/viewer/components/ThemeToggle.tsx +50 -0
- package/src/viewer/index.html +20 -0
- package/src/viewer/main.tsx +4 -0
- package/src/viewer/plots/CIPlot.ts +313 -0
- package/src/{html/browser → viewer/plots}/HistogramKde.ts +42 -47
- package/src/viewer/plots/LegendUtils.ts +134 -0
- package/src/viewer/plots/PlotTypes.ts +85 -0
- package/src/viewer/plots/RenderPlots.ts +230 -0
- package/src/viewer/plots/SampleTimeSeries.ts +306 -0
- package/src/viewer/plots/SvgHelpers.ts +136 -0
- package/src/viewer/plots/TimeSeriesMarks.ts +319 -0
- package/src/viewer/report.css +427 -0
- package/src/viewer/shell.css +357 -0
- package/src/viewer/tsconfig.json +11 -0
- package/dist/BenchRunner-CSKN9zPy.d.mts +0 -225
- package/dist/BrowserHeapSampler-DCeL42RE.mjs +0 -202
- package/dist/BrowserHeapSampler-DCeL42RE.mjs.map +0 -1
- package/dist/GcStats-ByEovUi1.mjs +0 -77
- package/dist/GcStats-ByEovUi1.mjs.map +0 -1
- package/dist/HeapSampler-B8dtKHn1.mjs.map +0 -1
- package/dist/TimingUtils-ClclVQ7E.mjs +0 -597
- package/dist/TimingUtils-ClclVQ7E.mjs.map +0 -1
- package/dist/browser/index.js +0 -914
- package/dist/src-Cf_LXwlp.mjs +0 -2873
- package/dist/src-Cf_LXwlp.mjs.map +0 -1
- package/src/BenchMatrix.ts +0 -380
- package/src/BenchmarkReport.ts +0 -156
- package/src/HtmlDataPrep.ts +0 -148
- package/src/StandardSections.ts +0 -261
- package/src/StatisticalUtils.ts +0 -176
- package/src/TypeUtil.ts +0 -8
- package/src/browser/BrowserGcStats.ts +0 -44
- package/src/browser/BrowserHeapSampler.ts +0 -271
- package/src/export/JsonExport.ts +0 -103
- package/src/export/JsonFormat.ts +0 -91
- package/src/heap-sample/HeapSampleReport.ts +0 -196
- package/src/html/HtmlReport.ts +0 -131
- package/src/html/HtmlTemplate.ts +0 -284
- package/src/html/Types.ts +0 -88
- package/src/html/browser/CIPlot.ts +0 -287
- package/src/html/browser/LegendUtils.ts +0 -163
- package/src/html/browser/RenderPlots.ts +0 -263
- package/src/html/browser/SampleTimeSeries.ts +0 -389
- package/src/html/browser/Types.ts +0 -96
- package/src/html/browser/index.ts +0 -1
- package/src/html/index.ts +0 -17
- package/src/runners/BasicRunner.ts +0 -364
- package/src/table-util/ConvergenceFormatters.ts +0 -19
- package/src/table-util/Formatters.ts +0 -152
- package/src/table-util/README.md +0 -70
- package/src/table-util/TableReport.ts +0 -293
- package/src/tests/fixtures/cases/asyncCases.ts +0 -7
- package/src/tests/fixtures/cases/variants/product.ts +0 -2
- package/src/tests/fixtures/cases/variants/sum.ts +0 -2
- package/src/tests/fixtures/discover/fast.ts +0 -1
- package/src/tests/fixtures/invalid/bad.ts +0 -1
- package/src/tests/fixtures/loader/fast.ts +0 -1
- package/src/tests/fixtures/loader/stateful.ts +0 -2
- package/src/tests/fixtures/stateful/stateful.ts +0 -2
- package/src/tests/fixtures/variants/extra.ts +0 -1
- package/src/tests/fixtures/variants/impl.ts +0 -1
- package/src/tests/fixtures/worker/fast.ts +0 -1
- package/src/{table-util/test → test}/TableValueExtractor.test.ts +0 -0
- package/src/{table-util/test → test}/TableValueExtractor.ts +9 -9
package/src/BenchMatrix.ts
DELETED
|
@@ -1,380 +0,0 @@
|
|
|
1
|
-
import type { MeasuredResults } from "./MeasuredResults.ts";
|
|
2
|
-
import { loadCaseData, loadCasesModule } from "./matrix/CaseLoader.ts";
|
|
3
|
-
import { discoverVariants } from "./matrix/VariantLoader.ts";
|
|
4
|
-
import { BasicRunner } from "./runners/BasicRunner.ts";
|
|
5
|
-
import type { RunnerOptions } from "./runners/BenchRunner.ts";
|
|
6
|
-
import { runMatrixVariant } from "./runners/RunnerOrchestrator.ts";
|
|
7
|
-
import { average } from "./StatisticalUtils.ts";
|
|
8
|
-
|
|
9
|
-
/** Stateless variant - called each iteration with case data */
|
|
10
|
-
export type VariantFn<T = unknown> = (caseData: T) => void;
|
|
11
|
-
|
|
12
|
-
/** Stateful variant - setup once, run many */
|
|
13
|
-
export interface StatefulVariant<T = unknown, S = unknown> {
|
|
14
|
-
setup: (caseData: T) => S | Promise<S>;
|
|
15
|
-
run: (state: S) => void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/** A variant is either a plain function or a stateful setup+run pair */
|
|
19
|
-
export type Variant<T = unknown, S = unknown> =
|
|
20
|
-
| VariantFn<T>
|
|
21
|
-
| StatefulVariant<T, S>;
|
|
22
|
-
|
|
23
|
-
/** Variant with any state type - used in BenchMatrix to allow mixed variants */
|
|
24
|
-
export type AnyVariant<T = unknown> = VariantFn<T> | StatefulVariant<T, any>;
|
|
25
|
-
|
|
26
|
-
/** Result from casesModule.loadCase() */
|
|
27
|
-
export interface LoadedCase<T = unknown> {
|
|
28
|
-
data: T;
|
|
29
|
-
metadata?: Record<string, unknown>;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface MatrixDefaults {
|
|
33
|
-
warmup?: number;
|
|
34
|
-
maxTime?: number;
|
|
35
|
-
iterations?: number;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** Bench matrix configuration */
|
|
39
|
-
export interface BenchMatrix<T = unknown> {
|
|
40
|
-
name: string;
|
|
41
|
-
variantDir?: string;
|
|
42
|
-
variants?: Record<string, AnyVariant<T>>;
|
|
43
|
-
cases?: string[];
|
|
44
|
-
casesModule?: string;
|
|
45
|
-
baselineDir?: string;
|
|
46
|
-
baselineVariant?: string;
|
|
47
|
-
defaults?: MatrixDefaults;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** Collection of matrices */
|
|
51
|
-
export interface MatrixSuite {
|
|
52
|
-
name: string;
|
|
53
|
-
matrices: BenchMatrix<any>[];
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Results for a single variant across all cases */
|
|
57
|
-
export interface VariantResult {
|
|
58
|
-
id: string;
|
|
59
|
-
cases: CaseResult[];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Results for a single (variant, case) pair */
|
|
63
|
-
export interface CaseResult {
|
|
64
|
-
caseId: string;
|
|
65
|
-
measured: MeasuredResults;
|
|
66
|
-
metadata?: Record<string, unknown>;
|
|
67
|
-
baseline?: MeasuredResults;
|
|
68
|
-
deltaPercent?: number;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** Results from running a matrix */
|
|
72
|
-
export interface MatrixResults {
|
|
73
|
-
name: string;
|
|
74
|
-
variants: VariantResult[];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** @return true if variant is a StatefulVariant (has setup + run) */
|
|
78
|
-
export function isStatefulVariant<T, S>(
|
|
79
|
-
v: Variant<T, S>,
|
|
80
|
-
): v is StatefulVariant<T, S> {
|
|
81
|
-
return typeof v === "object" && "setup" in v && "run" in v;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** Options for runMatrix */
|
|
85
|
-
export interface RunMatrixOptions {
|
|
86
|
-
iterations?: number;
|
|
87
|
-
maxTime?: number;
|
|
88
|
-
warmup?: number;
|
|
89
|
-
useWorker?: boolean; // use worker process isolation (default: true for variantDir)
|
|
90
|
-
filteredCases?: string[]; // run only these cases (from filter)
|
|
91
|
-
filteredVariants?: string[]; // run only these variants (from filter)
|
|
92
|
-
// Runner options passthrough
|
|
93
|
-
collect?: boolean;
|
|
94
|
-
cpuCounters?: boolean;
|
|
95
|
-
traceOpt?: boolean;
|
|
96
|
-
noSettle?: boolean;
|
|
97
|
-
pauseFirst?: number;
|
|
98
|
-
pauseInterval?: number;
|
|
99
|
-
pauseDuration?: number;
|
|
100
|
-
gcStats?: boolean;
|
|
101
|
-
heapSample?: boolean;
|
|
102
|
-
heapInterval?: number;
|
|
103
|
-
heapDepth?: number;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** Run a BenchMatrix with inline variants or variantDir */
|
|
107
|
-
export async function runMatrix<T>(
|
|
108
|
-
matrix: BenchMatrix<T>,
|
|
109
|
-
options: RunMatrixOptions = {},
|
|
110
|
-
): Promise<MatrixResults> {
|
|
111
|
-
validateBaseline(matrix);
|
|
112
|
-
const effectiveOptions = { ...matrix.defaults, ...options };
|
|
113
|
-
|
|
114
|
-
if (matrix.variantDir) {
|
|
115
|
-
return runMatrixWithDir(matrix, effectiveOptions);
|
|
116
|
-
}
|
|
117
|
-
if (matrix.variants) {
|
|
118
|
-
return runMatrixInline(matrix, effectiveOptions);
|
|
119
|
-
}
|
|
120
|
-
throw new Error("BenchMatrix requires either 'variants' or 'variantDir'");
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/** @throws if both baselineDir and baselineVariant are set */
|
|
124
|
-
function validateBaseline<T>(matrix: BenchMatrix<T>): void {
|
|
125
|
-
const msg =
|
|
126
|
-
"BenchMatrix cannot have both 'baselineDir' and 'baselineVariant'";
|
|
127
|
-
if (matrix.baselineDir && matrix.baselineVariant) throw new Error(msg);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function buildRunnerOptions(options: RunMatrixOptions): RunnerOptions {
|
|
131
|
-
return {
|
|
132
|
-
maxIterations: options.iterations,
|
|
133
|
-
maxTime: options.maxTime ?? 1000,
|
|
134
|
-
warmup: options.warmup ?? 0,
|
|
135
|
-
collect: options.collect,
|
|
136
|
-
cpuCounters: options.cpuCounters,
|
|
137
|
-
traceOpt: options.traceOpt,
|
|
138
|
-
noSettle: options.noSettle,
|
|
139
|
-
pauseFirst: options.pauseFirst,
|
|
140
|
-
pauseInterval: options.pauseInterval,
|
|
141
|
-
pauseDuration: options.pauseDuration,
|
|
142
|
-
gcStats: options.gcStats,
|
|
143
|
-
heapSample: options.heapSample,
|
|
144
|
-
heapInterval: options.heapInterval,
|
|
145
|
-
heapDepth: options.heapDepth,
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/** Load cases module and resolve filtered case IDs */
|
|
150
|
-
async function resolveCases<T>(
|
|
151
|
-
matrix: BenchMatrix<T>,
|
|
152
|
-
options: RunMatrixOptions,
|
|
153
|
-
) {
|
|
154
|
-
const casesModule = matrix.casesModule
|
|
155
|
-
? await loadCasesModule<T>(matrix.casesModule)
|
|
156
|
-
: undefined;
|
|
157
|
-
const allCaseIds = casesModule?.cases ?? matrix.cases ?? ["default"];
|
|
158
|
-
const caseIds = options.filteredCases ?? allCaseIds;
|
|
159
|
-
return { casesModule, caseIds };
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/** Run matrix with inline variants (non-worker mode) */
|
|
163
|
-
async function runMatrixInline<T>(
|
|
164
|
-
matrix: BenchMatrix<T>,
|
|
165
|
-
options: RunMatrixOptions,
|
|
166
|
-
): Promise<MatrixResults> {
|
|
167
|
-
// baselineDir is only valid with variantDir
|
|
168
|
-
const msg =
|
|
169
|
-
"BenchMatrix with inline 'variants' cannot use 'baselineDir'. Use 'variantDir' instead.";
|
|
170
|
-
if (matrix.baselineDir) throw new Error(msg);
|
|
171
|
-
|
|
172
|
-
const { casesModule, caseIds } = await resolveCases(matrix, options);
|
|
173
|
-
const runner = new BasicRunner();
|
|
174
|
-
const runnerOpts = buildRunnerOptions(options);
|
|
175
|
-
|
|
176
|
-
const variantEntries = options.filteredVariants
|
|
177
|
-
? Object.entries(matrix.variants!).filter(([id]) =>
|
|
178
|
-
options.filteredVariants!.includes(id),
|
|
179
|
-
)
|
|
180
|
-
: Object.entries(matrix.variants!);
|
|
181
|
-
|
|
182
|
-
const variants: VariantResult[] = [];
|
|
183
|
-
for (const [variantId, variant] of variantEntries) {
|
|
184
|
-
const cases: CaseResult[] = [];
|
|
185
|
-
for (const caseId of caseIds) {
|
|
186
|
-
const loaded = await loadCaseData(casesModule, caseId);
|
|
187
|
-
const caseData =
|
|
188
|
-
casesModule || matrix.cases ? loaded.data : (undefined as T);
|
|
189
|
-
const measured = await runVariant(
|
|
190
|
-
variant,
|
|
191
|
-
caseData,
|
|
192
|
-
variantId,
|
|
193
|
-
runner,
|
|
194
|
-
runnerOpts,
|
|
195
|
-
);
|
|
196
|
-
cases.push({ caseId, measured, metadata: loaded.metadata });
|
|
197
|
-
}
|
|
198
|
-
variants.push({ id: variantId, cases });
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (matrix.baselineVariant) {
|
|
202
|
-
applyBaselineVariant(variants, matrix.baselineVariant);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return { name: matrix.name, variants };
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/** Context for running matrix benchmarks in worker mode */
|
|
209
|
-
interface DirMatrixContext<T> {
|
|
210
|
-
matrix: BenchMatrix<T>;
|
|
211
|
-
casesModule?: import("./matrix/CaseLoader.ts").CasesModule<T>;
|
|
212
|
-
baselineIds: string[];
|
|
213
|
-
caseIds: string[];
|
|
214
|
-
runnerOpts: RunnerOptions;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/** Run matrix with variantDir (worker mode for memory isolation) */
|
|
218
|
-
async function runMatrixWithDir<T>(
|
|
219
|
-
matrix: BenchMatrix<T>,
|
|
220
|
-
options: RunMatrixOptions,
|
|
221
|
-
): Promise<MatrixResults> {
|
|
222
|
-
const allVariantIds = await discoverVariants(matrix.variantDir!);
|
|
223
|
-
if (allVariantIds.length === 0) {
|
|
224
|
-
throw new Error(`No variants found in ${matrix.variantDir}`);
|
|
225
|
-
}
|
|
226
|
-
const variantIds = options.filteredVariants ?? allVariantIds;
|
|
227
|
-
|
|
228
|
-
const ctx = await createDirContext(matrix, options);
|
|
229
|
-
const variants = await runDirVariants(variantIds, ctx);
|
|
230
|
-
|
|
231
|
-
if (matrix.baselineVariant) {
|
|
232
|
-
applyBaselineVariant(variants, matrix.baselineVariant);
|
|
233
|
-
}
|
|
234
|
-
return { name: matrix.name, variants };
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/** Create context for directory-based matrix execution */
|
|
238
|
-
async function createDirContext<T>(
|
|
239
|
-
matrix: BenchMatrix<T>,
|
|
240
|
-
options: RunMatrixOptions,
|
|
241
|
-
): Promise<DirMatrixContext<T>> {
|
|
242
|
-
const baselineIds = matrix.baselineDir
|
|
243
|
-
? await discoverVariants(matrix.baselineDir)
|
|
244
|
-
: [];
|
|
245
|
-
const { casesModule, caseIds } = await resolveCases(matrix, options);
|
|
246
|
-
const runnerOpts = buildRunnerOptions(options);
|
|
247
|
-
return { matrix, casesModule, baselineIds, caseIds, runnerOpts };
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/** Run all variants using worker processes */
|
|
251
|
-
async function runDirVariants<T>(
|
|
252
|
-
variantIds: string[],
|
|
253
|
-
ctx: DirMatrixContext<T>,
|
|
254
|
-
): Promise<VariantResult[]> {
|
|
255
|
-
const variants: VariantResult[] = [];
|
|
256
|
-
for (const variantId of variantIds) {
|
|
257
|
-
const cases = await runDirVariantCases(variantId, ctx);
|
|
258
|
-
variants.push({ id: variantId, cases });
|
|
259
|
-
}
|
|
260
|
-
return variants;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/** Run all cases for a single variant */
|
|
264
|
-
async function runDirVariantCases<T>(
|
|
265
|
-
variantId: string,
|
|
266
|
-
ctx: DirMatrixContext<T>,
|
|
267
|
-
): Promise<CaseResult[]> {
|
|
268
|
-
const { matrix, casesModule, caseIds, runnerOpts } = ctx;
|
|
269
|
-
const cases: CaseResult[] = [];
|
|
270
|
-
|
|
271
|
-
for (const caseId of caseIds) {
|
|
272
|
-
const caseData = !matrix.casesModule && matrix.cases ? caseId : undefined;
|
|
273
|
-
const [measured] = await runMatrixVariant({
|
|
274
|
-
variantDir: matrix.variantDir!,
|
|
275
|
-
variantId,
|
|
276
|
-
caseId,
|
|
277
|
-
caseData,
|
|
278
|
-
casesModule: matrix.casesModule,
|
|
279
|
-
runner: "basic",
|
|
280
|
-
options: runnerOpts,
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
const loaded = await loadCaseData(casesModule, caseId);
|
|
284
|
-
const baseline = await runBaselineIfExists(
|
|
285
|
-
variantId,
|
|
286
|
-
caseId,
|
|
287
|
-
caseData,
|
|
288
|
-
ctx,
|
|
289
|
-
);
|
|
290
|
-
const deltaPercent = baseline
|
|
291
|
-
? computeDeltaPercent(baseline, measured)
|
|
292
|
-
: undefined;
|
|
293
|
-
const metadata = loaded.metadata;
|
|
294
|
-
cases.push({ caseId, measured, metadata, baseline, deltaPercent });
|
|
295
|
-
}
|
|
296
|
-
return cases;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/** Run baseline variant if it exists in baselineDir */
|
|
300
|
-
async function runBaselineIfExists<T>(
|
|
301
|
-
variantId: string,
|
|
302
|
-
caseId: string,
|
|
303
|
-
caseData: unknown,
|
|
304
|
-
ctx: DirMatrixContext<T>,
|
|
305
|
-
): Promise<MeasuredResults | undefined> {
|
|
306
|
-
const { matrix, baselineIds, runnerOpts } = ctx;
|
|
307
|
-
if (!matrix.baselineDir || !baselineIds.includes(variantId)) return undefined;
|
|
308
|
-
|
|
309
|
-
const [measured] = await runMatrixVariant({
|
|
310
|
-
variantDir: matrix.baselineDir,
|
|
311
|
-
variantId,
|
|
312
|
-
caseId,
|
|
313
|
-
caseData,
|
|
314
|
-
casesModule: matrix.casesModule,
|
|
315
|
-
runner: "basic",
|
|
316
|
-
options: runnerOpts,
|
|
317
|
-
});
|
|
318
|
-
return measured;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/** Compute delta percentage: (current - baseline) / baseline * 100 */
|
|
322
|
-
function computeDeltaPercent(
|
|
323
|
-
baseline: MeasuredResults,
|
|
324
|
-
current: MeasuredResults,
|
|
325
|
-
): number {
|
|
326
|
-
const baseAvg = average(baseline.samples);
|
|
327
|
-
if (baseAvg === 0) return 0;
|
|
328
|
-
return ((average(current.samples) - baseAvg) / baseAvg) * 100;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/** Apply baselineVariant comparison - one variant is the reference for all others */
|
|
332
|
-
function applyBaselineVariant(
|
|
333
|
-
variants: VariantResult[],
|
|
334
|
-
baselineVariantId: string,
|
|
335
|
-
): void {
|
|
336
|
-
const baselineVariant = variants.find(v => v.id === baselineVariantId);
|
|
337
|
-
if (!baselineVariant) return;
|
|
338
|
-
|
|
339
|
-
const baselineByCase = new Map<string, MeasuredResults>();
|
|
340
|
-
for (const c of baselineVariant.cases) {
|
|
341
|
-
baselineByCase.set(c.caseId, c.measured);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
for (const variant of variants) {
|
|
345
|
-
if (variant.id === baselineVariantId) continue;
|
|
346
|
-
for (const caseResult of variant.cases) {
|
|
347
|
-
const baseline = baselineByCase.get(caseResult.caseId);
|
|
348
|
-
if (baseline) {
|
|
349
|
-
caseResult.baseline = baseline;
|
|
350
|
-
caseResult.deltaPercent = computeDeltaPercent(
|
|
351
|
-
baseline,
|
|
352
|
-
caseResult.measured,
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/** Run a single variant with case data */
|
|
360
|
-
async function runVariant<T>(
|
|
361
|
-
variant: AnyVariant<T>,
|
|
362
|
-
caseData: T,
|
|
363
|
-
name: string,
|
|
364
|
-
runner: BasicRunner,
|
|
365
|
-
options: RunnerOptions,
|
|
366
|
-
): Promise<MeasuredResults> {
|
|
367
|
-
if (isStatefulVariant(variant)) {
|
|
368
|
-
const state = await variant.setup(caseData);
|
|
369
|
-
const [result] = await runner.runBench(
|
|
370
|
-
{ name, fn: () => variant.run(state) },
|
|
371
|
-
options,
|
|
372
|
-
);
|
|
373
|
-
return result;
|
|
374
|
-
}
|
|
375
|
-
const [result] = await runner.runBench(
|
|
376
|
-
{ name, fn: () => variant(caseData) },
|
|
377
|
-
options,
|
|
378
|
-
);
|
|
379
|
-
return result;
|
|
380
|
-
}
|
package/src/BenchmarkReport.ts
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import type { MeasuredResults } from "./MeasuredResults.ts";
|
|
2
|
-
import { bootstrapDifferenceCI } from "./StatisticalUtils.ts";
|
|
3
|
-
import type { UnionToIntersection } from "./TypeUtil.ts";
|
|
4
|
-
import {
|
|
5
|
-
formatDiffWithCI,
|
|
6
|
-
formatDiffWithCIHigherIsBetter,
|
|
7
|
-
truncate,
|
|
8
|
-
} from "./table-util/Formatters.ts";
|
|
9
|
-
import {
|
|
10
|
-
type AnyColumn,
|
|
11
|
-
buildTable,
|
|
12
|
-
type ColumnGroup,
|
|
13
|
-
type ResultGroup,
|
|
14
|
-
} from "./table-util/TableReport.ts";
|
|
15
|
-
|
|
16
|
-
/** Benchmark results with optional baseline for comparison */
|
|
17
|
-
export interface ReportGroup {
|
|
18
|
-
name: string;
|
|
19
|
-
reports: BenchmarkReport[];
|
|
20
|
-
baseline?: BenchmarkReport;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** Results from a single benchmark run */
|
|
24
|
-
export interface BenchmarkReport {
|
|
25
|
-
name: string;
|
|
26
|
-
measuredResults: MeasuredResults;
|
|
27
|
-
metadata?: UnknownRecord;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface ReportColumnGroup<T> {
|
|
31
|
-
groupTitle?: string;
|
|
32
|
-
columns: ReportColumn<T>[];
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export type ReportColumn<T> = AnyColumn<T> & {
|
|
36
|
-
/** Add diff column after this column when baseline exists */
|
|
37
|
-
comparable?: boolean;
|
|
38
|
-
/** Set true for throughput metrics where higher values are better (e.g., lines/sec) */
|
|
39
|
-
higherIsBetter?: boolean;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/** Maps benchmark results to table columns */
|
|
43
|
-
export interface ResultsMapper<
|
|
44
|
-
T extends Record<string, any> = Record<string, any>,
|
|
45
|
-
> {
|
|
46
|
-
extract(results: MeasuredResults, metadata?: UnknownRecord): T;
|
|
47
|
-
columns(): ReportColumnGroup<T>[];
|
|
48
|
-
}
|
|
49
|
-
export type UnknownRecord = Record<string, unknown>;
|
|
50
|
-
|
|
51
|
-
type SectionStats<S> = S extends ResultsMapper<infer T> ? T : never;
|
|
52
|
-
|
|
53
|
-
interface ReportRowBase {
|
|
54
|
-
name: string;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** Row data combining all section statistics */
|
|
58
|
-
type ReportRowData<S extends ReadonlyArray<ResultsMapper<any>>> =
|
|
59
|
-
ReportRowBase & UnionToIntersection<SectionStats<S[number]>>;
|
|
60
|
-
|
|
61
|
-
/** @return formatted table report with optional baseline comparisons */
|
|
62
|
-
export function reportResults<S extends ReadonlyArray<ResultsMapper<any>>>(
|
|
63
|
-
groups: ReportGroup[],
|
|
64
|
-
sections: S,
|
|
65
|
-
): string {
|
|
66
|
-
const results = groups.map(group => resultGroupValues(group, sections));
|
|
67
|
-
const hasBaseline = results.some(g => g.baseline);
|
|
68
|
-
return buildTable(createColumnGroups(sections, hasBaseline), results);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** @return values for report group */
|
|
72
|
-
function resultGroupValues<S extends ReadonlyArray<ResultsMapper<any>>>(
|
|
73
|
-
group: ReportGroup,
|
|
74
|
-
sections: S,
|
|
75
|
-
): ResultGroup<ReportRowData<S>> {
|
|
76
|
-
const { reports, baseline } = group;
|
|
77
|
-
const baselineSamples = baseline?.measuredResults.samples;
|
|
78
|
-
|
|
79
|
-
const results = reports.map(report => {
|
|
80
|
-
const row = {
|
|
81
|
-
name: truncate(report.name),
|
|
82
|
-
...extractReportValues(report, sections),
|
|
83
|
-
} as ReportRowData<S>;
|
|
84
|
-
|
|
85
|
-
if (baselineSamples && report.measuredResults.samples) {
|
|
86
|
-
(row as any).diffCI = bootstrapDifferenceCI(
|
|
87
|
-
baselineSamples,
|
|
88
|
-
report.measuredResults.samples,
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
return row;
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
const baselineRow = baseline && valuesForReports([baseline], sections)[0];
|
|
95
|
-
return { results, baseline: baselineRow };
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/** @return rows with stats from sections */
|
|
99
|
-
export function valuesForReports<S extends ReadonlyArray<ResultsMapper<any>>>(
|
|
100
|
-
reports: BenchmarkReport[],
|
|
101
|
-
sections: S,
|
|
102
|
-
): ReportRowData<S>[] {
|
|
103
|
-
return reports.map(report => ({
|
|
104
|
-
name: truncate(report.name),
|
|
105
|
-
...extractReportValues(report, sections),
|
|
106
|
-
})) as ReportRowData<S>[];
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/** @return merged statistics from all sections */
|
|
110
|
-
function extractReportValues(
|
|
111
|
-
report: BenchmarkReport,
|
|
112
|
-
sections: ReadonlyArray<ResultsMapper<any>>,
|
|
113
|
-
): UnknownRecord {
|
|
114
|
-
const { measuredResults, metadata } = report;
|
|
115
|
-
const entries = sections.flatMap(s =>
|
|
116
|
-
Object.entries(s.extract(measuredResults, metadata)),
|
|
117
|
-
);
|
|
118
|
-
return Object.fromEntries(entries);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/** @return column groups with diff columns if baseline exists */
|
|
122
|
-
function createColumnGroups<S extends ReadonlyArray<ResultsMapper<any>>>(
|
|
123
|
-
sections: S,
|
|
124
|
-
hasBaseline: boolean,
|
|
125
|
-
): ColumnGroup<ReportRowData<S>>[] {
|
|
126
|
-
const nameColumn: ColumnGroup<ReportRowData<S>> = {
|
|
127
|
-
columns: [{ key: "name" as keyof ReportRowData<S>, title: "name" }],
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const groups = sections.flatMap(section => section.columns());
|
|
131
|
-
return [nameColumn, ...(hasBaseline ? injectDiffColumns(groups) : groups)];
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/** @return groups with single CI column after first comparable field */
|
|
135
|
-
export function injectDiffColumns<T>(
|
|
136
|
-
reportGroups: ReportColumnGroup<T>[],
|
|
137
|
-
): ColumnGroup<T>[] {
|
|
138
|
-
let ciAdded = false;
|
|
139
|
-
|
|
140
|
-
return reportGroups.map(group => ({
|
|
141
|
-
groupTitle: group.groupTitle,
|
|
142
|
-
columns: group.columns.flatMap(col => {
|
|
143
|
-
if (col.comparable && !ciAdded) {
|
|
144
|
-
ciAdded = true;
|
|
145
|
-
const fmt = col.higherIsBetter
|
|
146
|
-
? formatDiffWithCIHigherIsBetter
|
|
147
|
-
: formatDiffWithCI;
|
|
148
|
-
return [
|
|
149
|
-
col,
|
|
150
|
-
{ title: "Δ% CI", key: "diffCI" as keyof T, formatter: fmt },
|
|
151
|
-
];
|
|
152
|
-
}
|
|
153
|
-
return [col];
|
|
154
|
-
}),
|
|
155
|
-
}));
|
|
156
|
-
}
|
package/src/HtmlDataPrep.ts
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ReportColumnGroup,
|
|
3
|
-
ReportGroup,
|
|
4
|
-
ResultsMapper,
|
|
5
|
-
} from "./BenchmarkReport.ts";
|
|
6
|
-
import type { GitVersion } from "./GitUtils.ts";
|
|
7
|
-
import type {
|
|
8
|
-
BenchmarkData,
|
|
9
|
-
DifferenceCI,
|
|
10
|
-
FormattedStat,
|
|
11
|
-
GroupData,
|
|
12
|
-
ReportData,
|
|
13
|
-
} from "./html/index.ts";
|
|
14
|
-
import { bootstrapDifferenceCI } from "./StatisticalUtils.ts";
|
|
15
|
-
|
|
16
|
-
export interface PrepareHtmlOptions {
|
|
17
|
-
cliArgs?: Record<string, unknown>;
|
|
18
|
-
sections?: ResultsMapper[];
|
|
19
|
-
currentVersion?: GitVersion;
|
|
20
|
-
baselineVersion?: GitVersion;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** Find higherIsBetter from first comparable column in sections */
|
|
24
|
-
function findHigherIsBetter(sections?: ResultsMapper[]): boolean {
|
|
25
|
-
const cols = sections?.flatMap(s => s.columns().flatMap(g => g.columns));
|
|
26
|
-
return cols?.find(c => c.comparable)?.higherIsBetter ?? false;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Flip CI percent for metrics where higher is better (e.g., lines/sec) */
|
|
30
|
-
function flipCI(ci: DifferenceCI): DifferenceCI {
|
|
31
|
-
return {
|
|
32
|
-
percent: -ci.percent,
|
|
33
|
-
ci: [-ci.ci[1], -ci.ci[0]],
|
|
34
|
-
direction: ci.direction,
|
|
35
|
-
histogram: ci.histogram?.map(bin => ({ x: -bin.x, count: bin.count })),
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** Prepare ReportData from benchmark results for HTML rendering */
|
|
40
|
-
export function prepareHtmlData(
|
|
41
|
-
groups: ReportGroup[],
|
|
42
|
-
options: PrepareHtmlOptions,
|
|
43
|
-
): ReportData {
|
|
44
|
-
const { cliArgs, sections, currentVersion, baselineVersion } = options;
|
|
45
|
-
const higherIsBetter = findHigherIsBetter(sections);
|
|
46
|
-
return {
|
|
47
|
-
groups: groups.map(group =>
|
|
48
|
-
prepareGroupData(group, sections, higherIsBetter),
|
|
49
|
-
),
|
|
50
|
-
metadata: {
|
|
51
|
-
timestamp: new Date().toISOString(),
|
|
52
|
-
bencherVersion: process.env.npm_package_version || "unknown",
|
|
53
|
-
cliArgs,
|
|
54
|
-
gcTrackingEnabled: cliArgs?.["gc-stats"] === true,
|
|
55
|
-
currentVersion,
|
|
56
|
-
baselineVersion,
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** @return group data with bootstrap CI comparisons against baseline */
|
|
62
|
-
function prepareGroupData(
|
|
63
|
-
group: ReportGroup,
|
|
64
|
-
sections?: ResultsMapper[],
|
|
65
|
-
higherIsBetter?: boolean,
|
|
66
|
-
): GroupData {
|
|
67
|
-
const baselineSamples = group.baseline?.measuredResults.samples;
|
|
68
|
-
return {
|
|
69
|
-
name: group.name,
|
|
70
|
-
baseline: group.baseline
|
|
71
|
-
? prepareBenchmarkData(group.baseline, sections)
|
|
72
|
-
: undefined,
|
|
73
|
-
benchmarks: group.reports.map(report => {
|
|
74
|
-
const samples = report.measuredResults.samples;
|
|
75
|
-
const rawCI =
|
|
76
|
-
baselineSamples && samples
|
|
77
|
-
? bootstrapDifferenceCI(baselineSamples, samples)
|
|
78
|
-
: undefined;
|
|
79
|
-
const comparisonCI = rawCI && higherIsBetter ? flipCI(rawCI) : rawCI;
|
|
80
|
-
return { ...prepareBenchmarkData(report, sections), comparisonCI };
|
|
81
|
-
}),
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** @return benchmark data with samples, stats, and formatted section values */
|
|
86
|
-
function prepareBenchmarkData(
|
|
87
|
-
report: {
|
|
88
|
-
name: string;
|
|
89
|
-
measuredResults: any;
|
|
90
|
-
metadata?: Record<string, unknown>;
|
|
91
|
-
},
|
|
92
|
-
sections?: ResultsMapper[],
|
|
93
|
-
): Omit<BenchmarkData, "comparisonCI"> {
|
|
94
|
-
const { measuredResults } = report;
|
|
95
|
-
return {
|
|
96
|
-
name: report.name,
|
|
97
|
-
samples: measuredResults.samples,
|
|
98
|
-
warmupSamples: measuredResults.warmupSamples,
|
|
99
|
-
allocationSamples: measuredResults.allocationSamples,
|
|
100
|
-
heapSamples: measuredResults.heapSamples,
|
|
101
|
-
gcEvents: measuredResults.nodeGcTime?.events,
|
|
102
|
-
optSamples: measuredResults.optSamples,
|
|
103
|
-
pausePoints: measuredResults.pausePoints,
|
|
104
|
-
stats: measuredResults.time,
|
|
105
|
-
heapSize: measuredResults.heapSize,
|
|
106
|
-
sectionStats: sections ? extractSectionStats(report, sections) : undefined,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/** @return formatted stats from all sections for tooltip display */
|
|
111
|
-
function extractSectionStats(
|
|
112
|
-
report: { measuredResults: any; metadata?: Record<string, unknown> },
|
|
113
|
-
sections: ResultsMapper[],
|
|
114
|
-
): FormattedStat[] {
|
|
115
|
-
return sections.flatMap(section => {
|
|
116
|
-
const vals = section.extract(report.measuredResults, report.metadata);
|
|
117
|
-
return section.columns().flatMap(g => formatGroupStats(vals, g));
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/** @return formatted stats for one column group, skipping undefined values */
|
|
122
|
-
function formatGroupStats(
|
|
123
|
-
values: Record<string, unknown>,
|
|
124
|
-
group: ReportColumnGroup<Record<string, unknown>>,
|
|
125
|
-
): FormattedStat[] {
|
|
126
|
-
return group.columns
|
|
127
|
-
.map(c => formatColumnStat(values, c, group.groupTitle))
|
|
128
|
-
.filter((s): s is FormattedStat => s !== undefined);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
type ColumnLike = {
|
|
132
|
-
key: string;
|
|
133
|
-
title: string;
|
|
134
|
-
formatter?: (v: unknown) => string | null;
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
/** @return formatted stat for a single column, or undefined if empty/placeholder */
|
|
138
|
-
function formatColumnStat(
|
|
139
|
-
values: Record<string, unknown>,
|
|
140
|
-
col: ColumnLike,
|
|
141
|
-
groupTitle?: string,
|
|
142
|
-
): FormattedStat | undefined {
|
|
143
|
-
const raw = values[col.key];
|
|
144
|
-
if (raw === undefined) return undefined;
|
|
145
|
-
const formatted = col.formatter ? col.formatter(raw) : String(raw);
|
|
146
|
-
if (!formatted || formatted === "—" || formatted === "") return undefined;
|
|
147
|
-
return { label: col.title, value: formatted, groupTitle };
|
|
148
|
-
}
|