benchforge 0.1.9 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +99 -260
- package/bin/benchforge +1 -2
- package/dist/AnalyzeArchive-8NCJhmhS.mjs +145 -0
- package/dist/AnalyzeArchive-8NCJhmhS.mjs.map +1 -0
- package/dist/BenchMatrix-BZVrBB_h.mjs +1050 -0
- package/dist/BenchMatrix-BZVrBB_h.mjs.map +1 -0
- package/dist/BenchRunner-DglX1NOn.d.mts +302 -0
- package/dist/CoverageSampler-D5T9DRqe.mjs +27 -0
- package/dist/CoverageSampler-D5T9DRqe.mjs.map +1 -0
- package/dist/Formatters-BWj3d4sv.mjs +95 -0
- package/dist/Formatters-BWj3d4sv.mjs.map +1 -0
- package/dist/{HeapSampler-B8dtKHn1.mjs → HeapSampler-Dq-hpXem.mjs} +4 -4
- package/dist/HeapSampler-Dq-hpXem.mjs.map +1 -0
- package/dist/RunBenchCLI-C17DrJz8.mjs +3075 -0
- package/dist/RunBenchCLI-C17DrJz8.mjs.map +1 -0
- package/dist/StatisticalUtils-BD92crgM.mjs +255 -0
- package/dist/StatisticalUtils-BD92crgM.mjs.map +1 -0
- package/dist/TimeSampler-Ds8n7l2B.mjs +29 -0
- package/dist/TimeSampler-Ds8n7l2B.mjs.map +1 -0
- package/dist/ViewerServer-BJhdnxlN.mjs +639 -0
- package/dist/ViewerServer-BJhdnxlN.mjs.map +1 -0
- package/dist/ViewerServer-CuMNdNBz.mjs +2 -0
- package/dist/bin/benchforge.mjs +4 -5
- package/dist/bin/benchforge.mjs.map +1 -1
- package/dist/index.d.mts +731 -522
- package/dist/index.mjs +98 -3
- package/dist/index.mjs.map +1 -0
- package/dist/runners/WorkerScript.d.mts +12 -4
- package/dist/runners/WorkerScript.mjs +92 -120
- package/dist/runners/WorkerScript.mjs.map +1 -1
- package/dist/viewer/assets/CIPlot-BkOvMoMa.js +1 -0
- package/dist/viewer/assets/HistogramKde-CmSyUFY0.js +1 -0
- package/dist/viewer/assets/LegendUtils-BJpbn_jr.js +55 -0
- package/dist/viewer/assets/SampleTimeSeries-C4VBhXr3.js +1 -0
- package/dist/viewer/assets/index-Br9bp_cX.js +153 -0
- package/dist/viewer/assets/index-NzXXe_CC.css +1 -0
- package/dist/viewer/index.html +19 -0
- package/dist/viewer/speedscope/LICENSE +21 -0
- package/dist/viewer/speedscope/SourceCodePro-Regular.ttf-ILST5JV6.woff2 +0 -0
- package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js +2 -0
- package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js.map +7 -0
- package/dist/viewer/speedscope/favicon-16x16-VSI62OPJ.png +0 -0
- package/dist/viewer/speedscope/favicon-32x32-3EB2YCUY.png +0 -0
- package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js +2 -0
- package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js.map +7 -0
- package/dist/viewer/speedscope/favicon-FOKUP5Y5.ico +0 -0
- package/dist/viewer/speedscope/favicon-M34RF7BI.js +2 -0
- package/dist/viewer/speedscope/favicon-M34RF7BI.js.map +7 -0
- package/dist/viewer/speedscope/file-format-schema.json +274 -0
- package/dist/viewer/speedscope/index.html +19 -0
- package/dist/viewer/speedscope/jfrview_bg-BLJXNNQB.wasm +0 -0
- package/dist/viewer/speedscope/perf-vertx-stacks-01-collapsed-all-ZNUIGAJL.txt +199 -0
- package/dist/viewer/speedscope/release.txt +3 -0
- package/dist/viewer/speedscope/source-code-pro.LICENSE.md +93 -0
- package/dist/viewer/speedscope/speedscope-GHPHNKXC.css +2 -0
- package/dist/viewer/speedscope/speedscope-GHPHNKXC.css.map +7 -0
- package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js +212 -0
- package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js.map +7 -0
- package/package.json +52 -26
- package/src/bin/benchforge.ts +2 -2
- package/src/cli/AnalyzeArchive.ts +232 -0
- package/src/cli/BrowserBench.ts +322 -0
- package/src/cli/CliArgs.ts +164 -48
- package/src/cli/CliExport.ts +179 -0
- package/src/cli/CliOptions.ts +147 -0
- package/src/cli/CliReport.ts +197 -0
- package/src/cli/FilterBenchmarks.ts +18 -30
- package/src/cli/RunBenchCLI.ts +138 -844
- package/src/cli/SuiteRunner.ts +160 -0
- package/src/cli/ViewerServer.ts +282 -0
- package/src/export/AllocExport.ts +121 -0
- package/src/export/ArchiveExport.ts +146 -0
- package/src/export/ArchiveFormat.ts +50 -0
- package/src/export/CoverageExport.ts +148 -0
- package/src/export/EditorUri.ts +10 -0
- package/src/export/PerfettoExport.ts +91 -126
- package/src/export/SpeedscopeTypes.ts +98 -0
- package/src/export/TimeExport.ts +115 -0
- package/src/index.ts +87 -62
- package/src/matrix/BenchMatrix.ts +230 -0
- package/src/matrix/CaseLoader.ts +8 -6
- package/src/matrix/MatrixDirRunner.ts +153 -0
- package/src/matrix/MatrixFilter.ts +55 -53
- package/src/matrix/MatrixInlineRunner.ts +50 -0
- package/src/matrix/MatrixReport.ts +94 -254
- package/src/matrix/VariantLoader.ts +9 -9
- package/src/profiling/browser/BenchLoop.ts +51 -0
- package/src/profiling/browser/BrowserCDP.ts +133 -0
- package/src/profiling/browser/BrowserGcStats.ts +33 -0
- package/src/profiling/browser/BrowserProfiler.ts +160 -0
- package/src/profiling/browser/CdpClient.ts +82 -0
- package/src/profiling/browser/CdpPage.ts +138 -0
- package/src/profiling/browser/ChromeLauncher.ts +158 -0
- package/src/profiling/browser/ChromeTraceEvent.ts +28 -0
- package/src/profiling/browser/PageLoadMode.ts +61 -0
- package/src/profiling/node/CoverageSampler.ts +27 -0
- package/src/profiling/node/CoverageTypes.ts +23 -0
- package/src/profiling/node/HeapSampleReport.ts +261 -0
- package/src/{heap-sample → profiling/node}/HeapSampler.ts +55 -13
- package/src/profiling/node/ResolvedProfile.ts +98 -0
- package/src/profiling/node/TimeSampler.ts +57 -0
- package/src/report/BenchmarkReport.ts +146 -0
- package/src/report/Colors.ts +9 -0
- package/src/report/Formatters.ts +110 -0
- package/src/report/GcSections.ts +151 -0
- package/src/{GitUtils.ts → report/GitUtils.ts} +18 -19
- package/src/report/HtmlReport.ts +223 -0
- package/src/report/ParseStats.ts +73 -0
- package/src/report/StandardSections.ts +147 -0
- package/src/report/ViewerSections.ts +286 -0
- package/src/report/text/TableReport.ts +253 -0
- package/src/report/text/TextReport.ts +123 -0
- package/src/runners/AdaptiveWrapper.ts +167 -287
- package/src/runners/BenchRunner.ts +27 -22
- package/src/{Benchmark.ts → runners/BenchmarkSpec.ts} +5 -6
- package/src/runners/CreateRunner.ts +5 -7
- package/src/runners/GcStats.ts +58 -61
- package/src/{MeasuredResults.ts → runners/MeasuredResults.ts} +43 -37
- package/src/runners/MergeBatches.ts +123 -0
- package/src/{NodeGC.ts → runners/NodeGC.ts} +2 -3
- package/src/runners/RunnerOrchestrator.ts +180 -296
- package/src/runners/RunnerUtils.ts +75 -1
- package/src/runners/SampleStats.ts +100 -0
- package/src/runners/TimingRunner.ts +244 -0
- package/src/runners/TimingUtils.ts +3 -2
- package/src/runners/WorkerScript.ts +162 -178
- package/src/stats/BootstrapDifference.ts +282 -0
- package/src/{PermutationTest.ts → stats/PermutationTest.ts} +31 -40
- package/src/stats/StatisticalUtils.ts +445 -0
- package/src/{tests → test}/AdaptiveConvergence.test.ts +10 -10
- package/src/test/AdaptiveRunner.test.ts +39 -41
- package/src/{tests → test}/AdaptiveSampling.test.ts +9 -9
- package/src/test/AdaptiveStatistics.integration.ts +9 -41
- package/src/{tests → test}/BenchMatrix.test.ts +31 -28
- package/src/test/BenchmarkReport.test.ts +63 -13
- package/src/test/BrowserBench.e2e.test.ts +186 -17
- package/src/test/BrowserBench.test.ts +10 -5
- package/src/test/BuildTimeSection.test.ts +130 -0
- package/src/test/CapSamples.test.ts +82 -0
- package/src/test/CoverageExport.test.ts +115 -0
- package/src/test/CoverageSampler.test.ts +33 -0
- package/src/test/HeapAttribution.test.ts +51 -0
- package/src/{tests → test}/MatrixFilter.test.ts +16 -16
- package/src/{tests → test}/MatrixReport.test.ts +1 -1
- package/src/test/PermutationTest.test.ts +1 -1
- package/src/{tests → test}/RealDataValidation.test.ts +6 -6
- package/src/test/RunBenchCLI.test.ts +57 -56
- package/src/test/RunnerOrchestrator.test.ts +12 -12
- package/src/test/StatisticalUtils.test.ts +48 -12
- package/src/{table-util/test → test}/TableReport.test.ts +2 -2
- package/src/test/TestUtils.ts +35 -30
- package/src/test/TimeExport.test.ts +139 -0
- package/src/test/TimeSampler.test.ts +37 -0
- package/src/test/ViewerLive.e2e.test.ts +159 -0
- package/src/test/ViewerStatic.static.e2e.test.ts +137 -0
- package/src/{tests → test}/fixtures/baseline/impl.ts +1 -1
- package/src/{tests → test}/fixtures/bevy30-samples.ts +3 -1
- package/src/test/fixtures/cases/asyncCases.ts +9 -0
- package/src/{tests → test}/fixtures/cases/cases.ts +5 -2
- package/src/test/fixtures/cases/variants/product.ts +2 -0
- package/src/test/fixtures/cases/variants/sum.ts +2 -0
- package/src/test/fixtures/discover/fast.ts +1 -0
- package/src/{tests → test}/fixtures/discover/slow.ts +1 -1
- package/src/test/fixtures/invalid/bad.ts +1 -0
- package/src/test/fixtures/loader/fast.ts +1 -0
- package/src/{tests → test}/fixtures/loader/slow.ts +1 -1
- package/src/test/fixtures/loader/stateful.ts +2 -0
- package/src/test/fixtures/stateful/stateful.ts +2 -0
- package/src/test/fixtures/variants/extra.ts +1 -0
- package/src/test/fixtures/variants/impl.ts +1 -0
- package/src/test/fixtures/worker/fast.ts +1 -0
- package/src/{tests → test}/fixtures/worker/slow.ts +1 -1
- package/src/viewer/DateFormat.ts +30 -0
- package/src/viewer/Helpers.ts +23 -0
- package/src/viewer/LineData.ts +120 -0
- package/src/viewer/Providers.ts +191 -0
- package/src/viewer/ReportData.ts +123 -0
- package/src/viewer/State.ts +49 -0
- package/src/viewer/Theme.ts +15 -0
- package/src/viewer/components/App.tsx +73 -0
- package/src/viewer/components/DropZone.tsx +71 -0
- package/src/viewer/components/LazyPlot.ts +33 -0
- package/src/viewer/components/SamplesPanel.tsx +214 -0
- package/src/viewer/components/Shell.tsx +26 -0
- package/src/viewer/components/SourcePanel.tsx +216 -0
- package/src/viewer/components/SummaryPanel.tsx +332 -0
- package/src/viewer/components/TabBar.tsx +131 -0
- package/src/viewer/components/TabContent.tsx +46 -0
- package/src/viewer/components/ThemeToggle.tsx +50 -0
- package/src/viewer/index.html +20 -0
- package/src/viewer/main.tsx +4 -0
- package/src/viewer/plots/CIPlot.ts +313 -0
- package/src/{html/browser → viewer/plots}/HistogramKde.ts +42 -47
- package/src/viewer/plots/LegendUtils.ts +134 -0
- package/src/viewer/plots/PlotTypes.ts +85 -0
- package/src/viewer/plots/RenderPlots.ts +230 -0
- package/src/viewer/plots/SampleTimeSeries.ts +306 -0
- package/src/viewer/plots/SvgHelpers.ts +136 -0
- package/src/viewer/plots/TimeSeriesMarks.ts +319 -0
- package/src/viewer/report.css +427 -0
- package/src/viewer/shell.css +357 -0
- package/src/viewer/tsconfig.json +11 -0
- package/dist/BenchRunner-CSKN9zPy.d.mts +0 -225
- package/dist/BrowserHeapSampler-DCeL42RE.mjs +0 -202
- package/dist/BrowserHeapSampler-DCeL42RE.mjs.map +0 -1
- package/dist/GcStats-ByEovUi1.mjs +0 -77
- package/dist/GcStats-ByEovUi1.mjs.map +0 -1
- package/dist/HeapSampler-B8dtKHn1.mjs.map +0 -1
- package/dist/TimingUtils-ClclVQ7E.mjs +0 -597
- package/dist/TimingUtils-ClclVQ7E.mjs.map +0 -1
- package/dist/browser/index.js +0 -914
- package/dist/src-Cf_LXwlp.mjs +0 -2873
- package/dist/src-Cf_LXwlp.mjs.map +0 -1
- package/src/BenchMatrix.ts +0 -380
- package/src/BenchmarkReport.ts +0 -156
- package/src/HtmlDataPrep.ts +0 -148
- package/src/StandardSections.ts +0 -261
- package/src/StatisticalUtils.ts +0 -176
- package/src/TypeUtil.ts +0 -8
- package/src/browser/BrowserGcStats.ts +0 -44
- package/src/browser/BrowserHeapSampler.ts +0 -271
- package/src/export/JsonExport.ts +0 -103
- package/src/export/JsonFormat.ts +0 -91
- package/src/heap-sample/HeapSampleReport.ts +0 -196
- package/src/html/HtmlReport.ts +0 -131
- package/src/html/HtmlTemplate.ts +0 -284
- package/src/html/Types.ts +0 -88
- package/src/html/browser/CIPlot.ts +0 -287
- package/src/html/browser/LegendUtils.ts +0 -163
- package/src/html/browser/RenderPlots.ts +0 -263
- package/src/html/browser/SampleTimeSeries.ts +0 -389
- package/src/html/browser/Types.ts +0 -96
- package/src/html/browser/index.ts +0 -1
- package/src/html/index.ts +0 -17
- package/src/runners/BasicRunner.ts +0 -364
- package/src/table-util/ConvergenceFormatters.ts +0 -19
- package/src/table-util/Formatters.ts +0 -152
- package/src/table-util/README.md +0 -70
- package/src/table-util/TableReport.ts +0 -293
- package/src/tests/fixtures/cases/asyncCases.ts +0 -7
- package/src/tests/fixtures/cases/variants/product.ts +0 -2
- package/src/tests/fixtures/cases/variants/sum.ts +0 -2
- package/src/tests/fixtures/discover/fast.ts +0 -1
- package/src/tests/fixtures/invalid/bad.ts +0 -1
- package/src/tests/fixtures/loader/fast.ts +0 -1
- package/src/tests/fixtures/loader/stateful.ts +0 -2
- package/src/tests/fixtures/stateful/stateful.ts +0 -2
- package/src/tests/fixtures/variants/extra.ts +0 -1
- package/src/tests/fixtures/variants/impl.ts +0 -1
- package/src/tests/fixtures/worker/fast.ts +0 -1
- package/src/{table-util/test → test}/TableValueExtractor.test.ts +0 -0
- package/src/{table-util/test → test}/TableValueExtractor.ts +9 -9
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,99 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import {
|
|
1
|
+
import { A as gcStatsSection, C as buildTimeSection, D as totalTimeSection, E as timeSection, M as browserCliArgs, N as defaultCliArgs, O as gcSection, P as parseCliArgs, S as buildGenericSections, T as runsSection, _ as reportMatrixResults, a as runDefaultBench, b as prepareHtmlData, c as runBenchmarks, d as exportReports, f as defaultMatrixReport, g as reportOptStatus, h as printHeapReports, i as parseBenchArgs, j as exportPerfettoTrace, k as gcSections, l as filterMatrix, m as matrixToReportGroups, n as dispatchCli, o as runDefaultMatrixBench, p as defaultReport, r as matrixBenchExports, s as runMatrixSuite, t as benchExports, u as parseMatrixFilter, v as reportResults, w as optSection, x as adaptiveSections, y as cliToMatrixOptions } from "./RunBenchCLI-C17DrJz8.mjs";
|
|
2
|
+
import { a as archiveBenchmark, c as exportSpeedscope, l as heapProfileToSpeedscope, p as computeColumnValues, s as buildSpeedscopeFile, v as hasField } from "./ViewerServer-BJhdnxlN.mjs";
|
|
3
|
+
import { d as maxBootstrapInput, g as percentile, o as computeStat, p as median, t as average, u as isBootstrappable } from "./StatisticalUtils-BD92crgM.mjs";
|
|
4
|
+
import { c as timeMs, l as truncate, n as formatBytes, o as integer, r as formatConvergence } from "./Formatters-BWj3d4sv.mjs";
|
|
5
|
+
import { h as loadCasesModule, m as loadCaseData, n as runMatrix, t as isStatefulVariant } from "./BenchMatrix-BZVrBB_h.mjs";
|
|
6
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { execSync } from "node:child_process";
|
|
9
|
+
//#region src/viewer/DateFormat.ts
|
|
10
|
+
/** Format ISO date as local time with UTC: "Jan 9, 2026, 3:45 PM (2026-01-09T23:45:00Z)" */
|
|
11
|
+
function formatDateWithTimezone(isoDate) {
|
|
12
|
+
const date = new Date(isoDate);
|
|
13
|
+
return `${date.toLocaleString("en-US", {
|
|
14
|
+
month: "short",
|
|
15
|
+
day: "numeric",
|
|
16
|
+
year: "numeric",
|
|
17
|
+
hour: "numeric",
|
|
18
|
+
minute: "2-digit"
|
|
19
|
+
})} (${date.toISOString().replace(".000Z", "Z")})`;
|
|
20
|
+
}
|
|
21
|
+
/** Format relative time: "5m ago", "2h ago", "yesterday", "3 days ago" */
|
|
22
|
+
function formatRelativeTime(isoDate) {
|
|
23
|
+
const date = new Date(isoDate);
|
|
24
|
+
const diffMs = (/* @__PURE__ */ new Date()).getTime() - date.getTime();
|
|
25
|
+
const diffMins = Math.floor(diffMs / 6e4);
|
|
26
|
+
const diffHours = Math.floor(diffMs / 36e5);
|
|
27
|
+
const diffDays = Math.floor(diffMs / 864e5);
|
|
28
|
+
if (diffMins < 1) return "just now";
|
|
29
|
+
if (diffMins < 60) return `${diffMins}m ago`;
|
|
30
|
+
if (diffHours < 24) return `${diffHours}h ago`;
|
|
31
|
+
if (diffDays === 1) return "yesterday";
|
|
32
|
+
if (diffDays < 30) return `${diffDays} days ago`;
|
|
33
|
+
return date.toLocaleDateString("en-US", {
|
|
34
|
+
month: "short",
|
|
35
|
+
day: "numeric"
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/report/GitUtils.ts
|
|
40
|
+
/** Get current git version info. For dirty repos, uses most recent modified file date. */
|
|
41
|
+
function getCurrentGitVersion() {
|
|
42
|
+
try {
|
|
43
|
+
const exec = (cmd) => execSync(cmd, { encoding: "utf-8" }).trim();
|
|
44
|
+
const hash = exec("git rev-parse --short HEAD");
|
|
45
|
+
const commitDate = exec("git log -1 --format=%aI");
|
|
46
|
+
const dirty = exec("git status --porcelain").length > 0;
|
|
47
|
+
return {
|
|
48
|
+
hash,
|
|
49
|
+
date: dirty ? getMostRecentModifiedDate(".") ?? commitDate : commitDate,
|
|
50
|
+
dirty
|
|
51
|
+
};
|
|
52
|
+
} catch {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Read baseline version from .baseline-version file */
|
|
57
|
+
function getBaselineVersion(baselineDir = "_baseline") {
|
|
58
|
+
const versionFile = join(baselineDir, ".baseline-version");
|
|
59
|
+
if (!existsSync(versionFile)) return void 0;
|
|
60
|
+
try {
|
|
61
|
+
const content = readFileSync(versionFile, "utf-8");
|
|
62
|
+
const data = JSON.parse(content);
|
|
63
|
+
return {
|
|
64
|
+
hash: data.hash,
|
|
65
|
+
date: data.date
|
|
66
|
+
};
|
|
67
|
+
} catch {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Format git version for display: "abc1234 (Jan 9, 2026, 3:45 PM)" or "abc1234*" if dirty */
|
|
72
|
+
function formatGitVersion(version) {
|
|
73
|
+
return `${version.dirty ? `${version.hash}*` : version.hash} (${formatDateWithTimezone(version.date)})`;
|
|
74
|
+
}
|
|
75
|
+
/** Get most recent modified file date in a directory (for dirty repos) */
|
|
76
|
+
function getMostRecentModifiedDate(dir) {
|
|
77
|
+
try {
|
|
78
|
+
const files = execSync("git status --porcelain", {
|
|
79
|
+
encoding: "utf-8",
|
|
80
|
+
cwd: dir
|
|
81
|
+
}).trim().split("\n").filter(Boolean).map((l) => l.slice(3));
|
|
82
|
+
if (!files.length) return void 0;
|
|
83
|
+
const mtime = (f) => {
|
|
84
|
+
try {
|
|
85
|
+
return statSync(join(dir, f)).mtimeMs;
|
|
86
|
+
} catch {
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const mostRecent = Math.max(0, ...files.map(mtime));
|
|
91
|
+
return mostRecent > 0 ? new Date(mostRecent).toISOString() : void 0;
|
|
92
|
+
} catch {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
export { adaptiveSections, archiveBenchmark, average, benchExports, browserCliArgs, buildGenericSections, buildSpeedscopeFile, buildTimeSection, cliToMatrixOptions, computeColumnValues, computeStat, defaultCliArgs, defaultMatrixReport, defaultReport, dispatchCli, exportPerfettoTrace, exportReports, exportSpeedscope, filterMatrix, formatBytes, formatConvergence, formatDateWithTimezone, formatGitVersion, formatRelativeTime, gcSection, gcSections, gcStatsSection, getBaselineVersion, getCurrentGitVersion, hasField, heapProfileToSpeedscope, integer, isBootstrappable, isStatefulVariant, loadCaseData, loadCasesModule, matrixBenchExports, matrixToReportGroups, maxBootstrapInput, median, optSection, parseBenchArgs, parseCliArgs, parseMatrixFilter, percentile, prepareHtmlData, printHeapReports, reportMatrixResults, reportOptStatus, reportResults, runBenchmarks, runDefaultBench, runDefaultMatrixBench, runMatrix, runMatrixSuite, runsSection, timeMs, timeSection, totalTimeSection, truncate };
|
|
3
98
|
|
|
4
|
-
|
|
99
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/viewer/DateFormat.ts","../src/report/GitUtils.ts"],"sourcesContent":["/** Format ISO date as local time with UTC: \"Jan 9, 2026, 3:45 PM (2026-01-09T23:45:00Z)\" */\nexport function formatDateWithTimezone(isoDate: string): string {\n const date = new Date(isoDate);\n const local = date.toLocaleString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n const utc = date.toISOString().replace(\".000Z\", \"Z\");\n return `${local} (${utc})`;\n}\n\n/** Format relative time: \"5m ago\", \"2h ago\", \"yesterday\", \"3 days ago\" */\nexport function formatRelativeTime(isoDate: string): string {\n const date = new Date(isoDate);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n const diffHours = Math.floor(diffMs / 3600000);\n const diffDays = Math.floor(diffMs / 86400000);\n\n if (diffMins < 1) return \"just now\";\n if (diffMins < 60) return `${diffMins}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n if (diffDays === 1) return \"yesterday\";\n if (diffDays < 30) return `${diffDays} days ago`;\n return date.toLocaleDateString(\"en-US\", { month: \"short\", day: \"numeric\" });\n}\n","import { execSync } from \"node:child_process\";\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { formatDateWithTimezone } from \"../viewer/DateFormat.ts\";\n\n/** Git commit hash, date, and dirty status for version tracking */\nexport interface GitVersion {\n hash: string;\n date: string;\n dirty?: boolean;\n}\n\n/** Get current git version info. For dirty repos, uses most recent modified file date. */\nexport function getCurrentGitVersion(): GitVersion | undefined {\n try {\n const exec = (cmd: string) => execSync(cmd, { encoding: \"utf-8\" }).trim();\n const hash = exec(\"git rev-parse --short HEAD\");\n const commitDate = exec(\"git log -1 --format=%aI\");\n const dirty = exec(\"git status --porcelain\").length > 0;\n const date = dirty\n ? (getMostRecentModifiedDate(\".\") ?? commitDate)\n : commitDate;\n return { hash, date, dirty };\n } catch {\n return undefined;\n }\n}\n\n/** Read baseline version from .baseline-version file */\nexport function getBaselineVersion(\n baselineDir = \"_baseline\",\n): GitVersion | undefined {\n const versionFile = join(baselineDir, \".baseline-version\");\n if (!existsSync(versionFile)) return undefined;\n\n try {\n const content = readFileSync(versionFile, \"utf-8\");\n const data = JSON.parse(content);\n return { hash: data.hash, date: data.date };\n } catch {\n return undefined;\n }\n}\n\n/** Format git version for display: \"abc1234 (Jan 9, 2026, 3:45 PM)\" or \"abc1234*\" if dirty */\nexport function formatGitVersion(version: GitVersion): string {\n const hashDisplay = version.dirty ? `${version.hash}*` : version.hash;\n const dateDisplay = formatDateWithTimezone(version.date);\n return `${hashDisplay} (${dateDisplay})`;\n}\n\n/** Get most recent modified file date in a directory (for dirty repos) */\nexport function getMostRecentModifiedDate(dir: string): string | undefined {\n try {\n const raw = execSync(\"git status --porcelain\", {\n encoding: \"utf-8\",\n cwd: dir,\n });\n const files = raw\n .trim()\n .split(\"\\n\")\n .filter(Boolean)\n .map(l => l.slice(3));\n if (!files.length) return undefined;\n\n const mtime = (f: string): number => {\n try {\n return statSync(join(dir, f)).mtimeMs;\n } catch {\n return 0;\n }\n };\n const mostRecent = Math.max(0, ...files.map(mtime));\n return mostRecent > 0 ? new Date(mostRecent).toISOString() : undefined;\n } catch {\n return undefined;\n }\n}\n"],"mappings":";;;;;;;;;;AACA,SAAgB,uBAAuB,SAAyB;CAC9D,MAAM,OAAO,IAAI,KAAK,QAAQ;AAS9B,QAAO,GARO,KAAK,eAAe,SAAS;EACzC,OAAO;EACP,KAAK;EACL,MAAM;EACN,MAAM;EACN,QAAQ;EACT,CAAC,CAEc,IADJ,KAAK,aAAa,CAAC,QAAQ,SAAS,IAAI,CAC5B;;;AAI1B,SAAgB,mBAAmB,SAAyB;CAC1D,MAAM,OAAO,IAAI,KAAK,QAAQ;CAE9B,MAAM,0BADM,IAAI,MAAM,EACH,SAAS,GAAG,KAAK,SAAS;CAC7C,MAAM,WAAW,KAAK,MAAM,SAAS,IAAM;CAC3C,MAAM,YAAY,KAAK,MAAM,SAAS,KAAQ;CAC9C,MAAM,WAAW,KAAK,MAAM,SAAS,MAAS;AAE9C,KAAI,WAAW,EAAG,QAAO;AACzB,KAAI,WAAW,GAAI,QAAO,GAAG,SAAS;AACtC,KAAI,YAAY,GAAI,QAAO,GAAG,UAAU;AACxC,KAAI,aAAa,EAAG,QAAO;AAC3B,KAAI,WAAW,GAAI,QAAO,GAAG,SAAS;AACtC,QAAO,KAAK,mBAAmB,SAAS;EAAE,OAAO;EAAS,KAAK;EAAW,CAAC;;;;;ACf7E,SAAgB,uBAA+C;AAC7D,KAAI;EACF,MAAM,QAAQ,QAAgB,SAAS,KAAK,EAAE,UAAU,SAAS,CAAC,CAAC,MAAM;EACzE,MAAM,OAAO,KAAK,6BAA6B;EAC/C,MAAM,aAAa,KAAK,0BAA0B;EAClD,MAAM,QAAQ,KAAK,yBAAyB,CAAC,SAAS;AAItD,SAAO;GAAE;GAAM,MAHF,QACR,0BAA0B,IAAI,IAAI,aACnC;GACiB;GAAO;SACtB;AACN;;;;AAKJ,SAAgB,mBACd,cAAc,aACU;CACxB,MAAM,cAAc,KAAK,aAAa,oBAAoB;AAC1D,KAAI,CAAC,WAAW,YAAY,CAAE,QAAO,KAAA;AAErC,KAAI;EACF,MAAM,UAAU,aAAa,aAAa,QAAQ;EAClD,MAAM,OAAO,KAAK,MAAM,QAAQ;AAChC,SAAO;GAAE,MAAM,KAAK;GAAM,MAAM,KAAK;GAAM;SACrC;AACN;;;;AAKJ,SAAgB,iBAAiB,SAA6B;AAG5D,QAAO,GAFa,QAAQ,QAAQ,GAAG,QAAQ,KAAK,KAAK,QAAQ,KAE3C,IADF,uBAAuB,QAAQ,KAAK,CAClB;;;AAIxC,SAAgB,0BAA0B,KAAiC;AACzE,KAAI;EAKF,MAAM,QAJM,SAAS,0BAA0B;GAC7C,UAAU;GACV,KAAK;GACN,CAAC,CAEC,MAAM,CACN,MAAM,KAAK,CACX,OAAO,QAAQ,CACf,KAAI,MAAK,EAAE,MAAM,EAAE,CAAC;AACvB,MAAI,CAAC,MAAM,OAAQ,QAAO,KAAA;EAE1B,MAAM,SAAS,MAAsB;AACnC,OAAI;AACF,WAAO,SAAS,KAAK,KAAK,EAAE,CAAC,CAAC;WACxB;AACN,WAAO;;;EAGX,MAAM,aAAa,KAAK,IAAI,GAAG,GAAG,MAAM,IAAI,MAAM,CAAC;AACnD,SAAO,aAAa,IAAI,IAAI,KAAK,WAAW,CAAC,aAAa,GAAG,KAAA;SACvD;AACN"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as MeasuredResults, i as BenchmarkSpec,
|
|
1
|
+
import { a as MeasuredResults, c as HeapProfile, i as BenchmarkSpec, l as CoverageData, s as TimeProfile, t as RunnerOptions } from "../BenchRunner-DglX1NOn.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/runners/CreateRunner.d.ts
|
|
4
|
-
type KnownRunner = "
|
|
4
|
+
type KnownRunner = "timing";
|
|
5
5
|
//#endregion
|
|
6
6
|
//#region src/runners/WorkerScript.d.ts
|
|
7
7
|
/** Message sent to worker process to start a benchmark run. */
|
|
@@ -10,24 +10,32 @@ interface RunMessage {
|
|
|
10
10
|
spec: BenchmarkSpec;
|
|
11
11
|
runnerName: KnownRunner;
|
|
12
12
|
options: RunnerOptions;
|
|
13
|
+
/** Serialized function body (mutually exclusive with modulePath) */
|
|
13
14
|
fnCode?: string;
|
|
14
15
|
modulePath?: string;
|
|
16
|
+
/** Defaults to default export */
|
|
15
17
|
exportName?: string;
|
|
18
|
+
/** Called once before benchmarking; result passed as params to fn */
|
|
16
19
|
setupExportName?: string;
|
|
17
20
|
params?: unknown;
|
|
21
|
+
/** Directory URL containing variant .ts files (BenchMatrix mode) */
|
|
18
22
|
variantDir?: string;
|
|
23
|
+
/** Variant filename without .ts extension */
|
|
19
24
|
variantId?: string;
|
|
20
25
|
caseData?: unknown;
|
|
21
26
|
caseId?: string;
|
|
27
|
+
/** Module URL exporting cases[] and loadCase() */
|
|
22
28
|
casesModule?: string;
|
|
23
29
|
}
|
|
24
|
-
/**
|
|
30
|
+
/** Benchmark results returned from worker process. */
|
|
25
31
|
interface ResultMessage {
|
|
26
32
|
type: "result";
|
|
27
33
|
results: MeasuredResults[];
|
|
28
34
|
heapProfile?: HeapProfile;
|
|
35
|
+
timeProfile?: TimeProfile;
|
|
36
|
+
coverage?: CoverageData;
|
|
29
37
|
}
|
|
30
|
-
/**
|
|
38
|
+
/** Error returned from worker process when benchmark fails. */
|
|
31
39
|
interface ErrorMessage {
|
|
32
40
|
type: "error";
|
|
33
41
|
error: string;
|
|
@@ -1,68 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as
|
|
3
|
-
|
|
2
|
+
import { a as getPerfNow, c as resolveVariantFn, i as getElapsed, o as createBenchRunner, s as importBenchFn } from "../BenchMatrix-BZVrBB_h.mjs";
|
|
4
3
|
//#region src/runners/WorkerScript.ts
|
|
5
4
|
const workerStartTime = getPerfNow();
|
|
6
5
|
const maxLifetime = 300 * 1e3;
|
|
7
|
-
|
|
8
|
-
* Worker process for isolated benchmark execution.
|
|
9
|
-
* Uses eval() safely in isolated child process with trusted code.
|
|
10
|
-
*/
|
|
11
|
-
process.on("message", async (message) => {
|
|
12
|
-
if (message.type !== "run") return;
|
|
13
|
-
logTiming(`Processing ${message.spec.name} with ${message.runnerName}`);
|
|
14
|
-
try {
|
|
15
|
-
const start = getPerfNow();
|
|
16
|
-
const baseRunner = await createRunner(message.runnerName);
|
|
17
|
-
const runner = message.options.adaptive ? createAdaptiveWrapper(baseRunner, message.options) : baseRunner;
|
|
18
|
-
logTiming("Runner created in", getElapsed(start));
|
|
19
|
-
const benchStart = getPerfNow();
|
|
20
|
-
if (message.options.heapSample) {
|
|
21
|
-
const { withHeapSampling } = await import("../HeapSampler-B8dtKHn1.mjs");
|
|
22
|
-
const { result: results, profile: heapProfile } = await withHeapSampling({
|
|
23
|
-
samplingInterval: message.options.heapInterval,
|
|
24
|
-
stackDepth: message.options.heapDepth
|
|
25
|
-
}, async () => {
|
|
26
|
-
const { fn, params } = await resolveBenchmarkFn(message);
|
|
27
|
-
return runner.runBench({
|
|
28
|
-
...message.spec,
|
|
29
|
-
fn
|
|
30
|
-
}, message.options, params);
|
|
31
|
-
});
|
|
32
|
-
logTiming("Benchmark execution took", getElapsed(benchStart));
|
|
33
|
-
sendAndExit({
|
|
34
|
-
type: "result",
|
|
35
|
-
results,
|
|
36
|
-
heapProfile
|
|
37
|
-
}, 0);
|
|
38
|
-
} else {
|
|
39
|
-
const { fn, params } = await resolveBenchmarkFn(message);
|
|
40
|
-
const results = await runner.runBench({
|
|
41
|
-
...message.spec,
|
|
42
|
-
fn
|
|
43
|
-
}, message.options, params);
|
|
44
|
-
logTiming("Benchmark execution took", getElapsed(benchStart));
|
|
45
|
-
sendAndExit({
|
|
46
|
-
type: "result",
|
|
47
|
-
results
|
|
48
|
-
}, 0);
|
|
49
|
-
}
|
|
50
|
-
} catch (error) {
|
|
51
|
-
sendAndExit(createErrorMessage(error), 1);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
setTimeout(() => {
|
|
55
|
-
console.error("WorkerScript: Maximum lifetime exceeded, exiting");
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}, maxLifetime);
|
|
58
|
-
process.stdin.pause();
|
|
59
|
-
/** Log timing with consistent format */
|
|
60
|
-
const logTiming = debugWorkerTiming ? _logTiming : () => {};
|
|
6
|
+
const logTiming = () => {};
|
|
61
7
|
function _logTiming(operation, duration) {
|
|
62
|
-
|
|
63
|
-
|
|
8
|
+
const suffix = duration !== void 0 ? ` ${duration.toFixed(1)}ms` : "";
|
|
9
|
+
console.log(`[Worker] ${operation}${suffix}`);
|
|
64
10
|
}
|
|
65
|
-
/** Send message
|
|
11
|
+
/** Send IPC message to parent then exit the worker process */
|
|
66
12
|
function sendAndExit(msg, exitCode) {
|
|
67
13
|
process.send(msg, void 0, void 0, (err) => {
|
|
68
14
|
if (err) {
|
|
@@ -76,7 +22,12 @@ function sendAndExit(msg, exitCode) {
|
|
|
76
22
|
/** Resolve benchmark function from message (variant dir, module path, or fnCode) */
|
|
77
23
|
async function resolveBenchmarkFn(message) {
|
|
78
24
|
if (message.variantDir && message.variantId) return importVariantModule(message);
|
|
79
|
-
if (message.modulePath)
|
|
25
|
+
if (message.modulePath) {
|
|
26
|
+
const { modulePath, exportName, setupExportName, params } = message;
|
|
27
|
+
logTiming(`Importing from ${modulePath}${exportName ? ` (${exportName})` : ""}`);
|
|
28
|
+
if (setupExportName) logTiming(`Calling setup: ${setupExportName}`);
|
|
29
|
+
return importBenchFn(modulePath, exportName, setupExportName, params);
|
|
30
|
+
}
|
|
80
31
|
return {
|
|
81
32
|
fn: reconstructFunction(message.fnCode),
|
|
82
33
|
params: message.params
|
|
@@ -84,75 +35,96 @@ async function resolveBenchmarkFn(message) {
|
|
|
84
35
|
}
|
|
85
36
|
/** Import variant from directory and prepare benchmark function */
|
|
86
37
|
async function importVariantModule(message) {
|
|
87
|
-
const { variantDir, variantId
|
|
88
|
-
let { caseData } = message;
|
|
89
|
-
const moduleUrl = variantModuleUrl(variantDir, variantId);
|
|
38
|
+
const { variantDir, variantId } = message;
|
|
90
39
|
logTiming(`Importing variant ${variantId} from ${variantDir}`);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const state = await setup(caseData);
|
|
97
|
-
return {
|
|
98
|
-
fn: () => run(state),
|
|
99
|
-
params: void 0
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
return {
|
|
103
|
-
fn: () => run(caseData),
|
|
104
|
-
params: void 0
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
/** Load case data from a cases module */
|
|
108
|
-
async function loadCaseFromModule(casesModuleUrl, caseId) {
|
|
109
|
-
logTiming(`Loading case '${caseId}' from ${casesModuleUrl}`);
|
|
110
|
-
const module = await import(casesModuleUrl);
|
|
111
|
-
if (typeof module.loadCase === "function") return module.loadCase(caseId);
|
|
112
|
-
return { data: caseId };
|
|
113
|
-
}
|
|
114
|
-
/** Import benchmark function and optionally run setup */
|
|
115
|
-
async function importBenchmarkWithSetup(message) {
|
|
116
|
-
const { modulePath, exportName, setupExportName, params } = message;
|
|
117
|
-
logTiming(`Importing from ${modulePath}${exportName ? ` (${exportName})` : ""}`);
|
|
118
|
-
const module = await import(modulePath);
|
|
119
|
-
const fn = getModuleExport(module, exportName, modulePath);
|
|
120
|
-
if (setupExportName) {
|
|
121
|
-
logTiming(`Calling setup: ${setupExportName}`);
|
|
122
|
-
return {
|
|
123
|
-
fn,
|
|
124
|
-
params: await getModuleExport(module, setupExportName, modulePath)(params)
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
return {
|
|
128
|
-
fn,
|
|
129
|
-
params
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
/** Get named or default export from module */
|
|
133
|
-
function getModuleExport(module, exportName, modulePath) {
|
|
134
|
-
const fn = exportName ? module[exportName] : module.default || module;
|
|
135
|
-
if (typeof fn !== "function") {
|
|
136
|
-
const name = exportName || "default";
|
|
137
|
-
throw new Error(`Export '${name}' from ${modulePath} is not a function`);
|
|
138
|
-
}
|
|
139
|
-
return fn;
|
|
40
|
+
return resolveVariantFn({
|
|
41
|
+
...message,
|
|
42
|
+
variantDir,
|
|
43
|
+
variantId
|
|
44
|
+
});
|
|
140
45
|
}
|
|
141
|
-
/**
|
|
46
|
+
/** Eval serialized function body back into a callable */
|
|
142
47
|
function reconstructFunction(fnCode) {
|
|
143
48
|
const fn = eval(`(${fnCode})`);
|
|
144
49
|
if (typeof fn !== "function") throw new Error("Reconstructed code is not a function");
|
|
145
50
|
return fn;
|
|
146
51
|
}
|
|
147
|
-
/**
|
|
148
|
-
function
|
|
52
|
+
/** Run benchmark with optional heap, time, and coverage profiling */
|
|
53
|
+
async function runWithProfiling(message, runner) {
|
|
54
|
+
const state = {};
|
|
55
|
+
const runBench = buildProfilingChain(message, runner, state);
|
|
56
|
+
if (!message.options.callCounts) return {
|
|
57
|
+
type: "result",
|
|
58
|
+
results: await runBench(),
|
|
59
|
+
...state
|
|
60
|
+
};
|
|
61
|
+
const { withCoverageProfiling } = await import("../CoverageSampler-D5T9DRqe.mjs");
|
|
62
|
+
const r = await withCoverageProfiling(async (session) => {
|
|
63
|
+
state.profilerSession = session;
|
|
64
|
+
return runBench();
|
|
65
|
+
});
|
|
66
|
+
state.coverage = r.coverage;
|
|
149
67
|
return {
|
|
150
|
-
type: "
|
|
151
|
-
|
|
152
|
-
|
|
68
|
+
type: "result",
|
|
69
|
+
results: r.result,
|
|
70
|
+
...state
|
|
153
71
|
};
|
|
154
72
|
}
|
|
155
|
-
|
|
73
|
+
/** Build nested profiling wrappers: outer heap, inner time */
|
|
74
|
+
function buildProfilingChain(message, runner, state) {
|
|
75
|
+
const { alloc, profile, profileInterval, allocInterval, allocDepth } = message.options;
|
|
76
|
+
const run = async () => {
|
|
77
|
+
const { fn, params } = await resolveBenchmarkFn(message);
|
|
78
|
+
return runner.runBench({
|
|
79
|
+
...message.spec,
|
|
80
|
+
fn
|
|
81
|
+
}, message.options, params);
|
|
82
|
+
};
|
|
83
|
+
const runMaybeWithTime = profile ? async () => {
|
|
84
|
+
const { withTimeProfiling } = await import("../TimeSampler-Ds8n7l2B.mjs");
|
|
85
|
+
const r = await withTimeProfiling({
|
|
86
|
+
interval: profileInterval,
|
|
87
|
+
session: state.profilerSession
|
|
88
|
+
}, run);
|
|
89
|
+
state.timeProfile = r.profile;
|
|
90
|
+
return r.result;
|
|
91
|
+
} : run;
|
|
92
|
+
return alloc ? async () => {
|
|
93
|
+
const { withHeapSampling } = await import("../HeapSampler-Dq-hpXem.mjs");
|
|
94
|
+
const r = await withHeapSampling({
|
|
95
|
+
samplingInterval: allocInterval,
|
|
96
|
+
stackDepth: allocDepth
|
|
97
|
+
}, runMaybeWithTime);
|
|
98
|
+
state.heapProfile = r.profile;
|
|
99
|
+
return r.result;
|
|
100
|
+
} : runMaybeWithTime;
|
|
101
|
+
}
|
|
102
|
+
process.on("message", async (message) => {
|
|
103
|
+
if (message.type !== "run") return;
|
|
104
|
+
logTiming(`Processing ${message.spec.name} with ${message.runnerName}`);
|
|
105
|
+
try {
|
|
106
|
+
const start = getPerfNow();
|
|
107
|
+
const runner = await createBenchRunner(message.runnerName, message.options);
|
|
108
|
+
logTiming("Runner created in", getElapsed(start));
|
|
109
|
+
const benchStart = getPerfNow();
|
|
110
|
+
const result = await runWithProfiling(message, runner);
|
|
111
|
+
logTiming("Benchmark execution took", getElapsed(benchStart));
|
|
112
|
+
sendAndExit(result, 0);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
const err = error instanceof Error ? error : void 0;
|
|
115
|
+
sendAndExit({
|
|
116
|
+
type: "error",
|
|
117
|
+
error: err?.message ?? String(error),
|
|
118
|
+
stack: err?.stack
|
|
119
|
+
}, 1);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
setTimeout(() => {
|
|
123
|
+
console.error("WorkerScript: Maximum lifetime exceeded, exiting");
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}, maxLifetime);
|
|
126
|
+
process.stdin.pause();
|
|
156
127
|
//#endregion
|
|
157
|
-
export {
|
|
128
|
+
export {};
|
|
129
|
+
|
|
158
130
|
//# sourceMappingURL=WorkerScript.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkerScript.mjs","names":[],"sources":["../../src/runners/WorkerScript.ts"],"sourcesContent":["#!/usr/bin/env node\nimport type { BenchmarkFunction, BenchmarkSpec } from \"../Benchmark.ts\";\nimport type { HeapProfile } from \"../heap-sample/HeapSampler.ts\";\nimport type { MeasuredResults } from \"../MeasuredResults.ts\";\nimport { variantModuleUrl } from \"../matrix/VariantLoader.ts\";\nimport {\n type AdaptiveOptions,\n createAdaptiveWrapper,\n} from \"./AdaptiveWrapper.ts\";\nimport type { RunnerOptions } from \"./BenchRunner.ts\";\nimport { createRunner, type KnownRunner } from \"./CreateRunner.ts\";\nimport { debugWorkerTiming, getElapsed, getPerfNow } from \"./TimingUtils.ts\";\n\nconst workerStartTime = getPerfNow();\nconst maxLifetime = 5 * 60 * 1000; // 5 minutes\n\n/** Message sent to worker process to start a benchmark run. */\nexport interface RunMessage {\n type: \"run\";\n spec: BenchmarkSpec;\n runnerName: KnownRunner;\n options: RunnerOptions;\n fnCode?: string; // Made optional - either fnCode or modulePath is required\n modulePath?: string; // Path to module for dynamic import\n exportName?: string; // Export name from module\n setupExportName?: string; // Setup function export name - called once, result passed to fn\n params?: unknown;\n // Variant directory mode (BenchMatrix)\n variantDir?: string; // Directory URL containing variant .ts files\n variantId?: string; // Variant filename (without .ts)\n caseData?: unknown; // Data to pass to variant\n caseId?: string; // Case identifier\n casesModule?: string; // URL to cases module (exports cases[] and loadCase())\n}\n\n/** Message returned from worker process with benchmark results. */\nexport interface ResultMessage {\n type: \"result\";\n results: MeasuredResults[];\n heapProfile?: HeapProfile;\n}\n\n/** Message returned from worker process when benchmark fails. */\nexport interface ErrorMessage {\n type: \"error\";\n error: string;\n stack?: string;\n}\n\nexport type WorkerMessage = RunMessage | ResultMessage | ErrorMessage;\n\n/**\n * Worker process for isolated benchmark execution.\n * Uses eval() safely in isolated child process with trusted code.\n */\nprocess.on(\"message\", async (message: RunMessage) => {\n if (message.type !== \"run\") return;\n\n logTiming(`Processing ${message.spec.name} with ${message.runnerName}`);\n\n try {\n const start = getPerfNow();\n const baseRunner = await createRunner(message.runnerName);\n\n const runner = (message.options as any).adaptive\n ? createAdaptiveWrapper(baseRunner, message.options as AdaptiveOptions)\n : baseRunner;\n\n logTiming(\"Runner created in\", getElapsed(start));\n\n const benchStart = getPerfNow();\n\n // Run with heap sampling if enabled (covers module import + execution)\n if (message.options.heapSample) {\n const { withHeapSampling } = await import(\n \"../heap-sample/HeapSampler.ts\"\n );\n const heapOpts = {\n samplingInterval: message.options.heapInterval,\n stackDepth: message.options.heapDepth,\n };\n const { result: results, profile: heapProfile } = await withHeapSampling(\n heapOpts,\n async () => {\n const { fn, params } = await resolveBenchmarkFn(message);\n return runner.runBench(\n { ...message.spec, fn },\n message.options,\n params,\n );\n },\n );\n logTiming(\"Benchmark execution took\", getElapsed(benchStart));\n sendAndExit({ type: \"result\", results, heapProfile }, 0);\n } else {\n const { fn, params } = await resolveBenchmarkFn(message);\n const results = await runner.runBench(\n { ...message.spec, fn },\n message.options,\n params,\n );\n logTiming(\"Benchmark execution took\", getElapsed(benchStart));\n sendAndExit({ type: \"result\", results }, 0);\n }\n } catch (error) {\n sendAndExit(createErrorMessage(error), 1);\n }\n});\n\n// Exit after 5 minutes to prevent zombie processes\nsetTimeout(() => {\n console.error(\"WorkerScript: Maximum lifetime exceeded, exiting\");\n process.exit(1);\n}, maxLifetime);\n\nprocess.stdin.pause();\n\n/** Log timing with consistent format */\nconst logTiming = debugWorkerTiming ? _logTiming : () => {};\nfunction _logTiming(operation: string, duration?: number) {\n if (duration === undefined) {\n console.log(`[Worker] ${operation}`);\n } else {\n console.log(`[Worker] ${operation} ${duration.toFixed(1)}ms`);\n }\n}\n\n/** Send message and exit with duration log */\nfunction sendAndExit(msg: ResultMessage | ErrorMessage, exitCode: number) {\n process.send!(msg, undefined, undefined, (err: Error | null): void => {\n if (err) {\n const kind = msg.type === \"result\" ? \"results\" : \"error message\";\n console.error(`[Worker] Error sending ${kind}:`, err);\n }\n const suffix = exitCode === 0 ? \"\" : \" (error)\";\n logTiming(`Total worker duration${suffix}:`, getElapsed(workerStartTime));\n process.exit(exitCode);\n });\n}\n\ninterface BenchmarkImportResult {\n fn: BenchmarkFunction;\n params: unknown;\n}\n\n/** Resolve benchmark function from message (variant dir, module path, or fnCode) */\nasync function resolveBenchmarkFn(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n if (message.variantDir && message.variantId) {\n return importVariantModule(message);\n }\n if (message.modulePath) {\n return importBenchmarkWithSetup(message);\n }\n return { fn: reconstructFunction(message.fnCode!), params: message.params };\n}\n\n/** Import variant from directory and prepare benchmark function */\nasync function importVariantModule(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n const { variantDir, variantId, caseId, casesModule } = message;\n let { caseData } = message;\n const moduleUrl = variantModuleUrl(variantDir!, variantId!);\n logTiming(`Importing variant ${variantId} from ${variantDir}`);\n\n if (casesModule && caseId) {\n caseData = (await loadCaseFromModule(casesModule, caseId)).data;\n }\n\n const module = await import(moduleUrl);\n const { setup, run } = module;\n\n if (typeof run !== \"function\") {\n throw new Error(`Variant '${variantId}' must export 'run' function`);\n }\n\n // Stateful variant: setup returns state, run receives state\n if (typeof setup === \"function\") {\n logTiming(`Calling setup for ${variantId}`);\n const state = await setup(caseData);\n return { fn: () => run(state), params: undefined };\n }\n\n // Stateless variant: run receives caseData directly\n return { fn: () => run(caseData), params: undefined };\n}\n\n/** Load case data from a cases module */\nasync function loadCaseFromModule(\n casesModuleUrl: string,\n caseId: string,\n): Promise<{ data: unknown; metadata?: Record<string, unknown> }> {\n logTiming(`Loading case '${caseId}' from ${casesModuleUrl}`);\n const module = await import(casesModuleUrl);\n if (typeof module.loadCase === \"function\") {\n return module.loadCase(caseId);\n }\n return { data: caseId };\n}\n\n/** Import benchmark function and optionally run setup */\nasync function importBenchmarkWithSetup(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n const { modulePath, exportName, setupExportName, params } = message;\n logTiming(\n `Importing from ${modulePath}${exportName ? ` (${exportName})` : \"\"}`,\n );\n const module = await import(modulePath!);\n\n const fn = getModuleExport(module, exportName, modulePath!);\n\n if (setupExportName) {\n logTiming(`Calling setup: ${setupExportName}`);\n const setupFn = getModuleExport(module, setupExportName, modulePath!);\n const setupResult = await setupFn(params);\n return { fn, params: setupResult };\n }\n\n return { fn, params };\n}\n\n/** Get named or default export from module */\nfunction getModuleExport(\n module: any,\n exportName: string | undefined,\n modulePath: string,\n): BenchmarkFunction {\n const fn = exportName ? module[exportName] : module.default || module;\n if (typeof fn !== \"function\") {\n const name = exportName || \"default\";\n throw new Error(`Export '${name}' from ${modulePath} is not a function`);\n }\n return fn;\n}\n\n/** Reconstruct function from string code */\nfunction reconstructFunction(fnCode: string): BenchmarkFunction {\n // biome-ignore lint/security/noGlobalEval: Necessary for worker process isolation, code is from trusted source\n const fn = eval(`(${fnCode})`); // eslint-disable-line no-eval\n if (typeof fn !== \"function\") {\n throw new Error(\"Reconstructed code is not a function\");\n }\n return fn;\n}\n\n/** Create error message from exception */\nfunction createErrorMessage(error: unknown): ErrorMessage {\n return {\n type: \"error\",\n error: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WorkerScript.mjs","names":[],"sources":["../../src/runners/WorkerScript.ts"],"sourcesContent":["#!/usr/bin/env node\nimport type { Session } from \"node:inspector/promises\";\nimport type { CoverageData } from \"../profiling/node/CoverageTypes.ts\";\nimport type { HeapProfile } from \"../profiling/node/HeapSampler.ts\";\nimport type { TimeProfile } from \"../profiling/node/TimeSampler.ts\";\nimport type { BenchmarkFunction, BenchmarkSpec } from \"./BenchmarkSpec.ts\";\nimport type { BenchRunner, RunnerOptions } from \"./BenchRunner.ts\";\nimport type { KnownRunner } from \"./CreateRunner.ts\";\nimport type { MeasuredResults } from \"./MeasuredResults.ts\";\nimport {\n createBenchRunner,\n importBenchFn,\n resolveVariantFn,\n} from \"./RunnerUtils.ts\";\nimport { debugWorkerTiming, getElapsed, getPerfNow } from \"./TimingUtils.ts\";\n\n/** Message sent to worker process to start a benchmark run. */\nexport interface RunMessage {\n type: \"run\";\n spec: BenchmarkSpec;\n runnerName: KnownRunner;\n options: RunnerOptions;\n /** Serialized function body (mutually exclusive with modulePath) */\n fnCode?: string;\n modulePath?: string;\n /** Defaults to default export */\n exportName?: string;\n /** Called once before benchmarking; result passed as params to fn */\n setupExportName?: string;\n params?: unknown;\n\n /** Directory URL containing variant .ts files (BenchMatrix mode) */\n variantDir?: string;\n /** Variant filename without .ts extension */\n variantId?: string;\n caseData?: unknown;\n caseId?: string;\n /** Module URL exporting cases[] and loadCase() */\n casesModule?: string;\n}\n\n/** Benchmark results returned from worker process. */\nexport interface ResultMessage {\n type: \"result\";\n results: MeasuredResults[];\n heapProfile?: HeapProfile;\n timeProfile?: TimeProfile;\n coverage?: CoverageData;\n}\n\n/** Error returned from worker process when benchmark fails. */\nexport interface ErrorMessage {\n type: \"error\";\n error: string;\n stack?: string;\n}\n\nexport type WorkerMessage = RunMessage | ResultMessage | ErrorMessage;\n\ninterface BenchmarkImportResult {\n fn: BenchmarkFunction;\n params: unknown;\n}\n\n/** Profiling state accumulated during worker benchmark execution */\ninterface ProfilingState {\n heapProfile?: HeapProfile;\n timeProfile?: TimeProfile;\n coverage?: CoverageData;\n /** Shared session so TimeSampler doesn't reset coverage counters */\n profilerSession?: Session;\n}\n\nconst workerStartTime = getPerfNow();\nconst maxLifetime = 5 * 60 * 1000;\n\nconst logTiming = debugWorkerTiming ? _logTiming : () => {};\nfunction _logTiming(operation: string, duration?: number) {\n const suffix = duration !== undefined ? ` ${duration.toFixed(1)}ms` : \"\";\n console.log(`[Worker] ${operation}${suffix}`);\n}\n\n/** Send IPC message to parent then exit the worker process */\nfunction sendAndExit(msg: ResultMessage | ErrorMessage, exitCode: number) {\n process.send!(msg, undefined, undefined, (err: Error | null): void => {\n if (err) {\n const kind = msg.type === \"result\" ? \"results\" : \"error message\";\n console.error(`[Worker] Error sending ${kind}:`, err);\n }\n const suffix = exitCode === 0 ? \"\" : \" (error)\";\n logTiming(`Total worker duration${suffix}:`, getElapsed(workerStartTime));\n process.exit(exitCode);\n });\n}\n\n/** Resolve benchmark function from message (variant dir, module path, or fnCode) */\nasync function resolveBenchmarkFn(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n if (message.variantDir && message.variantId) {\n return importVariantModule(message);\n }\n if (message.modulePath) {\n const { modulePath, exportName, setupExportName, params } = message;\n logTiming(\n `Importing from ${modulePath}${exportName ? ` (${exportName})` : \"\"}`,\n );\n if (setupExportName) logTiming(`Calling setup: ${setupExportName}`);\n return importBenchFn(modulePath, exportName, setupExportName, params);\n }\n return { fn: reconstructFunction(message.fnCode!), params: message.params };\n}\n\n/** Import variant from directory and prepare benchmark function */\nasync function importVariantModule(\n message: RunMessage,\n): Promise<BenchmarkImportResult> {\n const { variantDir, variantId } = message;\n logTiming(`Importing variant ${variantId} from ${variantDir}`);\n return resolveVariantFn({\n ...message,\n variantDir: variantDir!,\n variantId: variantId!,\n });\n}\n\n/** Eval serialized function body back into a callable */\nfunction reconstructFunction(fnCode: string): BenchmarkFunction {\n // biome-ignore lint/security/noGlobalEval: Necessary for worker process isolation, code is from trusted source\n const fn = eval(`(${fnCode})`); // eslint-disable-line no-eval\n if (typeof fn !== \"function\") {\n throw new Error(\"Reconstructed code is not a function\");\n }\n return fn;\n}\n\n/** Run benchmark with optional heap, time, and coverage profiling */\nasync function runWithProfiling(\n message: RunMessage,\n runner: BenchRunner,\n): Promise<ResultMessage> {\n const state: ProfilingState = {};\n const runBench = buildProfilingChain(message, runner, state);\n\n if (!message.options.callCounts) {\n const results = await runBench();\n return { type: \"result\", results, ...state };\n }\n\n const { withCoverageProfiling } = await import(\n \"../profiling/node/CoverageSampler.ts\"\n );\n const r = await withCoverageProfiling(async session => {\n state.profilerSession = session;\n return runBench();\n });\n state.coverage = r.coverage;\n return { type: \"result\", results: r.result, ...state };\n}\n\n/** Build nested profiling wrappers: outer heap, inner time */\nfunction buildProfilingChain(\n message: RunMessage,\n runner: BenchRunner,\n state: ProfilingState,\n): () => Promise<MeasuredResults[]> {\n const { alloc, profile, profileInterval, allocInterval, allocDepth } =\n message.options;\n\n const run = async () => {\n const { fn, params } = await resolveBenchmarkFn(message);\n return runner.runBench({ ...message.spec, fn }, message.options, params);\n };\n\n const runMaybeWithTime = profile\n ? async () => {\n const { withTimeProfiling } = await import(\n \"../profiling/node/TimeSampler.ts\"\n );\n const opts = {\n interval: profileInterval,\n session: state.profilerSession,\n };\n const r = await withTimeProfiling(opts, run);\n state.timeProfile = r.profile;\n return r.result;\n }\n : run;\n\n return alloc\n ? async () => {\n const { withHeapSampling } = await import(\n \"../profiling/node/HeapSampler.ts\"\n );\n const heapOpts = {\n samplingInterval: allocInterval,\n stackDepth: allocDepth,\n };\n const r = await withHeapSampling(heapOpts, runMaybeWithTime);\n state.heapProfile = r.profile;\n return r.result;\n }\n : runMaybeWithTime;\n}\n\nprocess.on(\"message\", async (message: RunMessage) => {\n if (message.type !== \"run\") return;\n\n logTiming(`Processing ${message.spec.name} with ${message.runnerName}`);\n\n try {\n const start = getPerfNow();\n const runner = await createBenchRunner(message.runnerName, message.options);\n logTiming(\"Runner created in\", getElapsed(start));\n\n const benchStart = getPerfNow();\n const result = await runWithProfiling(message, runner);\n logTiming(\"Benchmark execution took\", getElapsed(benchStart));\n sendAndExit(result, 0);\n } catch (error) {\n const err = error instanceof Error ? error : undefined;\n sendAndExit(\n {\n type: \"error\",\n error: err?.message ?? String(error),\n stack: err?.stack,\n },\n 1,\n );\n }\n});\n\n// Prevent zombie processes\nsetTimeout(() => {\n console.error(\"WorkerScript: Maximum lifetime exceeded, exiting\");\n process.exit(1);\n}, maxLifetime);\n\n// Prevent stdin from keeping the worker process alive\nprocess.stdin.pause();\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./index-Br9bp_cX.js";var t=`http://www.w3.org/2000/svg`,n=e=>e.replace(/[A-Z]/g,e=>`-`+e.toLowerCase()),r=e=>document.createElementNS(t,e);function i(e,t){for(let[r,i]of Object.entries(t))e.setAttribute(n(r),i)}function a(e,n){let r=document.createElementNS(t,`svg`);return r.setAttribute(`width`,String(e)),r.setAttribute(`height`,String(n)),e&&n&&r.setAttribute(`viewBox`,`0 0 ${e} ${n}`),r}function o(e,n,r,a,o){let s=document.createElementNS(t,`rect`);return s.setAttribute(`x`,String(e)),s.setAttribute(`y`,String(n)),s.setAttribute(`width`,String(r)),s.setAttribute(`height`,String(a)),i(s,o),s}function s(e,n,r,a,o){let s=document.createElementNS(t,`line`);return s.setAttribute(`x1`,String(e)),s.setAttribute(`y1`,String(n)),s.setAttribute(`x2`,String(r)),s.setAttribute(`y2`,String(a)),i(s,o),s}function c(e,n,r,i=`start`,a=`9`,o=`#666`,s=`400`){let c=document.createElementNS(t,`text`);return c.setAttribute(`x`,String(e)),c.setAttribute(`y`,String(n)),c.setAttribute(`text-anchor`,i),c.setAttribute(`font-size`,a),c.setAttribute(`font-weight`,s),c.setAttribute(`fill`,o),c.textContent=r,c}function l(e,n){let r=document.createElementNS(t,`path`);return r.setAttribute(`d`,e),i(r,n),r}function u(e){let t=`ci-sketch`;if(e.querySelector(`#${t}`))return t;let n=f(e),a=r(`filter`);i(a,{id:t,x:`-5%`,y:`-5%`,width:`110%`,height:`110%`});let o=r(`feTurbulence`);i(o,{type:`turbulence`,baseFrequency:`0.06`,numOctaves:`4`,seed:`1`,result:`noise`});let s=r(`feDisplacementMap`);return i(s,{in:`SourceGraphic`,in2:`noise`,scale:`10`,xChannelSelector:`R`,yChannelSelector:`G`}),a.appendChild(o),a.appendChild(s),n.appendChild(a),t}function d(e){let t=`margin-hatch`;if(e.querySelector(`#${t}`))return t;let n=f(e),a=r(`pattern`);i(a,{id:t,patternUnits:`userSpaceOnUse`,width:`5`,height:`5`,patternTransform:`rotate(45)`});let o=r(`line`);return i(o,{x1:`0`,y1:`0`,x2:`0`,y2:`5`}),o.classList.add(`margin-hatch-stroke`),a.appendChild(o),n.appendChild(a),t}function f(e){let n=e.querySelector(`defs`);return n||(n=document.createElementNS(t,`defs`),e.insertBefore(n,e.firstChild)),n}var p={top:22,right:12,bottom:22,left:12},m={width:260,height:85,title:`Δ%`,smooth:!0,direction:`uncertain`,includeZero:!0},h={faster:{fill:`#bbf7d0`,stroke:`#22c55e`},slower:{fill:`#fee2e2`,stroke:`#ef4444`},uncertain:{fill:`#dbeafe`,stroke:`#3b82f6`},equivalent:{fill:`#dcfce7`,stroke:`#86efac`}};function g(e,t,n,r={}){let i={...m,...r},c=v(i.width,i.height,!!i.pointLabel),l=a(c.width,c.height);if(!e?.length)return l;let{fill:d,stroke:f}=h[i.direction],{includeZero:p,equivMargin:g}=i,_=y(e,t,c,p,g,n),{margin:E,plot:D}=c,O=_.x(n);b(l,i,E,O),g&&p&&x(l,g,_,c);let k=_.x(t[0]),A=o(k,E.top,_.x(t[1])-k,D.h,{fill:d}),j=p?`ci-region-strong`:`ci-region`;return A.classList.add(j,`ci-${i.direction}`),i.ciReliable===!1&&(A.classList.add(`ci-unreliable`),A.setAttribute(`filter`,`url(#${u(l)})`)),l.appendChild(A),i.smooth?S(l,e,_,f):C(l,e,_,c,f),w(l,_,c,p),l.appendChild(s(O,E.top,O,E.top+D.h,{stroke:f,strokeWidth:`2`})),T(l,t,_,c,i),l}function _(e,t={}){return e.histogram?g(e.histogram,e.ci,e.percent,{title:e.label,direction:e.direction,ciLevel:e.ciLevel,ciReliable:e.ciReliable,...t}):a(0,0)}function v(e,t,n){let r=t<p.top+p.bottom+10?{top:4,right:6,bottom:4,left:6}:{...p,top:n?30:p.top};return{width:e,height:t,margin:r,plot:{w:e-r.left-r.right,h:t-r.top-r.bottom}}}function y(e,t,n,r,i,a){let{margin:o,plot:s}=n,c=e.map(e=>e.x),l=r?[0]:[],u=i?[-i,i]:[],d=a==null?[]:[a],f=Math.min(...c,t[0],...l,...u,...d),p=Math.max(...c,t[1],...l,...u,...d),m=Math.max(...e.map(e=>e.count)),h=p-f||1;return{x:e=>o.left+(e-f)/h*s.w,y:e=>o.top+s.h-e/m*s.h}}function b(e,t,n,r){if(t.title&&e.appendChild(c(n.left,14,t.title,`start`,`13`,`currentColor`,`600`)),t.pointLabel){let i=c(r,n.top-6,t.pointLabel,`middle`,`15`,`currentColor`,`700`);e.appendChild(i)}}function x(e,t,n,r){let{margin:i,plot:a}=r,s=n.x(-t),c=n.x(t),l=`url(#${d(e)})`,u=a.h/3,f=o(s,i.top+(a.h-u)/2,c-s,u,{fill:l,strokeWidth:`1.5`});f.classList.add(`margin-zone`),e.appendChild(f)}function S(e,t,n,r){let i=E([...t].sort((e,t)=>e.x-t.x),2),a=i.map(e=>`${n.x(e.x)},${n.y(e.count)}`),o=n.y(0),s=n.x(i[0].x),c=n.x(i.at(-1).x),u=l(`M${s},${o}L${a.join(`L`)}L${c},${o}Z`,{fill:r});u.classList.add(`dist-fill`),e.appendChild(u);let d=l(`M${a.join(`L`)}`,{stroke:r,fill:`none`,strokeWidth:`1.5`});d.classList.add(`dist-stroke`),e.appendChild(d)}function C(e,t,n,r,i){let a=[...t].sort((e,t)=>e.x-t.x),s=a.length>1?a[1].x-a[0].x:1,c=s/(n.x(a.at(-1).x)-n.x(a[0].x)+s)*r.plot.w*.9,l=n.y(0),u={fill:i,opacity:`0.6`};for(let t of a){let r=n.y(t.count);e.appendChild(o(n.x(t.x)-c/2,r,c,l-r,u))}}function w(e,t,n,r){let{margin:i,plot:a}=n,o=t.x(0),c=o>=i.left&&o<=n.width-i.right;!r||!c||e.appendChild(s(o,i.top-4,o,i.top+a.h+4,{stroke:`#000`,strokeWidth:`1`}))}function T(t,n,r,i,a){if(i.margin.bottom<15)return;let o=i.height-4,s=a.ciLabels?.[0]??e(n[0],0),l=a.ciLabels?.[1]??e(n[1],0),u=r.x(n[0]),d=r.x(n[1]),f=Math.max(s.length,l.length)*6;(!a.includeZero||d-u>=f)&&(t.appendChild(c(u,o,s,`middle`,`11`)),t.appendChild(c(d,o,l,`middle`,`11`)))}function E(e,t){return e.map((n,r)=>{let i=0,a=0;for(let n=0;n<e.length;n++){let o=Math.exp(-((r-n)**2)/(2*t**2));i+=e[n].count*o,a+=o}return{x:n.x,count:i/a}})}export{_ as createCIPlot,g as createDistributionPlot};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e,r as t}from"./index-Br9bp_cX.js";import{_ as n,a as r,b as i,d as a,f as o,g as s,h as c,l,m as u,o as d,t as f,u as p,v as m,y as h}from"./LegendUtils-BJpbn_jr.js";var g=Array.prototype,_=g.slice;g.map;function v(e){return()=>e}function y(e,t,n){let r;for(;;){let i=s(e,t,n);if(i===r||i===0||!isFinite(i))return[e,t];i>0?(e=Math.floor(e/i)*i,t=Math.ceil(t/i)*i):i<0&&(e=Math.ceil(e*i)/i,t=Math.floor(t*i)/i),r=i}}function b(){var e=m,t=h,r=c;function a(a){Array.isArray(a)||(a=Array.from(a));var o,c=a.length,l,u,d=Array(c);for(o=0;o<c;++o)d[o]=e(a[o],o,a);var f=t(d),p=f[0],m=f[1],g=r(d,p,m);if(!Array.isArray(g)){let e=m,r=+g;if(t===h&&([p,m]=y(p,m,r)),g=n(p,m,r),g[0]<=p&&(u=s(p,m,r)),g[g.length-1]>=m)if(e>=m&&t===h){let e=s(p,m,r);isFinite(e)&&(e>0?m=(Math.floor(m/e)+1)*e:e<0&&(m=(Math.ceil(m*-e)+1)/-e))}else g.pop()}for(var _=g.length,v=0,b=_;g[v]<=p;)++v;for(;g[b-1]>m;)--b;(v||b<_)&&(g=g.slice(v,b),_=b-v);var x=Array(_+1),S;for(o=0;o<=_;++o)S=x[o]=[],S.x0=o>0?g[o-1]:p,S.x1=o<_?g[o]:m;if(isFinite(u)){if(u>0)for(o=0;o<c;++o)(l=d[o])!=null&&p<=l&&l<=m&&x[Math.min(_,Math.floor((l-p)/u))].push(a[o]);else if(u<0){for(o=0;o<c;++o)if((l=d[o])!=null&&p<=l&&l<=m){let e=Math.floor((p-l)*u);x[Math.min(_,e+(g[e]<=l))].push(a[o])}}}else for(o=0;o<c;++o)(l=d[o])!=null&&p<=l&&l<=m&&x[i(g,l,0,_)].push(a[o]);return x}return a.value=function(t){return arguments.length?(e=typeof t==`function`?t:v(t),a):e},a.domain=function(e){return arguments.length?(t=typeof e==`function`?e:v([e[0],e[1]]),a):t},a.thresholds=function(e){return arguments.length?(r=typeof e==`function`?e:v(Array.isArray(e)?_.call(e):e),a):r},a}function x(n,i){let{unitSuffix:a,convertValue:o,formatValue:s}=e(n.map(e=>e.value)),{barData:c,binMin:u,binMax:p,yMax:m}=S(n.map(e=>({...e,value:o(e.value)})),i),{colorMap:h,legendItems:g}=C(i);return r({...t,x:{label:`Time (${a})`,labelAnchor:`center`,domain:[u,p],labelOffset:45,tickFormat:s,ticks:5},y:{label:`Count`,labelAnchor:`top`,labelArrow:!1,grid:!0,domain:[0,m]},marks:[d(c,{x1:`x1`,x2:`x2`,y:`count`,fill:e=>h.get(e.benchmark),fillOpacity:.6}),l([0]),...f({xMin:u,xMax:p,yMax:m},g)]})}function S(e,n){let r=e.map(e=>e.value).sort((e,t)=>e-t),i=o(r,.01),s=o(r,.99),c=(s-i)/25,l=a(1,25).map(e=>i+e*c),d=b().domain([i,s]).thresholds(l).value(e=>e.value)(e),f=n.length,p=(s-i)/t.width,m=d.flatMap(e=>{let t=new Map;for(let n of e)t.set(n.benchmark,(t.get(n.benchmark)||0)+1);let r=e.x1-e.x0,i=Math.min(r*.5,p*8),a=e.x0+i/2,o=(r-i)/f;return n.map((e,n)=>{let r=a+n*o,i=a+(n+1)*o;return{benchmark:e,count:t.get(e)||0,x1:r,x2:i}})});return{barData:m,binMin:i,binMax:s,yMax:(u(m,e=>e.count)||1)*1.15}}function C(e){let t=p,n=e=>t[e%10];return{colorMap:new Map(e.map((e,t)=>[e,n(t)])),legendItems:e.map((e,t)=>({color:n(t),label:e,style:`vertical-bar`}))}}export{x as createHistogramKde};
|