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
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/** CPU time profile conversion to Speedscope sampled format. */
|
|
2
|
+
|
|
3
|
+
import { resolveCallFrame } from "../profiling/node/ResolvedProfile.ts";
|
|
4
|
+
import type {
|
|
5
|
+
TimeProfile,
|
|
6
|
+
TimeProfileNode,
|
|
7
|
+
} from "../profiling/node/TimeSampler.ts";
|
|
8
|
+
import {
|
|
9
|
+
type FrameContext,
|
|
10
|
+
frameContext,
|
|
11
|
+
internFrame,
|
|
12
|
+
type SpeedscopeFile,
|
|
13
|
+
type SpeedscopeTimeProfile,
|
|
14
|
+
speedscopeFile,
|
|
15
|
+
} from "./SpeedscopeTypes.ts";
|
|
16
|
+
|
|
17
|
+
/** Convert a TimeProfile to speedscope format */
|
|
18
|
+
export function timeProfileToSpeedscope(
|
|
19
|
+
name: string,
|
|
20
|
+
profile: TimeProfile,
|
|
21
|
+
): SpeedscopeFile {
|
|
22
|
+
const ctx = frameContext();
|
|
23
|
+
const p = buildTimeProfile(name, profile, ctx);
|
|
24
|
+
return speedscopeFile(ctx, [p]);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Build a SpeedscopeFile from multiple named time profiles (shared frames). */
|
|
28
|
+
export function buildTimeSpeedscopeFile(
|
|
29
|
+
entries: { name: string; profile: TimeProfile }[],
|
|
30
|
+
): SpeedscopeFile | undefined {
|
|
31
|
+
if (entries.length === 0) return undefined;
|
|
32
|
+
|
|
33
|
+
const ctx = frameContext();
|
|
34
|
+
const profiles = entries.map(e => buildTimeProfile(e.name, e.profile, ctx));
|
|
35
|
+
return speedscopeFile(ctx, profiles);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Build a speedscope profile from a V8 TimeProfile */
|
|
39
|
+
function buildTimeProfile(
|
|
40
|
+
name: string,
|
|
41
|
+
profile: TimeProfile,
|
|
42
|
+
ctx: FrameContext,
|
|
43
|
+
): SpeedscopeTimeProfile {
|
|
44
|
+
const { samples: sampleIds, timeDeltas, nodes } = profile;
|
|
45
|
+
|
|
46
|
+
if (!sampleIds?.length || !timeDeltas) {
|
|
47
|
+
return {
|
|
48
|
+
type: "sampled",
|
|
49
|
+
name,
|
|
50
|
+
unit: "microseconds",
|
|
51
|
+
startValue: 0,
|
|
52
|
+
endValue: 0,
|
|
53
|
+
samples: [],
|
|
54
|
+
weights: [],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const nodeMap = new Map<number, TimeProfileNode>(nodes.map(n => [n.id, n]));
|
|
59
|
+
const parentMap = new Map<number, number>(); // childId -> parentId
|
|
60
|
+
for (const node of nodes) {
|
|
61
|
+
for (const childId of node.children ?? []) {
|
|
62
|
+
parentMap.set(childId, node.id);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const cache = new Map<number, number[]>();
|
|
67
|
+
const resolve = (id: number) =>
|
|
68
|
+
resolveStack(id, nodeMap, parentMap, cache, ctx);
|
|
69
|
+
|
|
70
|
+
const samples = sampleIds.map(resolve);
|
|
71
|
+
const total = timeDeltas.reduce((sum, w) => sum + w, 0);
|
|
72
|
+
return {
|
|
73
|
+
type: "sampled",
|
|
74
|
+
name,
|
|
75
|
+
unit: "microseconds",
|
|
76
|
+
startValue: 0,
|
|
77
|
+
endValue: total,
|
|
78
|
+
samples,
|
|
79
|
+
weights: timeDeltas,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Walk from node to root, building a stack of frame indices (root-first) */
|
|
84
|
+
function resolveStack(
|
|
85
|
+
nodeId: number,
|
|
86
|
+
nodeMap: Map<number, TimeProfileNode>,
|
|
87
|
+
parentMap: Map<number, number>,
|
|
88
|
+
cache: Map<number, number[]>,
|
|
89
|
+
ctx: FrameContext,
|
|
90
|
+
): number[] {
|
|
91
|
+
const cached = cache.get(nodeId);
|
|
92
|
+
if (cached) return cached;
|
|
93
|
+
|
|
94
|
+
const path: number[] = [];
|
|
95
|
+
let current: number | undefined = nodeId;
|
|
96
|
+
while (current !== undefined) {
|
|
97
|
+
path.push(current);
|
|
98
|
+
current = parentMap.get(current);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Reverse to root-first order
|
|
102
|
+
const stack: number[] = [];
|
|
103
|
+
for (let i = path.length - 1; i >= 0; i--) {
|
|
104
|
+
const node = nodeMap.get(path[i]);
|
|
105
|
+
if (!node) continue;
|
|
106
|
+
const { functionName, url, lineNumber } = node.callFrame;
|
|
107
|
+
// Skip the synthetic (root) node
|
|
108
|
+
if (!functionName && !url && lineNumber <= 0) continue;
|
|
109
|
+
const frame = resolveCallFrame(node.callFrame);
|
|
110
|
+
stack.push(internFrame(frame.name, frame.url, frame.line, frame.col, ctx));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
cache.set(nodeId, stack);
|
|
114
|
+
return stack;
|
|
115
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
export type { Configure, DefaultCliArgs } from "./cli/CliArgs.ts";
|
|
2
|
+
export { browserCliArgs, defaultCliArgs, parseCliArgs } from "./cli/CliArgs.ts";
|
|
3
|
+
export type { ExportOptions, MatrixExportOptions } from "./cli/CliExport.ts";
|
|
4
|
+
export { exportReports } from "./cli/CliExport.ts";
|
|
5
|
+
export { cliToMatrixOptions } from "./cli/CliOptions.ts";
|
|
6
|
+
export {
|
|
7
|
+
defaultMatrixReport,
|
|
8
|
+
defaultReport,
|
|
9
|
+
matrixToReportGroups,
|
|
10
|
+
printHeapReports,
|
|
11
|
+
reportOptStatus,
|
|
12
|
+
} from "./cli/CliReport.ts";
|
|
13
|
+
export {
|
|
14
|
+
benchExports,
|
|
15
|
+
dispatchCli,
|
|
16
|
+
matrixBenchExports,
|
|
17
|
+
parseBenchArgs,
|
|
18
|
+
runDefaultBench,
|
|
19
|
+
runDefaultMatrixBench,
|
|
20
|
+
runMatrixSuite,
|
|
21
|
+
} from "./cli/RunBenchCLI.ts";
|
|
22
|
+
export { runBenchmarks } from "./cli/SuiteRunner.ts";
|
|
23
|
+
export {
|
|
24
|
+
buildSpeedscopeFile,
|
|
25
|
+
exportSpeedscope,
|
|
26
|
+
heapProfileToSpeedscope,
|
|
27
|
+
} from "./export/AllocExport.ts";
|
|
28
|
+
export { archiveBenchmark } from "./export/ArchiveExport.ts";
|
|
29
|
+
export type {
|
|
30
|
+
ArchiveMetadata,
|
|
31
|
+
BenchforgeArchive,
|
|
32
|
+
} from "./export/ArchiveFormat.ts";
|
|
33
|
+
export { exportPerfettoTrace } from "./export/PerfettoExport.ts";
|
|
1
34
|
export type {
|
|
2
35
|
AnyVariant,
|
|
3
36
|
BenchMatrix,
|
|
@@ -11,82 +44,74 @@ export type {
|
|
|
11
44
|
Variant,
|
|
12
45
|
VariantFn,
|
|
13
46
|
VariantResult,
|
|
14
|
-
} from "./BenchMatrix.ts";
|
|
15
|
-
export { isStatefulVariant, runMatrix } from "./BenchMatrix.ts";
|
|
16
|
-
export type {
|
|
47
|
+
} from "./matrix/BenchMatrix.ts";
|
|
48
|
+
export { isStatefulVariant, runMatrix } from "./matrix/BenchMatrix.ts";
|
|
49
|
+
export type { CasesModule } from "./matrix/CaseLoader.ts";
|
|
50
|
+
export { loadCaseData, loadCasesModule } from "./matrix/CaseLoader.ts";
|
|
51
|
+
export type { FilteredMatrix, MatrixFilter } from "./matrix/MatrixFilter.ts";
|
|
52
|
+
export { filterMatrix, parseMatrixFilter } from "./matrix/MatrixFilter.ts";
|
|
53
|
+
export type { MatrixReportOptions } from "./matrix/MatrixReport.ts";
|
|
54
|
+
export { reportMatrixResults } from "./matrix/MatrixReport.ts";
|
|
17
55
|
export type {
|
|
18
56
|
BenchmarkReport,
|
|
19
|
-
|
|
57
|
+
ComparisonOptions,
|
|
58
|
+
ReportColumn,
|
|
20
59
|
ReportGroup,
|
|
21
|
-
|
|
60
|
+
ReportSection,
|
|
22
61
|
UnknownRecord,
|
|
23
|
-
} from "./BenchmarkReport.ts";
|
|
24
|
-
export { reportResults } from "./BenchmarkReport.ts";
|
|
25
|
-
export type { Configure, DefaultCliArgs } from "./cli/CliArgs.ts";
|
|
26
|
-
export { defaultCliArgs, parseCliArgs } from "./cli/CliArgs.ts";
|
|
27
|
-
export type { ExportOptions, MatrixExportOptions } from "./cli/RunBenchCLI.ts";
|
|
62
|
+
} from "./report/BenchmarkReport.ts";
|
|
28
63
|
export {
|
|
29
|
-
|
|
30
|
-
cliToMatrixOptions,
|
|
31
|
-
defaultMatrixReport,
|
|
32
|
-
defaultReport,
|
|
33
|
-
exportReports,
|
|
64
|
+
computeColumnValues,
|
|
34
65
|
hasField,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
66
|
+
} from "./report/BenchmarkReport.ts";
|
|
67
|
+
export {
|
|
68
|
+
formatBytes,
|
|
69
|
+
formatConvergence,
|
|
70
|
+
integer,
|
|
71
|
+
timeMs,
|
|
72
|
+
truncate,
|
|
73
|
+
} from "./report/Formatters.ts";
|
|
74
|
+
export {
|
|
75
|
+
gcSection,
|
|
76
|
+
gcSections,
|
|
77
|
+
gcStatsSection,
|
|
78
|
+
} from "./report/GcSections.ts";
|
|
79
|
+
export type { GitVersion } from "./report/GitUtils.ts";
|
|
48
80
|
export {
|
|
49
|
-
formatDateWithTimezone,
|
|
50
81
|
formatGitVersion,
|
|
51
82
|
getBaselineVersion,
|
|
52
83
|
getCurrentGitVersion,
|
|
53
|
-
} from "./GitUtils.ts";
|
|
54
|
-
export type { PrepareHtmlOptions } from "./
|
|
55
|
-
export { prepareHtmlData } from "./
|
|
56
|
-
export type { HtmlReportOptions, ReportData } from "./html/index.ts";
|
|
57
|
-
export { generateHtmlReport } from "./html/index.ts";
|
|
58
|
-
export type { MeasuredResults } from "./MeasuredResults.ts";
|
|
59
|
-
export type { CasesModule } from "./matrix/CaseLoader.ts";
|
|
60
|
-
export { loadCaseData, loadCasesModule } from "./matrix/CaseLoader.ts";
|
|
61
|
-
export type { FilteredMatrix, MatrixFilter } from "./matrix/MatrixFilter.ts";
|
|
62
|
-
export { filterMatrix, parseMatrixFilter } from "./matrix/MatrixFilter.ts";
|
|
63
|
-
export type {
|
|
64
|
-
ExtraColumn,
|
|
65
|
-
MatrixReportOptions,
|
|
66
|
-
} from "./matrix/MatrixReport.ts";
|
|
67
|
-
export {
|
|
68
|
-
gcPauseColumn,
|
|
69
|
-
gcStatsColumns,
|
|
70
|
-
heapTotalColumn,
|
|
71
|
-
reportMatrixResults,
|
|
72
|
-
} from "./matrix/MatrixReport.ts";
|
|
73
|
-
export type { RunnerOptions } from "./runners/BenchRunner.ts";
|
|
84
|
+
} from "./report/GitUtils.ts";
|
|
85
|
+
export type { PrepareHtmlOptions } from "./report/HtmlReport.ts";
|
|
86
|
+
export { prepareHtmlData } from "./report/HtmlReport.ts";
|
|
74
87
|
export {
|
|
75
|
-
|
|
88
|
+
adaptiveSections,
|
|
76
89
|
buildGenericSections,
|
|
77
|
-
|
|
78
|
-
gcSection,
|
|
79
|
-
gcStatsSection,
|
|
90
|
+
buildTimeSection,
|
|
80
91
|
optSection,
|
|
81
92
|
runsSection,
|
|
82
93
|
timeSection,
|
|
83
94
|
totalTimeSection,
|
|
84
|
-
} from "./StandardSections.ts";
|
|
85
|
-
export {
|
|
86
|
-
export {
|
|
95
|
+
} from "./report/StandardSections.ts";
|
|
96
|
+
export { reportResults } from "./report/text/TextReport.ts";
|
|
97
|
+
export type {
|
|
98
|
+
BenchGroup,
|
|
99
|
+
BenchmarkSpec,
|
|
100
|
+
BenchSuite,
|
|
101
|
+
} from "./runners/BenchmarkSpec.ts";
|
|
102
|
+
export type { RunnerOptions } from "./runners/BenchRunner.ts";
|
|
103
|
+
export type { MeasuredResults } from "./runners/MeasuredResults.ts";
|
|
87
104
|
export {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
105
|
+
average,
|
|
106
|
+
computeStat,
|
|
107
|
+
isBootstrappable,
|
|
108
|
+
maxBootstrapInput,
|
|
109
|
+
median,
|
|
110
|
+
percentile,
|
|
111
|
+
type StatKind,
|
|
112
|
+
} from "./stats/StatisticalUtils.ts";
|
|
113
|
+
export {
|
|
114
|
+
formatDateWithTimezone,
|
|
115
|
+
formatRelativeTime,
|
|
116
|
+
} from "./viewer/DateFormat.ts";
|
|
117
|
+
export type { ReportData } from "./viewer/ReportData.ts";
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import type { RunnerOptions } from "../runners/BenchRunner.ts";
|
|
2
|
+
import type { MeasuredResults } from "../runners/MeasuredResults.ts";
|
|
3
|
+
import { average } from "../stats/StatisticalUtils.ts";
|
|
4
|
+
import type { CasesModule } from "./CaseLoader.ts";
|
|
5
|
+
import { loadCasesModule } from "./CaseLoader.ts";
|
|
6
|
+
import { runMatrixWithDir } from "./MatrixDirRunner.ts";
|
|
7
|
+
import { runMatrixInline } from "./MatrixInlineRunner.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, allowing mixed variants in a matrix */
|
|
24
|
+
export type AnyVariant<T = unknown> = VariantFn<T> | StatefulVariant<T, any>;
|
|
25
|
+
|
|
26
|
+
/** Case data and optional metadata returned by a cases module */
|
|
27
|
+
export interface LoadedCase<T = unknown> {
|
|
28
|
+
data: T;
|
|
29
|
+
metadata?: Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Default runner settings applied to all matrix benchmarks */
|
|
33
|
+
export interface MatrixDefaults {
|
|
34
|
+
warmup?: number;
|
|
35
|
+
maxTime?: number;
|
|
36
|
+
iterations?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Configuration for a cases x variants benchmark matrix */
|
|
40
|
+
export interface BenchMatrix<T = unknown> {
|
|
41
|
+
name: string;
|
|
42
|
+
variantDir?: string;
|
|
43
|
+
variants?: Record<string, AnyVariant<T>>;
|
|
44
|
+
cases?: string[];
|
|
45
|
+
casesModule?: string;
|
|
46
|
+
baselineDir?: string;
|
|
47
|
+
baselineVariant?: string;
|
|
48
|
+
defaults?: MatrixDefaults;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Named collection of benchmark matrices */
|
|
52
|
+
export interface MatrixSuite {
|
|
53
|
+
name: string;
|
|
54
|
+
matrices: BenchMatrix<any>[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Results for a single variant across all cases */
|
|
58
|
+
export interface VariantResult {
|
|
59
|
+
id: string;
|
|
60
|
+
cases: CaseResult[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Results for a single (variant, case) pair */
|
|
64
|
+
export interface CaseResult {
|
|
65
|
+
caseId: string;
|
|
66
|
+
measured: MeasuredResults;
|
|
67
|
+
metadata?: Record<string, unknown>;
|
|
68
|
+
baseline?: MeasuredResults;
|
|
69
|
+
deltaPercent?: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Aggregated results from running a benchmark matrix */
|
|
73
|
+
export interface MatrixResults {
|
|
74
|
+
name: string;
|
|
75
|
+
variants: VariantResult[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Options for {@link runMatrix} */
|
|
79
|
+
export interface RunMatrixOptions {
|
|
80
|
+
/** Maximum iterations per benchmark */
|
|
81
|
+
iterations?: number;
|
|
82
|
+
/** Maximum time in ms per benchmark */
|
|
83
|
+
maxTime?: number;
|
|
84
|
+
/** Number of warmup iterations before measurement */
|
|
85
|
+
warmup?: number;
|
|
86
|
+
/** Use worker process isolation (default: true for variantDir) */
|
|
87
|
+
useWorker?: boolean;
|
|
88
|
+
/** Number of interleaved batches for baseline comparison */
|
|
89
|
+
batches?: number;
|
|
90
|
+
/** Include first batch in results (normally dropped for OS cache warmup) */
|
|
91
|
+
warmupBatch?: boolean;
|
|
92
|
+
/** Run only these cases (from --filter) */
|
|
93
|
+
filteredCases?: string[];
|
|
94
|
+
/** Run only these variants (from --filter) */
|
|
95
|
+
filteredVariants?: string[];
|
|
96
|
+
/** Force garbage collection between iterations */
|
|
97
|
+
gcForce?: boolean;
|
|
98
|
+
/** Track V8 optimization/deoptimization events */
|
|
99
|
+
traceOpt?: boolean;
|
|
100
|
+
/** Pause duration in ms before warmup begins */
|
|
101
|
+
pauseWarmup?: number;
|
|
102
|
+
/** Pause duration in ms before first measurement */
|
|
103
|
+
pauseFirst?: number;
|
|
104
|
+
/** Pause every N iterations during measurement */
|
|
105
|
+
pauseInterval?: number;
|
|
106
|
+
/** Duration of each pause in ms */
|
|
107
|
+
pauseDuration?: number;
|
|
108
|
+
/** Collect GC statistics via --trace-gc-nvp */
|
|
109
|
+
gcStats?: boolean;
|
|
110
|
+
/** Enable heap allocation profiling */
|
|
111
|
+
alloc?: boolean;
|
|
112
|
+
/** Heap sampling interval in bytes */
|
|
113
|
+
allocInterval?: number;
|
|
114
|
+
/** Maximum stack depth for allocation traces */
|
|
115
|
+
allocDepth?: number;
|
|
116
|
+
/** Enable CPU time profiling */
|
|
117
|
+
profile?: boolean;
|
|
118
|
+
/** CPU profiling sample interval in microseconds */
|
|
119
|
+
profileInterval?: number;
|
|
120
|
+
/** Track function call counts via V8 coverage */
|
|
121
|
+
callCounts?: boolean;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Run a BenchMatrix with inline variants or variantDir */
|
|
125
|
+
export async function runMatrix<T>(
|
|
126
|
+
matrix: BenchMatrix<T>,
|
|
127
|
+
options: RunMatrixOptions = {},
|
|
128
|
+
): Promise<MatrixResults> {
|
|
129
|
+
if (matrix.baselineDir && matrix.baselineVariant)
|
|
130
|
+
throw new Error(
|
|
131
|
+
"BenchMatrix cannot have both 'baselineDir' and 'baselineVariant'",
|
|
132
|
+
);
|
|
133
|
+
if (!matrix.variantDir && !matrix.variants)
|
|
134
|
+
throw new Error("BenchMatrix requires either 'variants' or 'variantDir'");
|
|
135
|
+
|
|
136
|
+
const effectiveOptions = { ...matrix.defaults, ...options };
|
|
137
|
+
const result = matrix.variantDir
|
|
138
|
+
? await runMatrixWithDir(matrix, effectiveOptions)
|
|
139
|
+
: await runMatrixInline(matrix, effectiveOptions);
|
|
140
|
+
|
|
141
|
+
if (matrix.baselineVariant) {
|
|
142
|
+
applyBaselineVariant(result.variants, matrix.baselineVariant);
|
|
143
|
+
}
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Prepare a benchmark function from a variant, calling setup if stateful. */
|
|
148
|
+
export async function prepareBenchFn<T>(
|
|
149
|
+
variant: Variant<T>,
|
|
150
|
+
data: T,
|
|
151
|
+
): Promise<() => void> {
|
|
152
|
+
if (isStatefulVariant(variant)) {
|
|
153
|
+
const state = await variant.setup(data);
|
|
154
|
+
return () => variant.run(state);
|
|
155
|
+
}
|
|
156
|
+
return () => variant(data);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Type guard for StatefulVariant */
|
|
160
|
+
export function isStatefulVariant<T, S>(
|
|
161
|
+
v: Variant<T, S>,
|
|
162
|
+
): v is StatefulVariant<T, S> {
|
|
163
|
+
return typeof v === "object" && "setup" in v && "run" in v;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Apply baselineVariant comparison - one variant is the reference for all others */
|
|
167
|
+
export function applyBaselineVariant(
|
|
168
|
+
variants: VariantResult[],
|
|
169
|
+
baselineVariantId: string,
|
|
170
|
+
): void {
|
|
171
|
+
const baselineVariant = variants.find(v => v.id === baselineVariantId);
|
|
172
|
+
if (!baselineVariant) return;
|
|
173
|
+
|
|
174
|
+
const baselineByCase = new Map(
|
|
175
|
+
baselineVariant.cases.map(c => [c.caseId, c.measured]),
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
for (const variant of variants) {
|
|
179
|
+
if (variant.id === baselineVariantId) continue;
|
|
180
|
+
for (const cr of variant.cases) {
|
|
181
|
+
const base = baselineByCase.get(cr.caseId);
|
|
182
|
+
if (base) {
|
|
183
|
+
cr.baseline = base;
|
|
184
|
+
cr.deltaPercent = computeDeltaPercent(base, cr.measured);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Load cases module and resolve filtered case IDs */
|
|
191
|
+
export async function resolveCases<T>(
|
|
192
|
+
matrix: BenchMatrix<T>,
|
|
193
|
+
options: RunMatrixOptions,
|
|
194
|
+
): Promise<{ casesModule: CasesModule<T> | undefined; caseIds: string[] }> {
|
|
195
|
+
const casesModule = matrix.casesModule
|
|
196
|
+
? await loadCasesModule<T>(matrix.casesModule)
|
|
197
|
+
: undefined;
|
|
198
|
+
const allCaseIds = casesModule?.cases ?? matrix.cases ?? ["default"];
|
|
199
|
+
const caseIds = options.filteredCases ?? allCaseIds;
|
|
200
|
+
return { casesModule, caseIds };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Map matrix options to runner options, applying defaults for maxTime and warmup */
|
|
204
|
+
export function buildRunnerOptions(opts: RunMatrixOptions): RunnerOptions {
|
|
205
|
+
const {
|
|
206
|
+
filteredCases,
|
|
207
|
+
filteredVariants,
|
|
208
|
+
useWorker,
|
|
209
|
+
batches,
|
|
210
|
+
warmupBatch,
|
|
211
|
+
...base
|
|
212
|
+
} = opts;
|
|
213
|
+
const { iterations, maxTime, warmup, ...rest } = base;
|
|
214
|
+
return {
|
|
215
|
+
maxIterations: iterations,
|
|
216
|
+
maxTime: maxTime ?? (iterations ? undefined : 1000),
|
|
217
|
+
warmup: warmup ?? 0,
|
|
218
|
+
...rest,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Compute percentage change of current vs baseline mean */
|
|
223
|
+
export function computeDeltaPercent(
|
|
224
|
+
base: MeasuredResults,
|
|
225
|
+
cur: MeasuredResults,
|
|
226
|
+
): number {
|
|
227
|
+
const avg = average(base.samples);
|
|
228
|
+
if (avg === 0) return 0;
|
|
229
|
+
return ((average(cur.samples) - avg) / avg) * 100;
|
|
230
|
+
}
|
package/src/matrix/CaseLoader.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import type { LoadedCase } from "
|
|
1
|
+
import type { LoadedCase } from "./BenchMatrix.ts";
|
|
2
2
|
|
|
3
|
-
/** Module
|
|
3
|
+
/** Module exporting case IDs and an optional loader for case data */
|
|
4
4
|
export interface CasesModule<T = unknown> {
|
|
5
5
|
cases: string[];
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
/** Subset of cases for quick runs */
|
|
7
|
+
defaultCases?: string[];
|
|
8
|
+
/** Subset of variants for quick runs */
|
|
9
|
+
defaultVariants?: string[];
|
|
8
10
|
loadCase?: (id: string) => LoadedCase<T> | Promise<LoadedCase<T>>;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
/**
|
|
13
|
+
/** Import and validate a cases module, which must export a `cases` array */
|
|
12
14
|
export async function loadCasesModule<T = unknown>(
|
|
13
15
|
moduleUrl: string,
|
|
14
16
|
): Promise<CasesModule<T>> {
|
|
@@ -24,7 +26,7 @@ export async function loadCasesModule<T = unknown>(
|
|
|
24
26
|
};
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
/** Load case data from a CasesModule or
|
|
29
|
+
/** Load case data from a CasesModule, or use the caseId as data if no module */
|
|
28
30
|
export async function loadCaseData<T>(
|
|
29
31
|
casesModule: CasesModule<T> | undefined,
|
|
30
32
|
caseId: string,
|