benchforge 0.1.11 → 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 -294
- 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-BzyUfiyB.d.mts → BenchRunner-DglX1NOn.d.mts} +119 -66
- 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 +711 -558
- 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 +77 -105
- 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 -27
- 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 -51
- 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 +132 -866
- 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 +64 -99
- package/src/export/SpeedscopeTypes.ts +98 -0
- package/src/export/TimeExport.ts +115 -0
- package/src/index.ts +86 -67
- 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 +49 -47
- package/src/matrix/MatrixInlineRunner.ts +50 -0
- package/src/matrix/MatrixReport.ts +90 -250
- package/src/matrix/VariantLoader.ts +5 -5
- 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 +1 -2
- package/src/{heap-sample → profiling/node}/ResolvedProfile.ts +18 -9
- 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 +116 -236
- package/src/runners/BenchRunner.ts +20 -15
- package/src/{Benchmark.ts → runners/BenchmarkSpec.ts} +5 -6
- package/src/runners/CreateRunner.ts +5 -7
- package/src/runners/GcStats.ts +47 -50
- 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 +127 -243
- 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 +135 -151
- package/src/stats/BootstrapDifference.ts +282 -0
- package/src/{PermutationTest.ts → stats/PermutationTest.ts} +8 -17
- 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 +2 -2
- package/src/{tests → test}/BenchMatrix.test.ts +19 -16
- 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 +14 -14
- package/src/{tests → test}/MatrixFilter.test.ts +1 -1
- 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 +39 -38
- 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 +12 -7
- 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 +33 -38
- 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/BrowserHeapSampler-B6asLKWQ.mjs +0 -202
- package/dist/BrowserHeapSampler-B6asLKWQ.mjs.map +0 -1
- package/dist/GcStats-wX7Xyblu.mjs +0 -77
- package/dist/GcStats-wX7Xyblu.mjs.map +0 -1
- package/dist/HeapSampler-B8dtKHn1.mjs.map +0 -1
- package/dist/TimingUtils-DwOwkc8G.mjs +0 -597
- package/dist/TimingUtils-DwOwkc8G.mjs.map +0 -1
- package/dist/browser/index.js +0 -914
- package/dist/src-B-DDaCa9.mjs +0 -3108
- package/dist/src-B-DDaCa9.mjs.map +0 -1
- package/src/BenchMatrix.ts +0 -380
- package/src/BenchmarkReport.ts +0 -161
- package/src/HtmlDataPrep.ts +0 -148
- package/src/StandardSections.ts +0 -261
- package/src/StatisticalUtils.ts +0 -175
- 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/export/SpeedscopeExport.ts +0 -202
- package/src/heap-sample/HeapSampleReport.ts +0 -269
- 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 -157
- 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 +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BenchMatrix } from "
|
|
1
|
+
import type { BenchMatrix } from "./BenchMatrix.ts";
|
|
2
2
|
import { loadCasesModule } from "./CaseLoader.ts";
|
|
3
3
|
import { discoverVariants } from "./VariantLoader.ts";
|
|
4
4
|
|
|
@@ -17,11 +17,8 @@ export interface FilteredMatrix<T = unknown> extends BenchMatrix<T> {
|
|
|
17
17
|
/** Parse filter string: "case/variant", "case/", "/variant", or "case" */
|
|
18
18
|
export function parseMatrixFilter(filter: string): MatrixFilter {
|
|
19
19
|
if (filter.includes("/")) {
|
|
20
|
-
const [casePart,
|
|
21
|
-
return {
|
|
22
|
-
case: casePart || undefined,
|
|
23
|
-
variant: variantPart || undefined,
|
|
24
|
-
};
|
|
20
|
+
const [casePart, varPart] = filter.split("/", 2);
|
|
21
|
+
return { case: casePart || undefined, variant: varPart || undefined };
|
|
25
22
|
}
|
|
26
23
|
return { case: filter };
|
|
27
24
|
}
|
|
@@ -36,68 +33,73 @@ export async function filterMatrix<T>(
|
|
|
36
33
|
const caseList = await getFilteredCases(matrix, filter.case);
|
|
37
34
|
const variantList = await getFilteredVariants(matrix, filter.variant);
|
|
38
35
|
|
|
39
|
-
const filteredCases =
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const filteredVariants =
|
|
45
|
-
variantList && matrix.filteredVariants
|
|
46
|
-
? variantList.filter(v => matrix.filteredVariants!.includes(v))
|
|
47
|
-
: (variantList ?? matrix.filteredVariants);
|
|
36
|
+
const filteredCases = intersectFilters(caseList, matrix.filteredCases);
|
|
37
|
+
const filteredVariants = intersectFilters(
|
|
38
|
+
variantList,
|
|
39
|
+
matrix.filteredVariants,
|
|
40
|
+
);
|
|
48
41
|
|
|
49
42
|
return { ...matrix, filteredCases, filteredVariants };
|
|
50
43
|
}
|
|
51
44
|
|
|
52
|
-
/**
|
|
45
|
+
/** Collect all case IDs from either casesModule or inline cases */
|
|
46
|
+
export async function resolveCaseIds<T>(
|
|
47
|
+
matrix: BenchMatrix<T>,
|
|
48
|
+
): Promise<string[] | undefined> {
|
|
49
|
+
if (matrix.casesModule)
|
|
50
|
+
return (await loadCasesModule(matrix.casesModule)).cases;
|
|
51
|
+
return matrix.cases;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Collect all variant IDs from either inline variants or variantDir */
|
|
55
|
+
export async function resolveVariantIds<T>(
|
|
56
|
+
matrix: BenchMatrix<T>,
|
|
57
|
+
): Promise<string[]> {
|
|
58
|
+
if (matrix.variants) return Object.keys(matrix.variants);
|
|
59
|
+
if (matrix.variantDir) return discoverVariants(matrix.variantDir);
|
|
60
|
+
throw new Error("BenchMatrix requires 'variants' or 'variantDir'");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Return case IDs matching a substring pattern, or all if no pattern */
|
|
53
64
|
async function getFilteredCases<T>(
|
|
54
65
|
matrix: BenchMatrix<T>,
|
|
55
66
|
casePattern?: string,
|
|
56
67
|
): Promise<string[] | undefined> {
|
|
57
68
|
if (!casePattern) return undefined;
|
|
58
|
-
|
|
59
|
-
const caseIds = matrix.casesModule
|
|
60
|
-
? (await loadCasesModule(matrix.casesModule)).cases
|
|
61
|
-
: matrix.cases;
|
|
69
|
+
const caseIds = await resolveCaseIds(matrix);
|
|
62
70
|
if (!caseIds) return ["default"]; // implicit single case
|
|
63
|
-
|
|
64
|
-
const filtered = caseIds.filter(id => matchPattern(id, casePattern));
|
|
65
|
-
if (filtered.length === 0) {
|
|
66
|
-
throw new Error(`No cases match filter: "${casePattern}"`);
|
|
67
|
-
}
|
|
68
|
-
return filtered;
|
|
71
|
+
return filterByPattern(caseIds, casePattern, "cases");
|
|
69
72
|
}
|
|
70
73
|
|
|
71
|
-
/**
|
|
74
|
+
/** Return variant IDs matching a substring pattern, or all if no pattern */
|
|
72
75
|
async function getFilteredVariants<T>(
|
|
73
76
|
matrix: BenchMatrix<T>,
|
|
74
77
|
variantPattern?: string,
|
|
75
78
|
): Promise<string[] | undefined> {
|
|
76
79
|
if (!variantPattern) return undefined;
|
|
80
|
+
const allIds = await resolveVariantIds(matrix);
|
|
81
|
+
return filterByPattern(allIds, variantPattern, "variants");
|
|
82
|
+
}
|
|
77
83
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
throw new Error(`No variants match filter: "${variantPattern}"`);
|
|
84
|
-
}
|
|
85
|
-
return ids;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (matrix.variantDir) {
|
|
89
|
-
const allIds = await discoverVariants(matrix.variantDir);
|
|
90
|
-
const filtered = allIds.filter(id => matchPattern(id, variantPattern));
|
|
91
|
-
if (filtered.length === 0) {
|
|
92
|
-
throw new Error(`No variants match filter: "${variantPattern}"`);
|
|
93
|
-
}
|
|
94
|
-
return filtered;
|
|
95
|
-
}
|
|
84
|
+
/** Intersect two optional filter lists: both present ==> intersection, otherwise the one that exists */
|
|
85
|
+
function intersectFilters(a?: string[], b?: string[]): string[] | undefined {
|
|
86
|
+
if (a && b) return a.filter(v => b.includes(v));
|
|
87
|
+
return a ?? b;
|
|
88
|
+
}
|
|
96
89
|
|
|
97
|
-
|
|
90
|
+
/** Filter IDs by substring pattern, throwing if no matches */
|
|
91
|
+
function filterByPattern(
|
|
92
|
+
ids: string[],
|
|
93
|
+
pattern: string,
|
|
94
|
+
label: string,
|
|
95
|
+
): string[] {
|
|
96
|
+
const filtered = ids.filter(id => matchPattern(id, pattern));
|
|
97
|
+
if (filtered.length === 0)
|
|
98
|
+
throw new Error(`No ${label} match filter: "${pattern}"`);
|
|
99
|
+
return filtered;
|
|
98
100
|
}
|
|
99
101
|
|
|
100
|
-
/**
|
|
102
|
+
/** Case-insensitive substring match */
|
|
101
103
|
function matchPattern(id: string, pattern: string): boolean {
|
|
102
104
|
return id.toLowerCase().includes(pattern.toLowerCase());
|
|
103
105
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { TimingRunner } from "../runners/TimingRunner.ts";
|
|
2
|
+
import type {
|
|
3
|
+
BenchMatrix,
|
|
4
|
+
CaseResult,
|
|
5
|
+
RunMatrixOptions,
|
|
6
|
+
VariantResult,
|
|
7
|
+
} from "./BenchMatrix.ts";
|
|
8
|
+
import {
|
|
9
|
+
buildRunnerOptions,
|
|
10
|
+
prepareBenchFn,
|
|
11
|
+
resolveCases,
|
|
12
|
+
} from "./BenchMatrix.ts";
|
|
13
|
+
import { loadCaseData } from "./CaseLoader.ts";
|
|
14
|
+
|
|
15
|
+
/** Run matrix with in-memory variant functions (no worker isolation) */
|
|
16
|
+
export async function runMatrixInline<T>(
|
|
17
|
+
matrix: BenchMatrix<T>,
|
|
18
|
+
options: RunMatrixOptions,
|
|
19
|
+
): Promise<{ name: string; variants: VariantResult[] }> {
|
|
20
|
+
if (matrix.baselineDir)
|
|
21
|
+
throw new Error(
|
|
22
|
+
"BenchMatrix with inline 'variants' cannot use 'baselineDir'. Use 'variantDir' instead.",
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const { casesModule, caseIds } = await resolveCases(matrix, options);
|
|
26
|
+
const runner = new TimingRunner();
|
|
27
|
+
const runnerOpts = buildRunnerOptions(options);
|
|
28
|
+
|
|
29
|
+
const allEntries = Object.entries(matrix.variants!);
|
|
30
|
+
const { filteredVariants } = options;
|
|
31
|
+
const variantEntries = filteredVariants
|
|
32
|
+
? allEntries.filter(([id]) => filteredVariants.includes(id))
|
|
33
|
+
: allEntries;
|
|
34
|
+
|
|
35
|
+
const variants: VariantResult[] = [];
|
|
36
|
+
for (const [variantId, variant] of variantEntries) {
|
|
37
|
+
const cases: CaseResult[] = [];
|
|
38
|
+
for (const caseId of caseIds) {
|
|
39
|
+
const loaded = await loadCaseData(casesModule, caseId);
|
|
40
|
+
const data = casesModule || matrix.cases ? loaded.data : (undefined as T);
|
|
41
|
+
const fn = await prepareBenchFn(variant, data);
|
|
42
|
+
const spec = { name: variantId, fn };
|
|
43
|
+
const [measured] = await runner.runBench(spec, runnerOpts);
|
|
44
|
+
cases.push({ caseId, measured, metadata: loaded.metadata });
|
|
45
|
+
}
|
|
46
|
+
variants.push({ id: variantId, cases });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return { name: matrix.name, variants };
|
|
50
|
+
}
|
|
@@ -1,106 +1,53 @@
|
|
|
1
|
-
import type { CaseResult, MatrixResults } from "../BenchMatrix.ts";
|
|
2
|
-
import { injectDiffColumns, type ResultsMapper } from "../BenchmarkReport.ts";
|
|
3
|
-
import { totalProfileBytes } from "../heap-sample/HeapSampleReport.ts";
|
|
4
|
-
import { type GcStatsInfo, gcStatsSection } from "../StandardSections.ts";
|
|
5
1
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} from "../
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
key: string;
|
|
25
|
-
title: string;
|
|
26
|
-
groupTitle?: string; // optional column group header
|
|
27
|
-
extract: (caseResult: CaseResult) => unknown;
|
|
28
|
-
formatter?: (value: unknown) => string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Options for matrix report generation */
|
|
2
|
+
type ComparisonOptions,
|
|
3
|
+
computeDiffCI,
|
|
4
|
+
extractSectionValues,
|
|
5
|
+
findPrimaryColumn,
|
|
6
|
+
type ReportSection,
|
|
7
|
+
} from "../report/BenchmarkReport.ts";
|
|
8
|
+
import { truncate } from "../report/Formatters.ts";
|
|
9
|
+
import { runsSection, timeSection } from "../report/StandardSections.ts";
|
|
10
|
+
import { buildTable } from "../report/text/TableReport.ts";
|
|
11
|
+
import { sectionColumnGroups } from "../report/text/TextReport.ts";
|
|
12
|
+
import type { MeasuredResults } from "../runners/MeasuredResults.ts";
|
|
13
|
+
import type {
|
|
14
|
+
CaseResult,
|
|
15
|
+
MatrixResults,
|
|
16
|
+
VariantResult,
|
|
17
|
+
} from "./BenchMatrix.ts";
|
|
18
|
+
|
|
19
|
+
/** Options for {@link reportMatrixResults} */
|
|
32
20
|
export interface MatrixReportOptions {
|
|
33
|
-
|
|
34
|
-
sections?:
|
|
35
|
-
|
|
21
|
+
/** ReportSection sections (default: [timeSection, runsSection]) */
|
|
22
|
+
sections?: ReportSection[];
|
|
23
|
+
/** Custom title for the variant column (default: "variant") */
|
|
24
|
+
variantTitle?: string;
|
|
25
|
+
/** Comparison options (equivalence margin, batch trimming) */
|
|
26
|
+
comparison?: ComparisonOptions;
|
|
36
27
|
}
|
|
37
28
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
time: number;
|
|
42
|
-
samples: number;
|
|
43
|
-
diffCI?: DifferenceCI;
|
|
29
|
+
interface VariantCase {
|
|
30
|
+
variant: VariantResult;
|
|
31
|
+
cr: CaseResult;
|
|
44
32
|
}
|
|
45
33
|
|
|
46
|
-
|
|
47
|
-
export const gcStatsColumns: ExtraColumn[] = gcStatsSection
|
|
48
|
-
.columns()[0]
|
|
49
|
-
.columns.map(col => ({
|
|
50
|
-
key: col.key as string,
|
|
51
|
-
title: col.title,
|
|
52
|
-
groupTitle: "GC",
|
|
53
|
-
extract: (r: CaseResult) =>
|
|
54
|
-
gcStatsSection.extract(r.measured)[col.key as keyof GcStatsInfo],
|
|
55
|
-
formatter: (v: unknown) => col.formatter?.(v) ?? "-",
|
|
56
|
-
}));
|
|
57
|
-
|
|
58
|
-
/** GC pause time column */
|
|
59
|
-
export const gcPauseColumn: ExtraColumn = {
|
|
60
|
-
key: "gcPause",
|
|
61
|
-
title: "pause",
|
|
62
|
-
groupTitle: "GC",
|
|
63
|
-
extract: r => r.measured.gcStats?.gcPauseTime,
|
|
64
|
-
formatter: v => (v != null ? `${(v as number).toFixed(1)}ms` : "-"),
|
|
65
|
-
};
|
|
34
|
+
type Row = Record<string, unknown> & { name: string };
|
|
66
35
|
|
|
67
|
-
|
|
68
|
-
export const heapTotalColumn: ExtraColumn = {
|
|
69
|
-
key: "heapTotal",
|
|
70
|
-
title: "heap",
|
|
71
|
-
extract: r => {
|
|
72
|
-
const profile = r.measured.heapProfile;
|
|
73
|
-
if (!profile?.head) return undefined;
|
|
74
|
-
return totalProfileBytes(profile);
|
|
75
|
-
},
|
|
76
|
-
formatter: formatBytesOrDash,
|
|
77
|
-
};
|
|
36
|
+
const defaultSections: ReportSection[] = [timeSection, runsSection];
|
|
78
37
|
|
|
79
|
-
/** Format matrix results as one table per case */
|
|
38
|
+
/** Format matrix results as text, with one table per case */
|
|
80
39
|
export function reportMatrixResults(
|
|
81
40
|
results: MatrixResults,
|
|
82
41
|
options?: MatrixReportOptions,
|
|
83
42
|
): string {
|
|
84
|
-
|
|
85
|
-
const header = `Matrix: ${results.name}`;
|
|
86
|
-
return [header, ...tables].join("\n\n");
|
|
87
|
-
}
|
|
43
|
+
if (results.variants.length === 0) return `Matrix: ${results.name}`;
|
|
88
44
|
|
|
89
|
-
|
|
90
|
-
function formatBytesOrDash(value: unknown): string {
|
|
91
|
-
return formatBytes(value) ?? "-";
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** Build one table for each case showing all variants */
|
|
95
|
-
function buildCaseTables(
|
|
96
|
-
results: MatrixResults,
|
|
97
|
-
options?: MatrixReportOptions,
|
|
98
|
-
): string[] {
|
|
99
|
-
if (results.variants.length === 0) return [];
|
|
100
|
-
|
|
101
|
-
// Get all case IDs from first variant (all variants have same cases)
|
|
45
|
+
// all variants have the same cases
|
|
102
46
|
const caseIds = results.variants[0].cases.map(c => c.caseId);
|
|
103
|
-
|
|
47
|
+
const tables = caseIds.map(caseId =>
|
|
48
|
+
buildCaseTable(results, caseId, options),
|
|
49
|
+
);
|
|
50
|
+
return [`Matrix: ${results.name}`, ...tables].join("\n\n");
|
|
104
51
|
}
|
|
105
52
|
|
|
106
53
|
/** Build table for a single case showing all variants */
|
|
@@ -109,19 +56,45 @@ function buildCaseTable(
|
|
|
109
56
|
caseId: string,
|
|
110
57
|
options?: MatrixReportOptions,
|
|
111
58
|
): string {
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
59
|
+
const title = formatCaseTitle(results, caseId);
|
|
60
|
+
const sections = options?.sections ?? defaultSections;
|
|
61
|
+
const variantTitle = options?.variantTitle ?? "variant";
|
|
62
|
+
const primaryCol = findPrimaryColumn(sections);
|
|
63
|
+
|
|
64
|
+
const caseResults = collectCaseResults(results, caseId);
|
|
65
|
+
const shared = sharedBaseline(caseResults);
|
|
66
|
+
|
|
67
|
+
const rows: Row[] = caseResults.flatMap(({ variant, cr }) => {
|
|
68
|
+
const vals = extractSectionValues(cr.measured, sections, cr.metadata);
|
|
69
|
+
const row: Row = { name: truncate(variant.id, 25), ...vals };
|
|
70
|
+
if (cr.baseline && primaryCol?.statKind) {
|
|
71
|
+
const { statKind, higherIsBetter } = primaryCol;
|
|
72
|
+
row.diffCI = computeDiffCI(
|
|
73
|
+
cr.baseline,
|
|
74
|
+
cr.measured,
|
|
75
|
+
statKind,
|
|
76
|
+
options?.comparison,
|
|
77
|
+
higherIsBetter,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
const out: Row[] = [row];
|
|
81
|
+
if (cr.baseline && !shared)
|
|
82
|
+
out.push({
|
|
83
|
+
name: " \u21B3 baseline",
|
|
84
|
+
...extractSectionValues(cr.baseline, sections, cr.metadata),
|
|
85
|
+
});
|
|
86
|
+
return out;
|
|
87
|
+
});
|
|
117
88
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
89
|
+
if (shared)
|
|
90
|
+
rows.push({
|
|
91
|
+
name: "=> baseline",
|
|
92
|
+
...extractSectionValues(shared, sections),
|
|
93
|
+
});
|
|
121
94
|
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
return `${
|
|
95
|
+
const hasDiff = rows.some(r => r.diffCI);
|
|
96
|
+
const cols = sectionColumnGroups(sections, hasDiff, variantTitle);
|
|
97
|
+
return `${title}\n${buildTable(cols, [{ results: rows }])}`;
|
|
125
98
|
}
|
|
126
99
|
|
|
127
100
|
/** Format case title with metadata if available */
|
|
@@ -129,162 +102,29 @@ function formatCaseTitle(results: MatrixResults, caseId: string): string {
|
|
|
129
102
|
const caseResult = results.variants[0]?.cases.find(c => c.caseId === caseId);
|
|
130
103
|
const metadata = caseResult?.metadata;
|
|
131
104
|
|
|
132
|
-
if (metadata
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
return caseId;
|
|
105
|
+
if (!metadata || Object.keys(metadata).length === 0) return caseId;
|
|
106
|
+
const meta = Object.entries(metadata)
|
|
107
|
+
.map(([k, v]) => `${v} ${k}`)
|
|
108
|
+
.join(", ");
|
|
109
|
+
return `${caseId} (${meta})`;
|
|
139
110
|
}
|
|
140
111
|
|
|
141
|
-
/**
|
|
142
|
-
function
|
|
112
|
+
/** Collect (variant, caseResult) pairs for a given caseId */
|
|
113
|
+
function collectCaseResults(
|
|
143
114
|
results: MatrixResults,
|
|
144
115
|
caseId: string,
|
|
145
|
-
|
|
146
|
-
caseTitle: string,
|
|
147
|
-
): string {
|
|
148
|
-
const sections = options.sections!;
|
|
149
|
-
const variantTitle = options.variantTitle ?? "name";
|
|
150
|
-
|
|
151
|
-
const rows: Record<string, unknown>[] = [];
|
|
152
|
-
let hasBaseline = false;
|
|
153
|
-
|
|
154
|
-
for (const variant of results.variants) {
|
|
155
|
-
const caseResult = variant.cases.find(c => c.caseId === caseId);
|
|
156
|
-
if (!caseResult) continue;
|
|
157
|
-
|
|
158
|
-
const row: Record<string, unknown> = { name: truncate(variant.id, 25) };
|
|
159
|
-
|
|
160
|
-
for (const section of sections) {
|
|
161
|
-
Object.assign(
|
|
162
|
-
row,
|
|
163
|
-
section.extract(caseResult.measured, caseResult.metadata),
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (caseResult.baseline) {
|
|
168
|
-
hasBaseline = true;
|
|
169
|
-
const { samples: base } = caseResult.baseline;
|
|
170
|
-
row.diffCI = bootstrapDifferenceCI(base, caseResult.measured.samples);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
rows.push(row);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const columnGroups = buildSectionColumns(sections, variantTitle, hasBaseline);
|
|
177
|
-
const resultGroup: ResultGroup<Record<string, unknown>> = { results: rows };
|
|
178
|
-
const table = buildTable(columnGroups, [resultGroup]);
|
|
179
|
-
return `${caseTitle}\n${table}`;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/** Build rows for all variants for a given case */
|
|
183
|
-
function buildCaseRows(
|
|
184
|
-
results: MatrixResults,
|
|
185
|
-
caseId: string,
|
|
186
|
-
extraColumns?: ExtraColumn[],
|
|
187
|
-
): MatrixReportRow[] {
|
|
116
|
+
): VariantCase[] {
|
|
188
117
|
return results.variants.flatMap(variant => {
|
|
189
|
-
const
|
|
190
|
-
return
|
|
118
|
+
const cr = variant.cases.find(c => c.caseId === caseId);
|
|
119
|
+
return cr ? [{ variant, cr }] : [];
|
|
191
120
|
});
|
|
192
121
|
}
|
|
193
122
|
|
|
194
|
-
/**
|
|
195
|
-
function
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
)
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
columns: [{ key: "name", title: variantTitle }],
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const ciKey = "diffCI" as keyof MatrixReportRow;
|
|
205
|
-
const diffCol = { key: ciKey, title: "Δ% CI", formatter: formatDiff };
|
|
206
|
-
const timeCol: ColumnGroup<MatrixReportRow> = {
|
|
207
|
-
columns: [
|
|
208
|
-
{ key: "time", title: "time", formatter: duration },
|
|
209
|
-
...(hasBaseline ? [diffCol] : []),
|
|
210
|
-
],
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
const groups: ColumnGroup<MatrixReportRow>[] = [nameCol, timeCol];
|
|
214
|
-
|
|
215
|
-
// Add extra columns, grouped by groupTitle
|
|
216
|
-
const extraColumns = options?.extraColumns;
|
|
217
|
-
if (extraColumns?.length) {
|
|
218
|
-
const byGroup = new Map<string | undefined, ExtraColumn[]>();
|
|
219
|
-
for (const col of extraColumns) {
|
|
220
|
-
const group = byGroup.get(col.groupTitle) ?? [];
|
|
221
|
-
group.push(col);
|
|
222
|
-
byGroup.set(col.groupTitle, group);
|
|
223
|
-
}
|
|
224
|
-
for (const [groupTitle, cols] of byGroup) {
|
|
225
|
-
groups.push({
|
|
226
|
-
groupTitle,
|
|
227
|
-
columns: cols.map(col => ({
|
|
228
|
-
key: col.key as keyof MatrixReportRow,
|
|
229
|
-
title: col.title,
|
|
230
|
-
formatter: col.formatter ?? String,
|
|
231
|
-
})),
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return groups;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/** Build column groups from ResultsMapper sections */
|
|
240
|
-
function buildSectionColumns(
|
|
241
|
-
sections: ResultsMapper[],
|
|
242
|
-
variantTitle: string,
|
|
243
|
-
hasBaseline: boolean,
|
|
244
|
-
): ColumnGroup<Record<string, unknown>>[] {
|
|
245
|
-
const nameCol: ColumnGroup<Record<string, unknown>> = {
|
|
246
|
-
columns: [{ key: "name", title: variantTitle }],
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
const sectionColumns = sections.flatMap(s => s.columns());
|
|
250
|
-
const columnGroups = hasBaseline
|
|
251
|
-
? injectDiffColumns(sectionColumns)
|
|
252
|
-
: (sectionColumns as ColumnGroup<Record<string, unknown>>[]);
|
|
253
|
-
|
|
254
|
-
return [nameCol, ...columnGroups];
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/** Build a single row from case result */
|
|
258
|
-
function buildRow(
|
|
259
|
-
variantId: string,
|
|
260
|
-
caseResult: CaseResult,
|
|
261
|
-
extraColumns?: ExtraColumn[],
|
|
262
|
-
): MatrixReportRow {
|
|
263
|
-
const { measured, baseline } = caseResult;
|
|
264
|
-
const samples = measured.samples;
|
|
265
|
-
const time = measured.time?.avg ?? average(samples);
|
|
266
|
-
|
|
267
|
-
const row: MatrixReportRow = {
|
|
268
|
-
name: truncate(variantId, 25),
|
|
269
|
-
time,
|
|
270
|
-
samples: samples.length,
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
if (baseline) {
|
|
274
|
-
row.diffCI = bootstrapDifferenceCI(baseline.samples, samples);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (extraColumns) {
|
|
278
|
-
for (const col of extraColumns) {
|
|
279
|
-
row[col.key] = col.extract(caseResult);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return row;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/** Format diff with CI, or "baseline" marker */
|
|
287
|
-
function formatDiff(value: unknown): string | null {
|
|
288
|
-
if (!value) return null;
|
|
289
|
-
return formatDiffWithCI(value as DifferenceCI);
|
|
123
|
+
/** @return shared baseline if all variants reference the same one (baselineVariant mode) */
|
|
124
|
+
function sharedBaseline(
|
|
125
|
+
caseResults: VariantCase[],
|
|
126
|
+
): MeasuredResults | undefined {
|
|
127
|
+
const baselines = caseResults.map(({ cr }) => cr.baseline).filter(Boolean);
|
|
128
|
+
if (baselines.length < 2) return undefined;
|
|
129
|
+
return baselines.every(b => b === baselines[0]) ? baselines[0] : undefined;
|
|
290
130
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
import type { Variant } from "
|
|
3
|
+
import type { Variant } from "./BenchMatrix.ts";
|
|
4
4
|
|
|
5
|
-
/**
|
|
5
|
+
/** List variant IDs by scanning .ts files in a directory */
|
|
6
6
|
export async function discoverVariants(dirUrl: string): Promise<string[]> {
|
|
7
7
|
const dirPath = fileURLToPath(dirUrl);
|
|
8
8
|
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
@@ -12,7 +12,7 @@ export async function discoverVariants(dirUrl: string): Promise<string[]> {
|
|
|
12
12
|
.sort();
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
/**
|
|
15
|
+
/** Import a variant module and return its run/setup exports as a Variant */
|
|
16
16
|
export async function loadVariant<T = unknown>(
|
|
17
17
|
dirUrl: string,
|
|
18
18
|
variantId: string,
|
|
@@ -22,12 +22,12 @@ export async function loadVariant<T = unknown>(
|
|
|
22
22
|
return extractVariant(module, variantId, moduleUrl);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
/**
|
|
25
|
+
/** Resolve the import URL for a variant file */
|
|
26
26
|
export function variantModuleUrl(dirUrl: string, variantId: string): string {
|
|
27
27
|
return new URL(`${variantId}.ts`, dirUrl).href;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
/**
|
|
30
|
+
/** Validate and extract a Variant from a module's exports */
|
|
31
31
|
function extractVariant<T>(
|
|
32
32
|
module: Record<string, unknown>,
|
|
33
33
|
variantId: string,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import {
|
|
2
|
+
instrumentOpts,
|
|
3
|
+
startInstruments,
|
|
4
|
+
stopInstruments,
|
|
5
|
+
} from "./BrowserCDP.ts";
|
|
6
|
+
import type { BrowserProfileResult, ProfileCtx } from "./BrowserProfiler.ts";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Bench function mode: run window.__bench in a timed iteration loop.
|
|
10
|
+
*
|
|
11
|
+
* Simplified vs TimingRunner because it runs inside page.evaluate()
|
|
12
|
+
* where shared code, Node APIs, and V8 intrinsics are unavailable.
|
|
13
|
+
*
|
|
14
|
+
* Not feasible in browser page context:
|
|
15
|
+
* - heap tracking (no getHeapStatistics)
|
|
16
|
+
* - V8 opt status tracing (no %GetOptimizationStatus)
|
|
17
|
+
* - explicit GC or pause-for-compilation
|
|
18
|
+
*/
|
|
19
|
+
export async function runBenchLoop(
|
|
20
|
+
ctx: ProfileCtx,
|
|
21
|
+
): Promise<BrowserProfileResult> {
|
|
22
|
+
const { page, cdp, params, samplingInterval } = ctx;
|
|
23
|
+
const maxTime = params.maxTime ?? Number.MAX_SAFE_INTEGER;
|
|
24
|
+
const maxIter = params.maxIterations ?? Number.MAX_SAFE_INTEGER;
|
|
25
|
+
const opts = instrumentOpts(params, samplingInterval);
|
|
26
|
+
|
|
27
|
+
await startInstruments(cdp, opts);
|
|
28
|
+
|
|
29
|
+
const { samples, totalMs } = await page.evaluate(
|
|
30
|
+
async ({ maxTime, maxIter }) => {
|
|
31
|
+
const bench = (globalThis as any).__bench;
|
|
32
|
+
const estimated = Math.min(maxIter, Math.ceil(maxTime / 0.1));
|
|
33
|
+
const samples = new Array<number>(estimated);
|
|
34
|
+
let count = 0;
|
|
35
|
+
const startAll = performance.now();
|
|
36
|
+
const deadline = startAll + maxTime;
|
|
37
|
+
for (let i = 0; i < maxIter && performance.now() < deadline; i++) {
|
|
38
|
+
const t0 = performance.now();
|
|
39
|
+
await bench();
|
|
40
|
+
samples[count++] = performance.now() - t0;
|
|
41
|
+
}
|
|
42
|
+
samples.length = count;
|
|
43
|
+
return { samples, totalMs: performance.now() - startAll };
|
|
44
|
+
},
|
|
45
|
+
{ maxTime, maxIter },
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const collected = await stopInstruments(cdp, opts);
|
|
49
|
+
|
|
50
|
+
return { samples, wallTimeMs: totalMs, ...collected };
|
|
51
|
+
}
|