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,153 @@
|
|
|
1
|
+
import type { RunnerOptions } from "../runners/BenchRunner.ts";
|
|
2
|
+
import type { MeasuredResults } from "../runners/MeasuredResults.ts";
|
|
3
|
+
import { runBatched } from "../runners/MergeBatches.ts";
|
|
4
|
+
import { runMatrixVariant } from "../runners/RunnerOrchestrator.ts";
|
|
5
|
+
import type {
|
|
6
|
+
BenchMatrix,
|
|
7
|
+
CaseResult,
|
|
8
|
+
RunMatrixOptions,
|
|
9
|
+
VariantResult,
|
|
10
|
+
} from "./BenchMatrix.ts";
|
|
11
|
+
import {
|
|
12
|
+
buildRunnerOptions,
|
|
13
|
+
computeDeltaPercent,
|
|
14
|
+
resolveCases,
|
|
15
|
+
} from "./BenchMatrix.ts";
|
|
16
|
+
import type { CasesModule } from "./CaseLoader.ts";
|
|
17
|
+
import { loadCaseData } from "./CaseLoader.ts";
|
|
18
|
+
import { discoverVariants } from "./VariantLoader.ts";
|
|
19
|
+
|
|
20
|
+
type VariantArgs = Parameters<typeof runMatrixVariant>[0];
|
|
21
|
+
|
|
22
|
+
/** Shared state for directory-based matrix execution */
|
|
23
|
+
interface DirMatrixContext<T> {
|
|
24
|
+
matrix: BenchMatrix<T>;
|
|
25
|
+
casesModule?: CasesModule<T>;
|
|
26
|
+
baselineIds: string[];
|
|
27
|
+
caseIds: string[];
|
|
28
|
+
runnerOpts: RunnerOptions;
|
|
29
|
+
batches: number;
|
|
30
|
+
warmupBatch: boolean;
|
|
31
|
+
useWorker: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Run matrix using variant files from a directory, each in a worker process */
|
|
35
|
+
export async function runMatrixWithDir<T>(
|
|
36
|
+
matrix: BenchMatrix<T>,
|
|
37
|
+
options: RunMatrixOptions,
|
|
38
|
+
): Promise<{ name: string; variants: VariantResult[] }> {
|
|
39
|
+
const allVariantIds = await discoverVariants(matrix.variantDir!);
|
|
40
|
+
if (allVariantIds.length === 0) {
|
|
41
|
+
throw new Error(`No variants found in ${matrix.variantDir}`);
|
|
42
|
+
}
|
|
43
|
+
const variantIds = options.filteredVariants ?? allVariantIds;
|
|
44
|
+
|
|
45
|
+
const ctx = await createDirContext(matrix, options);
|
|
46
|
+
const variants = await runDirVariants(variantIds, ctx);
|
|
47
|
+
return { name: matrix.name, variants };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Create context for directory-based matrix execution */
|
|
51
|
+
async function createDirContext<T>(
|
|
52
|
+
matrix: BenchMatrix<T>,
|
|
53
|
+
options: RunMatrixOptions,
|
|
54
|
+
): Promise<DirMatrixContext<T>> {
|
|
55
|
+
const baselineIds = matrix.baselineDir
|
|
56
|
+
? await discoverVariants(matrix.baselineDir)
|
|
57
|
+
: [];
|
|
58
|
+
const { casesModule, caseIds } = await resolveCases(matrix, options);
|
|
59
|
+
const runnerOpts = buildRunnerOptions(options);
|
|
60
|
+
const { batches = 1, warmupBatch = false, useWorker = true } = options;
|
|
61
|
+
return {
|
|
62
|
+
matrix,
|
|
63
|
+
casesModule,
|
|
64
|
+
baselineIds,
|
|
65
|
+
caseIds,
|
|
66
|
+
runnerOpts,
|
|
67
|
+
batches,
|
|
68
|
+
warmupBatch,
|
|
69
|
+
useWorker,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Run all variants sequentially, collecting per-case results */
|
|
74
|
+
async function runDirVariants<T>(
|
|
75
|
+
variantIds: string[],
|
|
76
|
+
ctx: DirMatrixContext<T>,
|
|
77
|
+
): Promise<VariantResult[]> {
|
|
78
|
+
const variants: VariantResult[] = [];
|
|
79
|
+
for (const id of variantIds) {
|
|
80
|
+
const cases = await runDirVariantCases(id, ctx);
|
|
81
|
+
variants.push({ id, cases });
|
|
82
|
+
}
|
|
83
|
+
return variants;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Run all cases for a single variant */
|
|
87
|
+
async function runDirVariantCases<T>(
|
|
88
|
+
variantId: string,
|
|
89
|
+
ctx: DirMatrixContext<T>,
|
|
90
|
+
): Promise<CaseResult[]> {
|
|
91
|
+
const { matrix, casesModule, caseIds, runnerOpts, batches } = ctx;
|
|
92
|
+
const cases: CaseResult[] = [];
|
|
93
|
+
|
|
94
|
+
for (const caseId of caseIds) {
|
|
95
|
+
const caseData = matrix.cases && !matrix.casesModule ? caseId : undefined;
|
|
96
|
+
const variantArgs: VariantArgs = {
|
|
97
|
+
variantDir: matrix.variantDir!,
|
|
98
|
+
variantId,
|
|
99
|
+
caseId,
|
|
100
|
+
caseData,
|
|
101
|
+
casesModule: matrix.casesModule,
|
|
102
|
+
runner: "timing" as const,
|
|
103
|
+
options: runnerOpts,
|
|
104
|
+
useWorker: ctx.useWorker,
|
|
105
|
+
};
|
|
106
|
+
const baselineArgs =
|
|
107
|
+
matrix.baselineDir && ctx.baselineIds.includes(variantId)
|
|
108
|
+
? { ...variantArgs, variantDir: matrix.baselineDir! }
|
|
109
|
+
: undefined;
|
|
110
|
+
|
|
111
|
+
const { metadata } = await loadCaseData(casesModule, caseId);
|
|
112
|
+
const { measured, baseline } =
|
|
113
|
+
batches > 1
|
|
114
|
+
? await runCaseBatched(variantArgs, baselineArgs, ctx)
|
|
115
|
+
: await runCaseSingle(variantArgs, baselineArgs);
|
|
116
|
+
const deltaPercent = baseline
|
|
117
|
+
? computeDeltaPercent(baseline, measured)
|
|
118
|
+
: undefined;
|
|
119
|
+
cases.push({ caseId, measured, metadata, baseline, deltaPercent });
|
|
120
|
+
}
|
|
121
|
+
return cases;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Run a batched measurement for a case, alternating current/baseline order. */
|
|
125
|
+
async function runCaseBatched<T>(
|
|
126
|
+
variantArgs: VariantArgs,
|
|
127
|
+
baselineArgs: VariantArgs | undefined,
|
|
128
|
+
ctx: DirMatrixContext<T>,
|
|
129
|
+
): Promise<{ measured: MeasuredResults; baseline?: MeasuredResults }> {
|
|
130
|
+
const runCurrent = async () => (await runMatrixVariant(variantArgs))[0];
|
|
131
|
+
const runBase = baselineArgs
|
|
132
|
+
? async () => (await runMatrixVariant(baselineArgs))[0]
|
|
133
|
+
: undefined;
|
|
134
|
+
const { results, baseline } = await runBatched(
|
|
135
|
+
[runCurrent],
|
|
136
|
+
runBase,
|
|
137
|
+
ctx.batches,
|
|
138
|
+
ctx.warmupBatch,
|
|
139
|
+
);
|
|
140
|
+
return { measured: results[0], baseline };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Run a single unbatched measurement for a case. */
|
|
144
|
+
async function runCaseSingle(
|
|
145
|
+
variantArgs: VariantArgs,
|
|
146
|
+
baselineArgs: VariantArgs | undefined,
|
|
147
|
+
): Promise<{ measured: MeasuredResults; baseline?: MeasuredResults }> {
|
|
148
|
+
const [measured] = await runMatrixVariant(variantArgs);
|
|
149
|
+
const baseline = baselineArgs
|
|
150
|
+
? (await runMatrixVariant(baselineArgs))[0]
|
|
151
|
+
: undefined;
|
|
152
|
+
return { measured, baseline };
|
|
153
|
+
}
|
|
@@ -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
|
|
|
@@ -8,24 +8,21 @@ export interface MatrixFilter {
|
|
|
8
8
|
variant?: string;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/** Filtered matrix with explicit case and variant lists */
|
|
12
|
+
export interface FilteredMatrix<T = unknown> extends BenchMatrix<T> {
|
|
13
|
+
filteredCases?: string[];
|
|
14
|
+
filteredVariants?: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
11
17
|
/** Parse filter string: "case/variant", "case/", "/variant", or "case" */
|
|
12
18
|
export function parseMatrixFilter(filter: string): MatrixFilter {
|
|
13
19
|
if (filter.includes("/")) {
|
|
14
|
-
const [casePart,
|
|
15
|
-
return {
|
|
16
|
-
case: casePart || undefined,
|
|
17
|
-
variant: variantPart || undefined,
|
|
18
|
-
};
|
|
20
|
+
const [casePart, varPart] = filter.split("/", 2);
|
|
21
|
+
return { case: casePart || undefined, variant: varPart || undefined };
|
|
19
22
|
}
|
|
20
23
|
return { case: filter };
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
/** Filtered matrix with explicit case and variant lists */
|
|
24
|
-
export interface FilteredMatrix<T = unknown> extends BenchMatrix<T> {
|
|
25
|
-
filteredCases?: string[];
|
|
26
|
-
filteredVariants?: string[];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
26
|
/** Apply filter to a matrix, merging with existing filters via intersection */
|
|
30
27
|
export async function filterMatrix<T>(
|
|
31
28
|
matrix: FilteredMatrix<T>,
|
|
@@ -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
|
+
}
|