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 @@
|
|
|
1
|
+
{"version":3,"file":"BenchMatrix-BZVrBB_h.mjs","names":[],"sources":["../src/matrix/CaseLoader.ts","../src/runners/MeasuredResults.ts","../src/runners/SampleStats.ts","../src/runners/MergeBatches.ts","../src/runners/GcStats.ts","../src/matrix/VariantLoader.ts","../src/runners/AdaptiveWrapper.ts","../src/runners/BenchRunner.ts","../src/runners/TimingRunner.ts","../src/runners/CreateRunner.ts","../src/runners/RunnerUtils.ts","../src/runners/TimingUtils.ts","../src/runners/RunnerOrchestrator.ts","../src/matrix/MatrixDirRunner.ts","../src/matrix/MatrixInlineRunner.ts","../src/matrix/BenchMatrix.ts"],"sourcesContent":["import type { LoadedCase } from \"./BenchMatrix.ts\";\n\n/** Module exporting case IDs and an optional loader for case data */\nexport interface CasesModule<T = unknown> {\n cases: string[];\n /** Subset of cases for quick runs */\n defaultCases?: string[];\n /** Subset of variants for quick runs */\n defaultVariants?: string[];\n loadCase?: (id: string) => LoadedCase<T> | Promise<LoadedCase<T>>;\n}\n\n/** Import and validate a cases module, which must export a `cases` array */\nexport async function loadCasesModule<T = unknown>(\n moduleUrl: string,\n): Promise<CasesModule<T>> {\n const module = await import(moduleUrl);\n if (!Array.isArray(module.cases)) {\n throw new Error(`Cases module at ${moduleUrl} must export 'cases' array`);\n }\n return {\n cases: module.cases,\n defaultCases: module.defaultCases,\n defaultVariants: module.defaultVariants,\n loadCase: module.loadCase,\n };\n}\n\n/** Load case data from a CasesModule, or use the caseId as data if no module */\nexport async function loadCaseData<T>(\n casesModule: CasesModule<T> | undefined,\n caseId: string,\n): Promise<LoadedCase<T>> {\n if (casesModule?.loadCase) {\n return casesModule.loadCase(caseId);\n }\n return { data: caseId as T };\n}\n","import type { NavTiming } from \"../profiling/browser/BrowserProfiler.ts\";\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 { GcStats } from \"../runners/GcStats.ts\";\nimport type { NodeGCTime } from \"../runners/NodeGC.ts\";\n\n/** Benchmark results: times in milliseconds, sizes in kilobytes */\nexport interface MeasuredResults {\n name: string;\n\n /** Raw execution time samples for custom statistics */\n samples: number[];\n\n /** Warmup iteration timings (ms) - captured before gc/pause-warmup */\n warmupSamples?: number[];\n\n /** Raw allocation samples per iteration (KB) */\n allocationSamples?: number[];\n\n /** Heap size per sample (bytes) - used for charts */\n heapSamples?: number[];\n\n /** Execution time in milliseconds */\n time: {\n min: number;\n max: number;\n avg: number;\n p25?: number;\n p50: number;\n p75: number;\n p95?: number;\n p99: number;\n p999: number;\n cv?: number;\n mad?: number;\n outlierRate?: number;\n };\n\n /** Heap size increase during test run (kilobytes) */\n heapSize?: {\n avg: number;\n min: number;\n max: number;\n };\n\n /** Time for explicit gc() call after test execution (ms), excludes in-run GC. */\n gcTime?: {\n avg: number;\n min: number;\n max: number;\n };\n\n /** Stop-the-world GC pause time (ms) via Node perf hooks (nodeObserveGC). */\n nodeGcTime?: NodeGCTime;\n\n /** Total time spent collecting samples (seconds) */\n totalTime?: number;\n\n /** Monotonic start time (μs, hrtime-based) for Perfetto trace alignment. */\n startTime?: number;\n\n /** Convergence information for adaptive mode */\n convergence?: {\n converged: boolean;\n confidence: number;\n reason: string;\n };\n\n /** V8 optimization tier tracking (requires --allow-natives-syntax) */\n optStatus?: OptStatusInfo;\n\n /** Per-sample V8 optimization status codes (for chart visualization) */\n optSamples?: number[];\n\n /** Points where pauses occurred for V8 optimization */\n pausePoints?: PausePoint[];\n\n /** Batch boundaries for block bootstrap (indices into samples where each batch starts) */\n batchOffsets?: number[];\n\n /** GC stats from V8's --trace-gc-nvp (requires --gc-stats and worker mode) */\n gcStats?: GcStats;\n\n /** Heap sampling allocation profile (requires --heap-sample and worker mode) */\n heapProfile?: HeapProfile;\n\n /** V8 CPU time sampling profile (requires --profile and worker mode) */\n timeProfile?: TimeProfile;\n\n /** Per-function execution counts (requires --call-counts) */\n coverage?: CoverageData;\n\n /** Navigation timings from page-load mode (one per iteration) */\n navTimings?: NavTiming[];\n}\n\n/** Pause inserted during sample collection for V8 optimization settling. */\nexport interface PausePoint {\n sampleIndex: number;\n durationMs: number;\n}\n\n/** V8 optimization tier distribution */\nexport interface OptTierInfo {\n count: number;\n medianMs: number;\n}\n\n/** V8 optimization status summary */\nexport interface OptStatusInfo {\n /** Samples by tier name (e.g., \"turbofan\", \"sparkplug\") */\n byTier: Record<string, OptTierInfo>;\n /** Number of samples with deopt flag set */\n deoptCount: number;\n}\n\n/**\n * V8 GetOptimizationStatus() return codes ==> human-readable tier names.\n * Bit 0 (1): is_function\n * Bit 4 (16): is_optimized (TurboFan)\n * Bit 5 (32): is_optimized (Maglev)\n * Bit 7 (128): is_baseline (Sparkplug)\n * Bit 3 (8): maybe_deoptimized\n */\nexport const optStatusNames: Record<number, string> = {\n 1: \"interpreted\",\n 129: \"sparkplug\", // 1 + 128\n 17: \"turbofan\", // 1 + 16\n 33: \"maglev\", // 1 + 32\n 49: \"turbofan+maglev\", // 1 + 16 + 32\n 32769: \"optimized\", // common optimized status\n};\n","import {\n coefficientOfVariation,\n median,\n medianAbsoluteDeviation,\n percentile,\n} from \"../stats/StatisticalUtils.ts\";\nimport {\n type MeasuredResults,\n type OptStatusInfo,\n optStatusNames,\n} from \"./MeasuredResults.ts\";\n\n/** Compute percentiles, CV, MAD, and outlier rate from timing samples. */\nexport function computeStats(samples: number[]): MeasuredResults[\"time\"] {\n let min = Number.POSITIVE_INFINITY;\n let max = Number.NEGATIVE_INFINITY;\n let sum = 0;\n for (const s of samples) {\n if (s < min) min = s;\n if (s > max) max = s;\n sum += s;\n }\n const sorted = [...samples].sort((a, b) => a - b);\n const pct = (p: number) =>\n sorted[Math.max(0, Math.ceil(sorted.length * p) - 1)];\n return {\n min,\n max,\n avg: sum / samples.length,\n p25: pct(0.25),\n p50: pct(0.5),\n p75: pct(0.75),\n p95: pct(0.95),\n p99: pct(0.99),\n p999: pct(0.999),\n cv: coefficientOfVariation(samples),\n mad: medianAbsoluteDeviation(samples),\n outlierRate: outlierImpactRatio(samples),\n };\n}\n\n/** Measure outlier impact as proportion of excess time above 1.5*IQR threshold. */\nexport function outlierImpactRatio(samples: number[]): number {\n if (samples.length === 0) return 0;\n const med = median(samples);\n const q75 = percentile(samples, 0.75);\n const threshold = med + 1.5 * (q75 - med);\n\n let excessTime = 0;\n for (const sample of samples) {\n if (sample > threshold) excessTime += sample - med;\n }\n const total = samples.reduce((a, b) => a + b, 0);\n return total > 0 ? excessTime / total : 0;\n}\n\n/** Group samples by V8 optimization tier and count deopts. */\nexport function analyzeOptStatus(\n samples: number[],\n statuses: number[],\n): OptStatusInfo | undefined {\n if (statuses.length === 0 || statuses[0] === undefined) return undefined;\n\n const byStatus = new Map<number, number[]>();\n let deoptCount = 0;\n for (let i = 0; i < samples.length; i++) {\n const status = statuses[i];\n if (status === undefined) continue;\n if (status & 8) deoptCount++; // deopt flag (bit 3)\n const group = byStatus.get(status);\n if (group) group.push(samples[i]);\n else byStatus.set(status, [samples[i]]);\n }\n\n const entries = [...byStatus].map(([status, times]) => {\n const name = optStatusNames[status] || `status=${status}`;\n return [name, { count: times.length, medianMs: median(times) }] as const;\n });\n return { byTier: Object.fromEntries(entries), deoptCount };\n}\n\n/** @return runtime gc() function, or a no-op if --expose-gc wasn't passed. */\nexport function gcFunction(): () => void {\n const gc = globalThis.gc ?? (globalThis as any).__gc;\n if (gc) return gc;\n console.warn(\"gc() not available, run node/bun with --expose-gc\");\n return () => {};\n}\n\n/** @return function that reads V8 optimization status via %GetOptimizationStatus. */\nexport function createOptStatusGetter(): ((fn: unknown) => number) | undefined {\n try {\n // %GetOptimizationStatus returns a bitmask\n const fn = new Function(\"f\", \"return %GetOptimizationStatus(f)\");\n fn(() => {});\n return fn as (fn: unknown) => number;\n } catch {\n return undefined;\n }\n}\n","import type { GcStats } from \"./GcStats.ts\";\nimport type { MeasuredResults } from \"./MeasuredResults.ts\";\nimport { computeStats } from \"./SampleStats.ts\";\n\n/** Progress update emitted after each batch run. */\nexport interface BatchProgress {\n batch: number;\n batches: number;\n label: \"baseline\" | \"current\";\n elapsed: number;\n}\n\ntype SamplesFn = (r: MeasuredResults) => number[] | undefined;\n\n/** Merge multiple batch results, concatenating samples and tracking batch boundaries. */\nexport function mergeBatchResults(results: MeasuredResults[]): MeasuredResults {\n if (results.length === 0) {\n throw new Error(\"Cannot merge empty results array\");\n }\n if (results.length === 1) return { ...results[0], batchOffsets: [0] };\n\n const allSamples = results.flatMap(r => r.samples);\n const time = computeStats(allSamples);\n\n const batchOffsets: number[] = [];\n const offsetPauses: MeasuredResults[\"pausePoints\"] = [];\n let offset = 0;\n for (const r of results) {\n batchOffsets.push(offset);\n for (const p of r.pausePoints ?? []) {\n const sampleIndex = p.sampleIndex + offset;\n offsetPauses.push({ sampleIndex, durationMs: p.durationMs });\n }\n offset += r.samples.length;\n }\n\n // last batch as base ==> new MeasuredResults fields get \"take last\"\n // semantics by default instead of silently disappearing\n return {\n ...results[results.length - 1],\n name: results[0].name,\n samples: allSamples,\n warmupSamples: concatOptional(results, r => r.warmupSamples),\n allocationSamples: concatOptional(results, r => r.allocationSamples),\n heapSamples: concatOptional(results, r => r.heapSamples),\n optSamples: concatOptional(results, r => r.optSamples),\n time,\n startTime: results[0].startTime,\n totalTime: results.reduce((sum, r) => sum + (r.totalTime || 0), 0),\n pausePoints: offsetPauses.length ? offsetPauses : undefined,\n batchOffsets,\n gcStats: mergeGcStats(results),\n };\n}\n\n/** Sum GcStats across batches, or undefined if none collected. */\nexport function mergeGcStats(\n results: { gcStats?: GcStats }[],\n): GcStats | undefined {\n const stats = results.map(r => r.gcStats).filter(Boolean) as GcStats[];\n if (!stats.length) return undefined;\n const sum = (fn: (s: GcStats) => number | undefined) =>\n stats.reduce((acc, s) => acc + (fn(s) ?? 0), 0);\n return {\n scavenges: sum(s => s.scavenges),\n markCompacts: sum(s => s.markCompacts),\n totalCollected: sum(s => s.totalCollected),\n gcPauseTime: sum(s => s.gcPauseTime),\n totalAllocated: sum(s => s.totalAllocated) || undefined,\n totalPromoted: sum(s => s.totalPromoted) || undefined,\n totalSurvived: sum(s => s.totalSurvived) || undefined,\n };\n}\n\n/** Run N benchmarks + optional baseline in batched alternation, merge results. */\nexport async function runBatched(\n runners: (() => Promise<MeasuredResults>)[],\n baseline: (() => Promise<MeasuredResults>) | undefined,\n batches: number,\n warmupBatch = false,\n onProgress?: (p: BatchProgress) => void,\n): Promise<{ results: MeasuredResults[]; baseline?: MeasuredResults }> {\n const runnerBatches: MeasuredResults[][] = runners.map(() => []);\n const baselineBatches: MeasuredResults[] = [];\n const start = performance.now();\n\n const report = (batch: number, label: BatchProgress[\"label\"]) =>\n onProgress?.({ batch, batches, label, elapsed: performance.now() - start });\n\n for (let i = 0; i < batches; i++) {\n const reverse = i % 2 === 1;\n // baseline runs before benchmarks on even batches, after on odd (alternation)\n if (!reverse && baseline) {\n baselineBatches.push(await baseline());\n report(i, \"baseline\");\n }\n for (let j = 0; j < runners.length; j++) {\n runnerBatches[j].push(await runners[j]());\n report(i, \"current\");\n }\n if (reverse && baseline) {\n baselineBatches.push(await baseline());\n report(i, \"baseline\");\n }\n }\n\n if (!warmupBatch && batches > 1) {\n for (const b of runnerBatches) b.shift();\n baselineBatches.shift();\n }\n\n const results = runnerBatches.map(b => mergeBatchResults(b));\n const mergedBaseline = baselineBatches.length\n ? mergeBatchResults(baselineBatches)\n : undefined;\n return { results, baseline: mergedBaseline };\n}\n\n/** Concat optional number arrays across batches. */\nfunction concatOptional(results: MeasuredResults[], fn: SamplesFn) {\n const all = results.flatMap(r => fn(r) || []);\n return all.length ? all : undefined;\n}\n","/**\n * Aggregated GC statistics from V8 trace events.\n * Node (--trace-gc-nvp) provides all fields; browser (CDP) provides counts, collected, and pause only.\n */\nexport interface GcStats {\n scavenges: number;\n markCompacts: number;\n /** Bytes freed by GC. */\n totalCollected: number;\n /** Total GC pause time in milliseconds. */\n gcPauseTime: number;\n /** Bytes allocated (Node only). */\n totalAllocated?: number;\n /** Bytes promoted to old generation (Node only). */\n totalPromoted?: number;\n /** Bytes survived in young generation (Node only). */\n totalSurvived?: number;\n}\n\n/** Single GC event. Node provides all fields; browser provides type, pauseMs, collected. */\nexport interface GcEvent {\n type: \"scavenge\" | \"mark-compact\" | \"minor-ms\" | \"unknown\";\n pauseMs: number;\n collected: number;\n /** Node only. */\n allocated?: number;\n /** Node only. */\n promoted?: number;\n /** Node only. */\n survived?: number;\n}\n\n/** Parse a single --trace-gc-nvp stderr line into a GcEvent. */\nexport function parseGcLine(line: string): GcEvent | undefined {\n if (!line.includes(\"pause=\")) return undefined;\n\n const fields = parseNvpFields(line);\n if (!fields.gc) return undefined;\n\n const intField = (name: string) => Number.parseInt(fields[name] || \"0\", 10);\n const type = parseGcType(fields.gc);\n const pauseMs = Number.parseFloat(fields.pause || \"0\");\n if (Number.isNaN(pauseMs)) return undefined;\n\n const allocated = intField(\"allocated\");\n const promoted = intField(\"promoted\");\n // V8 uses \"new_space_survived\" not \"survived\"\n const survived = intField(\"new_space_survived\") || intField(\"survived\");\n const start = intField(\"start_object_size\");\n const end = intField(\"end_object_size\");\n const collected = start > end ? start - end : 0;\n\n return { type, pauseMs, allocated, collected, promoted, survived };\n}\n\n/** Aggregate a list of GC events into summary statistics. */\nexport function aggregateGcStats(events: GcEvent[]): GcStats {\n let scavenges = 0;\n let markCompacts = 0;\n let gcPauseTime = 0;\n let totalCollected = 0;\n let hasNode = false;\n let totalAllocated = 0;\n let totalPromoted = 0;\n let totalSurvived = 0;\n\n for (const event of events) {\n if (event.type === \"scavenge\" || event.type === \"minor-ms\") scavenges++;\n else if (event.type === \"mark-compact\") markCompacts++;\n gcPauseTime += event.pauseMs;\n totalCollected += event.collected;\n if (event.allocated != null) {\n hasNode = true;\n totalAllocated += event.allocated;\n totalPromoted += event.promoted ?? 0;\n totalSurvived += event.survived ?? 0;\n }\n }\n\n return {\n scavenges,\n markCompacts,\n totalCollected,\n gcPauseTime,\n ...(hasNode && { totalAllocated, totalPromoted, totalSurvived }),\n };\n}\n\n/** Parse name=value pairs from a trace-gc-nvp line. */\nfunction parseNvpFields(line: string): Record<string, string> {\n const pairs = [...line.matchAll(/(\\w+)=([^\\s,]+)/g)];\n return Object.fromEntries(pairs.map(([, key, value]) => [key, value]));\n}\n\n/** Map V8 gc type codes to normalized event types. */\nfunction parseGcType(gcField: string): GcEvent[\"type\"] {\n // V8 uses: s=scavenge, mc=mark-compact, mmc=minor-mc (young gen mark-compact)\n if (gcField === \"s\" || gcField === \"scavenge\") return \"scavenge\";\n if (gcField === \"mc\" || gcField === \"ms\" || gcField === \"mark-compact\")\n return \"mark-compact\";\n if (gcField === \"mmc\" || gcField === \"minor-mc\" || gcField === \"minor-ms\")\n return \"minor-ms\";\n return \"unknown\";\n}\n","import fs from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport type { Variant } from \"./BenchMatrix.ts\";\n\n/** List variant IDs by scanning .ts files in a directory */\nexport async function discoverVariants(dirUrl: string): Promise<string[]> {\n const dirPath = fileURLToPath(dirUrl);\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n return entries\n .filter(e => e.isFile() && e.name.endsWith(\".ts\"))\n .map(e => e.name.slice(0, -3))\n .sort();\n}\n\n/** Import a variant module and return its run/setup exports as a Variant */\nexport async function loadVariant<T = unknown>(\n dirUrl: string,\n variantId: string,\n): Promise<Variant<T>> {\n const moduleUrl = variantModuleUrl(dirUrl, variantId);\n const module = await import(moduleUrl);\n return extractVariant(module, variantId, moduleUrl);\n}\n\n/** Resolve the import URL for a variant file */\nexport function variantModuleUrl(dirUrl: string, variantId: string): string {\n return new URL(`${variantId}.ts`, dirUrl).href;\n}\n\n/** Validate and extract a Variant from a module's exports */\nfunction extractVariant<T>(\n module: Record<string, unknown>,\n variantId: string,\n moduleUrl: string,\n): Variant<T> {\n const { setup, run } = module;\n const loc = `Variant '${variantId}' at ${moduleUrl}`;\n if (typeof run !== \"function\") {\n throw new Error(`${loc} must export 'run'`);\n }\n if (setup === undefined) return run as (data: T) => void;\n if (typeof setup !== \"function\") {\n throw new Error(`${loc}: 'setup' must be a function`);\n }\n return { setup: setup as (data: T) => unknown, run: run as () => void };\n}\n","import { median } from \"../stats/StatisticalUtils.ts\";\nimport type { BenchmarkSpec } from \"./BenchmarkSpec.ts\";\nimport type { BenchRunner, RunnerOptions } from \"./BenchRunner.ts\";\nimport type { MeasuredResults } from \"./MeasuredResults.ts\";\nimport { msToNs } from \"./RunnerUtils.ts\";\nimport { computeStats, outlierImpactRatio } from \"./SampleStats.ts\";\n\n/** Options for adaptive sampling: collects until statistical convergence or timeout. */\nexport interface AdaptiveOptions extends RunnerOptions {\n /** Enable adaptive sampling (default: true when using adaptive runner) */\n adaptive?: boolean;\n /** Minimum measurement time in ms before convergence can stop sampling (default: 1000) */\n minTime?: number;\n /** Maximum measurement time in ms, hard stop (default: 10000) */\n maxTime?: number;\n /** Target confidence percentage to stop early (default: 95) */\n targetConfidence?: number;\n /** Confidence threshold 0-100 (alias for targetConfidence) */\n convergence?: number;\n}\n\ntype Metrics = {\n medianDrift: number;\n impactDrift: number;\n medianStable: boolean;\n impactStable: boolean;\n};\n\ninterface ConvergenceResult {\n converged: boolean;\n confidence: number;\n reason: string;\n}\n\nconst minTime = 1000;\nconst maxTime = 10000;\nconst targetConfidence = 95;\nconst fallbackThreshold = 80;\nconst windowSize = 50;\nconst stability = 0.05; // 5% drift threshold (was 2%, too strict for real benchmarks)\nconst initialBatch = 100;\nconst continueBatch = 100;\nconst continueIterations = 10;\n\n/** Wrap a runner with adaptive sampling (convergence detection or timeout). */\nexport function createAdaptiveWrapper(\n baseRunner: BenchRunner,\n options: AdaptiveOptions,\n): BenchRunner {\n return {\n async runBench<T = unknown>(\n bench: BenchmarkSpec<T>,\n opts: RunnerOptions,\n params?: T,\n ): Promise<MeasuredResults[]> {\n return runAdaptiveBench(baseRunner, bench, opts, options, params);\n },\n };\n}\n\n/** Check convergence by comparing sliding windows of samples for stability. */\nexport function checkConvergence(samples: number[]): ConvergenceResult {\n const windowSize = getWindowSize(samples);\n const minSamples = windowSize * 2;\n if (samples.length < minSamples) {\n const confidence = (samples.length / minSamples) * 100;\n const reason = `Collecting samples: ${samples.length}/${minSamples}`;\n return { converged: false, confidence, reason };\n }\n return buildConvergence(getStability(samples, windowSize));\n}\n\n/** Run benchmark with adaptive sampling until convergence or timeout. */\nasync function runAdaptiveBench<T>(\n runner: BenchRunner,\n bench: BenchmarkSpec<T>,\n opts: RunnerOptions,\n adaptive: AdaptiveOptions,\n params?: T,\n): Promise<MeasuredResults[]> {\n const overrides = opts as AdaptiveOptions;\n const min = overrides.minTime ?? adaptive.minTime ?? minTime;\n const max = overrides.maxTime ?? adaptive.maxTime ?? maxTime;\n const target =\n overrides.convergence ?? adaptive.convergence ?? targetConfidence;\n const allSamples: number[] = [];\n\n const { warmup, startTime: hrtimeStart } = await collectInitial(\n runner,\n bench,\n opts,\n params,\n allSamples,\n );\n // Start timing after warmup so warmup time doesn't count against maxTime\n const startTime = performance.now();\n const limits = {\n minTime: min,\n maxTime: max,\n targetConfidence: target,\n startTime,\n };\n await collectAdaptive(runner, bench, opts, params, allSamples, limits);\n\n const samplesNs = allSamples.map(s => s * msToNs);\n const convergence = checkConvergence(samplesNs);\n return buildResults(\n allSamples,\n startTime,\n convergence,\n bench.name,\n warmup,\n hrtimeStart,\n );\n}\n\n/** Scale window size inversely with execution time -- fast ops need more samples. */\nfunction getWindowSize(samples: number[]): number {\n if (samples.length < 20) return windowSize;\n\n const recentMs = samples.slice(-20).map(s => s / msToNs);\n const recentMedian = median(recentMs);\n\n if (recentMedian < 0.01) return 200; // <10μs\n if (recentMedian < 0.1) return 100; // <100μs\n if (recentMedian < 1) return 50; // <1ms\n if (recentMedian < 10) return 30; // <10ms\n return 20; // >10ms\n}\n\n/** Convert stability metrics to a convergence result with confidence score. */\nfunction buildConvergence(metrics: Metrics): ConvergenceResult {\n const { medianDrift, impactDrift, medianStable, impactStable } = metrics;\n if (medianStable && impactStable)\n return {\n converged: true,\n confidence: 100,\n reason: \"Stable performance pattern\",\n };\n const raw =\n (1 - medianDrift / stability) * 50 + (1 - impactDrift / stability) * 50;\n const confidence = Math.max(0, Math.min(100, raw));\n const reason =\n medianDrift > impactDrift\n ? `Median drifting: ${(medianDrift * 100).toFixed(1)}%`\n : `Outlier impact changing: ${(impactDrift * 100).toFixed(1)}%`;\n return { converged: false, confidence, reason };\n}\n\n/** Compare median and outlier-impact drift between recent and previous windows. */\nfunction getStability(samples: number[], windowSize: number): Metrics {\n const toMs = (s: number) => s / msToNs;\n const recentMs = samples.slice(-windowSize).map(toMs);\n const previousMs = samples.slice(-windowSize * 2, -windowSize).map(toMs);\n\n const medianRecent = median(recentMs);\n const medianPrevious = median(previousMs);\n const medianDrift = Math.abs(medianRecent - medianPrevious) / medianPrevious;\n\n const impactRecent = outlierImpactRatio(recentMs);\n const impactPrevious = outlierImpactRatio(previousMs);\n const impactDrift = Math.abs(impactRecent - impactPrevious);\n\n const medianStable = medianDrift < stability;\n const impactStable = impactDrift < stability;\n return { medianDrift, impactDrift, medianStable, impactStable };\n}\n\n/** Collect the initial batch (warmup + settle), returning warmup samples. */\nasync function collectInitial<T>(\n runner: BenchRunner,\n bench: BenchmarkSpec<T>,\n opts: RunnerOptions,\n params: T | undefined,\n allSamples: number[],\n): Promise<{ warmup?: number[]; startTime?: number }> {\n const batchOpts = {\n ...(opts as any),\n maxTime: initialBatch,\n maxIterations: undefined,\n };\n const results = await runner.runBench(bench, batchOpts, params);\n appendSamples(results[0], allSamples);\n return { warmup: results[0].warmupSamples, startTime: results[0].startTime };\n}\n\n/** Collect batches until convergence or timeout, with progress logging. */\nasync function collectAdaptive<T>(\n runner: BenchRunner,\n bench: BenchmarkSpec<T>,\n opts: RunnerOptions,\n params: T | undefined,\n allSamples: number[],\n limits: {\n minTime: number;\n maxTime: number;\n targetConfidence: number;\n startTime: number;\n },\n): Promise<void> {\n const { minTime, maxTime, targetConfidence, startTime } = limits;\n let lastLog = 0;\n while (performance.now() - startTime < maxTime) {\n const samplesNs = allSamples.map(s => s * msToNs);\n const convergence = checkConvergence(samplesNs);\n const elapsed = performance.now() - startTime;\n\n lastLog = logProgress(bench.name, convergence, elapsed, lastLog);\n if (shouldStop(convergence, targetConfidence, elapsed, minTime)) break;\n\n const batch = {\n ...(opts as any),\n maxTime: continueBatch,\n maxIterations: continueIterations,\n skipWarmup: true,\n };\n const results = await runner.runBench(bench, batch, params);\n appendSamples(results[0], allSamples);\n }\n process.stderr.write(\"\\r\" + \" \".repeat(60) + \"\\r\");\n}\n\n/** Build final MeasuredResults from collected samples and convergence state. */\nfunction buildResults(\n samples: number[],\n elapsedStart: number,\n convergence: ConvergenceResult,\n name: string,\n warmupSamples?: number[],\n startTime?: number,\n): MeasuredResults[] {\n const totalTime = (performance.now() - elapsedStart) / 1000;\n const time = computeStats(samples);\n return [\n { name, samples, warmupSamples, time, totalTime, startTime, convergence },\n ];\n}\n\n/** Append samples one-by-one to avoid stack overflow from spread on large arrays. */\nfunction appendSamples(result: MeasuredResults, samples: number[]): void {\n if (!result.samples?.length) return;\n for (const sample of result.samples) samples.push(sample);\n}\n\n/** Log adaptive sampling progress at ~1s intervals. */\nfunction logProgress(\n name: string,\n convergence: ConvergenceResult,\n elapsed: number,\n lastLog: number,\n): number {\n if (elapsed - lastLog <= 1000) return lastLog;\n const sec = (elapsed / 1000).toFixed(1);\n const conf = convergence.confidence.toFixed(0);\n process.stderr.write(`\\r◊ ${name}: ${conf}% confident (${sec}s) `);\n return elapsed;\n}\n\n/** @return true if convergence target met, or minTime elapsed with fallback confidence. */\nfunction shouldStop(\n convergence: ConvergenceResult,\n target: number,\n elapsed: number,\n minElapsed: number,\n): boolean {\n if (convergence.converged && convergence.confidence >= target) return true;\n return (\n elapsed >= minElapsed &&\n convergence.confidence >= Math.max(target, fallbackThreshold)\n );\n}\n","import type { BenchmarkSpec } from \"./BenchmarkSpec.ts\";\nimport type { MeasuredResults } from \"./MeasuredResults.ts\";\n\n/** Benchmark execution strategy that collects timing samples. */\nexport interface BenchRunner {\n runBench<T = unknown>(\n benchmark: BenchmarkSpec<T>,\n options: RunnerOptions,\n params?: T,\n ): Promise<MeasuredResults[]>;\n}\n\n/** Configuration for benchmark execution: timing limits, warmup, profiling, and V8 options. */\nexport interface RunnerOptions {\n /** Minimum time to run each benchmark (milliseconds) */\n minTime?: number;\n /** Maximum time to run each benchmark - ignored by mitata (milliseconds) */\n maxTime?: number;\n /** Maximum iterations per benchmark - ignored by TinyBench */\n maxIterations?: number;\n /** Warmup iterations before measurement (default: 0) */\n warmup?: number;\n /** Warmup time before measurement (milliseconds) */\n warmupTime?: number;\n /** Warmup samples - mitata only, for reducing test time */\n warmupSamples?: number;\n /** Warmup threshold - mitata only (nanoseconds) */\n warmupThreshold?: number;\n /** Minimum samples required - mitata only */\n minSamples?: number;\n /** Force GC after each iteration (requires --expose-gc) */\n gcForce?: boolean;\n /** Trace V8 optimization tiers (requires --allow-natives-syntax) */\n traceOpt?: boolean;\n /** Post-warmup settle time in ms for V8 background compilation (0 to skip) */\n pauseWarmup?: number;\n /** Iterations before first pause (then pauseInterval applies) */\n pauseFirst?: number;\n /** Iterations between pauses for V8 optimization (0 to disable) */\n pauseInterval?: number;\n /** Pause duration in ms for V8 optimization */\n pauseDuration?: number;\n /** Collect GC stats via --trace-gc-nvp (requires worker mode) */\n gcStats?: boolean;\n /** Allocation sampling attribution */\n alloc?: boolean;\n /** Allocation sampling interval in bytes */\n allocInterval?: number;\n /** Allocation sampling stack depth */\n allocDepth?: number;\n /** V8 CPU time sampling */\n profile?: boolean;\n /** CPU sampling interval in microseconds (default 1000) */\n profileInterval?: number;\n /** Collect per-function execution counts via V8 precise coverage */\n callCounts?: boolean;\n}\n\n/** Invoke the benchmark function, forwarding setup params. */\nexport function executeBenchmark<T>(\n benchmark: BenchmarkSpec<T>,\n params?: T,\n): void {\n (benchmark.fn as (params?: T) => void)(params);\n}\n","import { getHeapStatistics } from \"node:v8\";\nimport type { BenchmarkSpec } from \"./BenchmarkSpec.ts\";\nimport type { BenchRunner, RunnerOptions } from \"./BenchRunner.ts\";\nimport { executeBenchmark } from \"./BenchRunner.ts\";\nimport type {\n MeasuredResults,\n OptStatusInfo,\n PausePoint,\n} from \"./MeasuredResults.ts\";\nimport {\n analyzeOptStatus,\n computeStats,\n createOptStatusGetter,\n gcFunction,\n} from \"./SampleStats.ts\";\n\ntype CollectParams<T = unknown> = {\n benchmark: BenchmarkSpec<T>;\n maxTime: number;\n maxIterations: number;\n warmup: number;\n params?: T;\n skipWarmup?: boolean;\n traceOpt?: boolean;\n pauseWarmup?: number;\n pauseFirst?: number;\n pauseInterval?: number;\n pauseDuration?: number;\n};\n\ntype CollectResult = {\n samples: number[];\n warmupSamples: number[];\n heapGrowth: number;\n heapSamples: number[];\n startTime: number;\n optStatus?: OptStatusInfo;\n optSamples?: number[];\n pausePoints: PausePoint[];\n};\n\ntype SampleArrays = {\n samples: number[];\n heapSamples: number[];\n optStatuses: number[];\n pausePoints: PausePoint[];\n};\n\nconst defaultCollectOptions = {\n maxTime: 5000,\n maxIterations: 1000000,\n warmup: 0,\n traceOpt: false,\n pauseWarmup: 0,\n};\n\n/**\n * Timing-based runner that collects samples within time/iteration limits.\n * Handles warmup, heap tracking, V8 optimization tracing, and periodic pauses.\n */\nexport class TimingRunner implements BenchRunner {\n async runBench<T = unknown>(\n benchmark: BenchmarkSpec<T>,\n options: RunnerOptions,\n params?: T,\n ): Promise<MeasuredResults[]> {\n const opts = { ...defaultCollectOptions, ...(options as any) };\n const collected = await collectSamples({ benchmark, params, ...opts });\n return [buildMeasuredResults(benchmark.name, collected)];\n }\n}\n\n/** Collect timing samples with warmup, heap tracking, and optional V8 opt tracing. */\nasync function collectSamples<T>(\n config: CollectParams<T>,\n): Promise<CollectResult> {\n if (!config.maxIterations && !config.maxTime) {\n throw new Error(`At least one of maxIterations or maxTime must be set`);\n }\n const warmupSamples = config.skipWarmup ? [] : await runWarmup(config);\n const heapBefore = process.memoryUsage().heapUsed;\n const { samples, heapSamples, optStatuses, pausePoints, startTime } =\n await runSampleLoop(config);\n if (samples.length === 0)\n throw new Error(\n `No samples collected for benchmark: ${config.benchmark.name}`,\n );\n const heapAfter = process.memoryUsage().heapUsed;\n const heapGrowth =\n Math.max(0, heapAfter - heapBefore) / 1024 / samples.length;\n const optStatus = config.traceOpt\n ? analyzeOptStatus(samples, optStatuses)\n : undefined;\n const optSamples =\n config.traceOpt && optStatuses.length > 0 ? optStatuses : undefined;\n return {\n samples,\n warmupSamples,\n heapGrowth,\n heapSamples,\n startTime,\n optStatus,\n optSamples,\n pausePoints,\n };\n}\n\n/** Assemble CollectResult into a MeasuredResults record. */\nfunction buildMeasuredResults(\n name: string,\n collected: CollectResult,\n): MeasuredResults {\n const { samples, warmupSamples, heapSamples } = collected;\n const { optStatus, optSamples, pausePoints, heapGrowth, startTime } =\n collected;\n const time = computeStats(samples);\n const heapSize = { avg: heapGrowth, min: heapGrowth, max: heapGrowth };\n return {\n name,\n samples,\n warmupSamples,\n heapSamples,\n time,\n heapSize,\n startTime,\n optStatus,\n optSamples,\n pausePoints,\n };\n}\n\n/**\n * Run warmup iterations with gc + settle time for V8 optimization. Returns warmup timings.\n *\n * V8 has 4 compilation tiers: Ignition (interpreter) ==> Sparkplug (baseline) ==>\n * Maglev (mid-tier optimizer) ==> TurboFan (full optimizer). Tiering thresholds:\n * - Ignition ==> Sparkplug: 8 invocations\n * - Sparkplug ==> Maglev: 500 invocations\n * - Maglev ==> TurboFan: 6000 invocations\n *\n * Optimization compilation happens on background threads and requires idle time\n * on the main thread to complete. Without sufficient warmup + settle time,\n * benchmarks exhibit bimodal timing: slow Sparkplug samples (~30% slower) mixed\n * with fast optimized samples.\n *\n * The warmup iterations trigger the optimization decision, then settle time\n * provides idle time for background compilation to finish before measurement.\n *\n * @see https://v8.dev/blog/sparkplug\n * @see https://v8.dev/blog/maglev\n * @see https://v8.dev/blog/background-compilation\n */\nasync function runWarmup<T>(config: CollectParams<T>): Promise<number[]> {\n const gc = gcFunction();\n const samples = new Array<number>(config.warmup);\n for (let i = 0; i < config.warmup; i++) {\n const start = performance.now();\n executeBenchmark(config.benchmark, config.params);\n samples[i] = performance.now() - start;\n }\n gc();\n if (config.pauseWarmup) {\n await new Promise(r => setTimeout(r, config.pauseWarmup));\n gc();\n }\n return samples;\n}\n\n/** Collect timing samples with optional periodic pauses for V8 background compilation to complete. */\nasync function runSampleLoop<T>(\n config: CollectParams<T>,\n): Promise<SampleArrays & { startTime: number }> {\n const { maxTime, maxIterations, pauseFirst } = config;\n const { pauseInterval = 0, pauseDuration = 100 } = config;\n const getOptStatus = config.traceOpt ? createOptStatusGetter() : undefined;\n const trackOpt = !!getOptStatus;\n const estimated = maxIterations || Math.ceil(maxTime / 0.1);\n const arrays = createSampleArrays(estimated, trackOpt);\n\n let count = 0;\n let elapsed = 0;\n let totalPauseTime = 0;\n const startTime = Number(process.hrtime.bigint() / 1000n);\n const loopStart = performance.now();\n\n while (\n (!maxIterations || count < maxIterations) &&\n (!maxTime || elapsed < maxTime)\n ) {\n const start = performance.now();\n executeBenchmark(config.benchmark, config.params);\n const end = performance.now();\n arrays.samples[count] = end - start;\n arrays.heapSamples[count] = getHeapStatistics().used_heap_size;\n if (getOptStatus)\n arrays.optStatuses[count] = getOptStatus(config.benchmark.fn);\n count++;\n\n if (shouldPause(count, pauseFirst, pauseInterval)) {\n const sampleIndex = count - 1;\n arrays.pausePoints.push({ sampleIndex, durationMs: pauseDuration });\n const pauseStart = performance.now();\n await new Promise(r => setTimeout(r, pauseDuration));\n totalPauseTime += performance.now() - pauseStart;\n }\n elapsed = performance.now() - loopStart - totalPauseTime;\n }\n\n trimArrays(arrays, count, trackOpt);\n return { ...arrays, startTime };\n}\n\n/** Pre-allocate sample arrays to reduce GC pressure during measurement. */\nfunction createSampleArrays(n: number, trackOpt: boolean): SampleArrays {\n const arr = () => new Array<number>(n);\n return {\n samples: arr(),\n heapSamples: arr(),\n optStatuses: trackOpt ? arr() : [],\n pausePoints: [],\n };\n}\n\n/** @return true if this iteration should pause for V8 background compilation. */\nfunction shouldPause(\n iter: number,\n first: number | undefined,\n interval: number,\n): boolean {\n if (first !== undefined && iter === first) return true;\n if (interval <= 0) return false;\n if (first === undefined) return iter % interval === 0;\n return (iter - first) % interval === 0;\n}\n\n/** Trim pre-allocated arrays to the actual sample count. */\nfunction trimArrays(\n arrays: SampleArrays,\n count: number,\n trackOpt: boolean,\n): void {\n arrays.samples.length = arrays.heapSamples.length = count;\n if (trackOpt) arrays.optStatuses.length = count;\n}\n","import type { BenchRunner } from \"./BenchRunner.ts\";\nimport { TimingRunner } from \"./TimingRunner.ts\";\n\nexport type KnownRunner = \"timing\";\n\n/** Create a benchmark runner by name. */\nexport async function createRunner(_name: KnownRunner): Promise<BenchRunner> {\n return new TimingRunner();\n}\n","import { prepareBenchFn } from \"../matrix/BenchMatrix.ts\";\nimport { loadCaseData, loadCasesModule } from \"../matrix/CaseLoader.ts\";\nimport { loadVariant } from \"../matrix/VariantLoader.ts\";\nimport {\n type AdaptiveOptions,\n createAdaptiveWrapper,\n} from \"./AdaptiveWrapper.ts\";\nimport type { BenchmarkFunction } from \"./BenchmarkSpec.ts\";\nimport type { BenchRunner, RunnerOptions } from \"./BenchRunner.ts\";\nimport { createRunner, type KnownRunner } from \"./CreateRunner.ts\";\n\nexport const msToNs = 1e6;\n\n/** Get named or default export from module, throw if not a function */\n// biome-ignore lint/complexity/noBannedTypes: generic function constraint\nexport function getModuleExport<T extends Function = Function>(\n module: any,\n exportName: string | undefined,\n modulePath: string,\n): T {\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 as T;\n}\n\n/** Import a benchmark function from a module, optionally running a setup export */\nexport async function importBenchFn(\n modulePath: string,\n exportName: string | undefined,\n setupExportName: string | undefined,\n params: unknown,\n): Promise<{ fn: BenchmarkFunction; params: unknown }> {\n const module = await import(modulePath);\n const fn = getModuleExport<BenchmarkFunction>(module, exportName, modulePath);\n if (!setupExportName) return { fn, params };\n\n const setup = getModuleExport<BenchmarkFunction>(\n module,\n setupExportName,\n modulePath,\n );\n return { fn, params: await setup(params) };\n}\n\n/** Resolve a matrix variant to a benchmark function (shared by orchestrator and worker). */\nexport async function resolveVariantFn(params: {\n variantDir: string;\n variantId: string;\n caseId?: string;\n caseData?: unknown;\n casesModule?: string;\n}): Promise<{ fn: BenchmarkFunction; params: undefined }> {\n let { caseData } = params;\n if (params.casesModule && params.caseId) {\n const cases = await loadCasesModule(params.casesModule);\n caseData = (await loadCaseData(cases, params.caseId)).data;\n }\n const variant = await loadVariant(params.variantDir, params.variantId);\n const fn = await prepareBenchFn(variant, caseData);\n return { fn, params: undefined };\n}\n\n/** Create runner, wrapping with adaptive sampling if options.adaptive is set */\nexport async function createBenchRunner(\n runnerName: KnownRunner,\n options: RunnerOptions,\n): Promise<BenchRunner> {\n const base = await createRunner(runnerName);\n if (\"adaptive\" in options && options.adaptive) {\n return createAdaptiveWrapper(base, options as AdaptiveOptions);\n }\n return base;\n}\n","/** Toggle for worker process timing logs (manual, not exposed as CLI flag) */\nexport const debugWorkerTiming = false;\n\n/** Current time in ms, or 0 when debug timing is off (zero-cost no-op) */\nexport function getPerfNow(): number {\n return debugWorkerTiming ? performance.now() : 0;\n}\n\n/** Elapsed ms between marks, or 0 when debug timing is off */\nexport function getElapsed(startMark: number, endMark?: number): number {\n if (!debugWorkerTiming) return 0;\n const end = endMark ?? performance.now();\n return end - startMark;\n}\n","import { type ChildProcess, fork } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\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 { RunnerOptions } from \"./BenchRunner.ts\";\nimport type { KnownRunner } from \"./CreateRunner.ts\";\nimport { aggregateGcStats, type GcEvent, parseGcLine } from \"./GcStats.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\";\nimport type {\n ErrorMessage,\n ResultMessage,\n RunMessage,\n} from \"./WorkerScript.ts\";\n\n/** Parameters for running a matrix variant */\nexport interface RunMatrixVariantParams {\n variantDir: string;\n variantId: string;\n caseId: string;\n caseData?: unknown;\n casesModule?: string;\n runner: KnownRunner;\n options: RunnerOptions;\n useWorker?: boolean;\n}\n\ninterface RunBenchmarkParams<T = unknown> {\n spec: BenchmarkSpec<T>;\n runner: KnownRunner;\n options: RunnerOptions;\n useWorker?: boolean;\n params?: T;\n}\n\nconst logTiming = debugWorkerTiming\n ? (message: string) => console.log(`[RunnerOrchestrator] ${message}`)\n : () => {};\n\n/** Run a benchmark spec, optionally in an isolated worker process for profiling support. */\nexport async function runBenchmark<T = unknown>({\n spec,\n runner,\n options,\n useWorker = false,\n params,\n}: RunBenchmarkParams<T>): Promise<MeasuredResults[]> {\n if (!useWorker) {\n const resolved = spec.modulePath\n ? await resolveModuleSpec(spec, params)\n : { spec, params };\n const benchRunner = await createBenchRunner(runner, options);\n return benchRunner.runBench(resolved.spec, options, resolved.params);\n }\n\n const msg = createRunMessage(spec, runner, options, params);\n return runWorkerWithMessage(spec.name, options, msg);\n}\n\n/** Run a matrix variant benchmark, directly or in a worker. */\nexport async function runMatrixVariant(\n params: RunMatrixVariantParams,\n): Promise<MeasuredResults[]> {\n const { variantId, caseId, runner, options, useWorker = true } = params;\n const name = `${variantId}/${caseId}`;\n\n if (!useWorker) return runMatrixVariantDirect(params, name);\n\n const { variantDir, caseData, casesModule } = params;\n const message: RunMessage = {\n type: \"run\",\n spec: { name } as BenchmarkSpec,\n runnerName: runner,\n options,\n variantDir,\n variantId,\n caseId,\n caseData,\n casesModule,\n };\n return runWorkerWithMessage(name, options, message);\n}\n\n/** Resolve modulePath/exportName to a real function for non-worker mode */\nasync function resolveModuleSpec<T>(\n spec: BenchmarkSpec<T>,\n params: T | undefined,\n): Promise<{ spec: BenchmarkSpec<T>; params: T | undefined }> {\n const { modulePath, exportName, setupExportName } = spec;\n const imported = await importBenchFn(\n modulePath!,\n exportName,\n setupExportName,\n params,\n );\n const fn = imported.fn as BenchmarkFunction<T>;\n return { spec: { ...spec, fn }, params: imported.params as T | undefined };\n}\n\n/** Serialize a BenchmarkSpec into a worker-safe message (modulePath or fnCode) */\nfunction createRunMessage<T>(\n spec: BenchmarkSpec<T>,\n runnerName: KnownRunner,\n options: RunnerOptions,\n params?: T,\n): RunMessage {\n const { fn, ...rest } = spec;\n const message: RunMessage = {\n type: \"run\",\n spec: rest as BenchmarkSpec,\n runnerName,\n options,\n params,\n };\n if (spec.modulePath) {\n message.modulePath = spec.modulePath;\n message.exportName = spec.exportName;\n if (spec.setupExportName) message.setupExportName = spec.setupExportName;\n } else {\n message.fnCode = fn.toString();\n }\n return message;\n}\n\n/** Run a benchmark in an isolated worker process with timeout and GC capture. */\nfunction runWorkerWithMessage(\n name: string,\n options: RunnerOptions,\n message: RunMessage,\n): Promise<MeasuredResults[]> {\n const startTime = getPerfNow();\n const collectGcStats = options.gcStats ?? false;\n logTiming(`Starting worker for ${name}`);\n\n return new Promise((resolve, reject) => {\n const gcEvents: GcEvent[] = [];\n const worker = spawnWorkerProcess(collectGcStats);\n if (collectGcStats && worker.stdout) setupGcCapture(worker, gcEvents);\n\n const timeoutId = setTimeout(() => {\n killWorker();\n reject(new Error(`Benchmark \"${name}\" timed out after 60 seconds`));\n }, 60000);\n\n function killWorker() {\n clearTimeout(timeoutId);\n if (!worker.killed) worker.kill(\"SIGTERM\");\n }\n\n worker.on(\"message\", (msg: ResultMessage | ErrorMessage) => {\n killWorker();\n if (msg.type === \"error\") {\n const error = new Error(`Benchmark \"${name}\" failed: ${msg.error}`);\n if (msg.stack) error.stack = msg.stack;\n return reject(error);\n }\n const elapsed = getElapsed(startTime).toFixed(1);\n logTiming(`Total worker time for ${name}: ${elapsed}ms`);\n const { results, heapProfile, timeProfile, coverage } = msg;\n attachProfilingData(\n results,\n gcEvents,\n heapProfile,\n timeProfile,\n coverage,\n );\n resolve(results);\n });\n worker.on(\"error\", (error: Error) => {\n killWorker();\n const msg = `Worker process failed for \"${name}\": ${error.message}`;\n reject(new Error(msg));\n });\n worker.on(\"exit\", (code: number | null) => {\n if (code !== 0 && code !== null) {\n killWorker();\n reject(new Error(`Worker exited with code ${code} for \"${name}\"`));\n }\n });\n\n worker.send(message);\n });\n}\n\n/** Run matrix variant in-process (no worker isolation) */\nasync function runMatrixVariantDirect(\n params: RunMatrixVariantParams,\n name: string,\n): Promise<MeasuredResults[]> {\n const { runner, options } = params;\n const { fn } = await resolveVariantFn(params);\n const benchRunner = await createBenchRunner(runner, options);\n return benchRunner.runBench({ name, fn }, options);\n}\n\n/** Spawn worker process with V8 flags */\nfunction spawnWorkerProcess(gcStats: boolean) {\n const workerPath = resolveWorkerPath();\n const execArgv = [\"--expose-gc\", \"--allow-natives-syntax\"];\n if (gcStats) execArgv.push(\"--trace-gc-nvp\");\n\n const env = { ...process.env, NODE_OPTIONS: \"\" };\n // silent mode captures stdout so we can parse --trace-gc-nvp output\n return fork(workerPath, [], {\n execArgv,\n silent: gcStats,\n env,\n serialization: \"advanced\",\n });\n}\n\n/** Capture and parse GC lines from worker stdout (--trace-gc-nvp). */\nfunction setupGcCapture(worker: ChildProcess, gcEvents: GcEvent[]): void {\n let buffer = \"\";\n worker.stdout!.on(\"data\", (data: Buffer) => {\n buffer += data.toString();\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\";\n for (const line of lines) {\n const event = parseGcLine(line);\n if (event) gcEvents.push(event);\n else if (line.trim()) process.stdout.write(line + \"\\n\");\n }\n });\n}\n\n/** Attach profiling data collected by the worker to each result. */\nfunction attachProfilingData(\n results: MeasuredResults[],\n gcEvents: GcEvent[] | undefined,\n heapProfile?: HeapProfile,\n timeProfile?: TimeProfile,\n coverage?: CoverageData,\n): void {\n const gcStats = gcEvents?.length ? aggregateGcStats(gcEvents) : undefined;\n const attach = <K extends keyof MeasuredResults>(\n key: K,\n value: MeasuredResults[K] | undefined,\n ) => {\n if (value) for (const r of results) r[key] = value;\n };\n attach(\"gcStats\", gcStats);\n attach(\"heapProfile\", heapProfile);\n attach(\"timeProfile\", timeProfile);\n attach(\"coverage\", coverage);\n}\n\n/** Resolve WorkerScript path for dev (.ts) or dist (.mjs) */\nfunction resolveWorkerPath(): string {\n const dir = import.meta.dirname!;\n const tsPath = path.join(dir, \"WorkerScript.ts\");\n if (existsSync(tsPath)) return tsPath;\n return path.join(dir, \"runners\", \"WorkerScript.mjs\");\n}\n","import type { RunnerOptions } from \"../runners/BenchRunner.ts\";\nimport type { MeasuredResults } from \"../runners/MeasuredResults.ts\";\nimport { runBatched } from \"../runners/MergeBatches.ts\";\nimport { runMatrixVariant } from \"../runners/RunnerOrchestrator.ts\";\nimport type {\n BenchMatrix,\n CaseResult,\n RunMatrixOptions,\n VariantResult,\n} from \"./BenchMatrix.ts\";\nimport {\n buildRunnerOptions,\n computeDeltaPercent,\n resolveCases,\n} from \"./BenchMatrix.ts\";\nimport type { CasesModule } from \"./CaseLoader.ts\";\nimport { loadCaseData } from \"./CaseLoader.ts\";\nimport { discoverVariants } from \"./VariantLoader.ts\";\n\ntype VariantArgs = Parameters<typeof runMatrixVariant>[0];\n\n/** Shared state for directory-based matrix execution */\ninterface DirMatrixContext<T> {\n matrix: BenchMatrix<T>;\n casesModule?: CasesModule<T>;\n baselineIds: string[];\n caseIds: string[];\n runnerOpts: RunnerOptions;\n batches: number;\n warmupBatch: boolean;\n useWorker: boolean;\n}\n\n/** Run matrix using variant files from a directory, each in a worker process */\nexport async function runMatrixWithDir<T>(\n matrix: BenchMatrix<T>,\n options: RunMatrixOptions,\n): Promise<{ name: string; variants: VariantResult[] }> {\n const allVariantIds = await discoverVariants(matrix.variantDir!);\n if (allVariantIds.length === 0) {\n throw new Error(`No variants found in ${matrix.variantDir}`);\n }\n const variantIds = options.filteredVariants ?? allVariantIds;\n\n const ctx = await createDirContext(matrix, options);\n const variants = await runDirVariants(variantIds, ctx);\n return { name: matrix.name, variants };\n}\n\n/** Create context for directory-based matrix execution */\nasync function createDirContext<T>(\n matrix: BenchMatrix<T>,\n options: RunMatrixOptions,\n): Promise<DirMatrixContext<T>> {\n const baselineIds = matrix.baselineDir\n ? await discoverVariants(matrix.baselineDir)\n : [];\n const { casesModule, caseIds } = await resolveCases(matrix, options);\n const runnerOpts = buildRunnerOptions(options);\n const { batches = 1, warmupBatch = false, useWorker = true } = options;\n return {\n matrix,\n casesModule,\n baselineIds,\n caseIds,\n runnerOpts,\n batches,\n warmupBatch,\n useWorker,\n };\n}\n\n/** Run all variants sequentially, collecting per-case results */\nasync function runDirVariants<T>(\n variantIds: string[],\n ctx: DirMatrixContext<T>,\n): Promise<VariantResult[]> {\n const variants: VariantResult[] = [];\n for (const id of variantIds) {\n const cases = await runDirVariantCases(id, ctx);\n variants.push({ id, cases });\n }\n return variants;\n}\n\n/** Run all cases for a single variant */\nasync function runDirVariantCases<T>(\n variantId: string,\n ctx: DirMatrixContext<T>,\n): Promise<CaseResult[]> {\n const { matrix, casesModule, caseIds, runnerOpts, batches } = ctx;\n const cases: CaseResult[] = [];\n\n for (const caseId of caseIds) {\n const caseData = matrix.cases && !matrix.casesModule ? caseId : undefined;\n const variantArgs: VariantArgs = {\n variantDir: matrix.variantDir!,\n variantId,\n caseId,\n caseData,\n casesModule: matrix.casesModule,\n runner: \"timing\" as const,\n options: runnerOpts,\n useWorker: ctx.useWorker,\n };\n const baselineArgs =\n matrix.baselineDir && ctx.baselineIds.includes(variantId)\n ? { ...variantArgs, variantDir: matrix.baselineDir! }\n : undefined;\n\n const { metadata } = await loadCaseData(casesModule, caseId);\n const { measured, baseline } =\n batches > 1\n ? await runCaseBatched(variantArgs, baselineArgs, ctx)\n : await runCaseSingle(variantArgs, baselineArgs);\n const deltaPercent = baseline\n ? computeDeltaPercent(baseline, measured)\n : undefined;\n cases.push({ caseId, measured, metadata, baseline, deltaPercent });\n }\n return cases;\n}\n\n/** Run a batched measurement for a case, alternating current/baseline order. */\nasync function runCaseBatched<T>(\n variantArgs: VariantArgs,\n baselineArgs: VariantArgs | undefined,\n ctx: DirMatrixContext<T>,\n): Promise<{ measured: MeasuredResults; baseline?: MeasuredResults }> {\n const runCurrent = async () => (await runMatrixVariant(variantArgs))[0];\n const runBase = baselineArgs\n ? async () => (await runMatrixVariant(baselineArgs))[0]\n : undefined;\n const { results, baseline } = await runBatched(\n [runCurrent],\n runBase,\n ctx.batches,\n ctx.warmupBatch,\n );\n return { measured: results[0], baseline };\n}\n\n/** Run a single unbatched measurement for a case. */\nasync function runCaseSingle(\n variantArgs: VariantArgs,\n baselineArgs: VariantArgs | undefined,\n): Promise<{ measured: MeasuredResults; baseline?: MeasuredResults }> {\n const [measured] = await runMatrixVariant(variantArgs);\n const baseline = baselineArgs\n ? (await runMatrixVariant(baselineArgs))[0]\n : undefined;\n return { measured, baseline };\n}\n","import { TimingRunner } from \"../runners/TimingRunner.ts\";\nimport type {\n BenchMatrix,\n CaseResult,\n RunMatrixOptions,\n VariantResult,\n} from \"./BenchMatrix.ts\";\nimport {\n buildRunnerOptions,\n prepareBenchFn,\n resolveCases,\n} from \"./BenchMatrix.ts\";\nimport { loadCaseData } from \"./CaseLoader.ts\";\n\n/** Run matrix with in-memory variant functions (no worker isolation) */\nexport async function runMatrixInline<T>(\n matrix: BenchMatrix<T>,\n options: RunMatrixOptions,\n): Promise<{ name: string; variants: VariantResult[] }> {\n if (matrix.baselineDir)\n throw new Error(\n \"BenchMatrix with inline 'variants' cannot use 'baselineDir'. Use 'variantDir' instead.\",\n );\n\n const { casesModule, caseIds } = await resolveCases(matrix, options);\n const runner = new TimingRunner();\n const runnerOpts = buildRunnerOptions(options);\n\n const allEntries = Object.entries(matrix.variants!);\n const { filteredVariants } = options;\n const variantEntries = filteredVariants\n ? allEntries.filter(([id]) => filteredVariants.includes(id))\n : allEntries;\n\n const variants: VariantResult[] = [];\n for (const [variantId, variant] of variantEntries) {\n const cases: CaseResult[] = [];\n for (const caseId of caseIds) {\n const loaded = await loadCaseData(casesModule, caseId);\n const data = casesModule || matrix.cases ? loaded.data : (undefined as T);\n const fn = await prepareBenchFn(variant, data);\n const spec = { name: variantId, fn };\n const [measured] = await runner.runBench(spec, runnerOpts);\n cases.push({ caseId, measured, metadata: loaded.metadata });\n }\n variants.push({ id: variantId, cases });\n }\n\n return { name: matrix.name, variants };\n}\n","import type { RunnerOptions } from \"../runners/BenchRunner.ts\";\nimport type { MeasuredResults } from \"../runners/MeasuredResults.ts\";\nimport { average } from \"../stats/StatisticalUtils.ts\";\nimport type { CasesModule } from \"./CaseLoader.ts\";\nimport { loadCasesModule } from \"./CaseLoader.ts\";\nimport { runMatrixWithDir } from \"./MatrixDirRunner.ts\";\nimport { runMatrixInline } from \"./MatrixInlineRunner.ts\";\n\n/** Stateless variant - called each iteration with case data */\nexport type VariantFn<T = unknown> = (caseData: T) => void;\n\n/** Stateful variant - setup once, run many */\nexport interface StatefulVariant<T = unknown, S = unknown> {\n setup: (caseData: T) => S | Promise<S>;\n run: (state: S) => void;\n}\n\n/** A variant is either a plain function or a stateful setup+run pair */\nexport type Variant<T = unknown, S = unknown> =\n | VariantFn<T>\n | StatefulVariant<T, S>;\n\n/** Variant with any state type, allowing mixed variants in a matrix */\nexport type AnyVariant<T = unknown> = VariantFn<T> | StatefulVariant<T, any>;\n\n/** Case data and optional metadata returned by a cases module */\nexport interface LoadedCase<T = unknown> {\n data: T;\n metadata?: Record<string, unknown>;\n}\n\n/** Default runner settings applied to all matrix benchmarks */\nexport interface MatrixDefaults {\n warmup?: number;\n maxTime?: number;\n iterations?: number;\n}\n\n/** Configuration for a cases x variants benchmark matrix */\nexport interface BenchMatrix<T = unknown> {\n name: string;\n variantDir?: string;\n variants?: Record<string, AnyVariant<T>>;\n cases?: string[];\n casesModule?: string;\n baselineDir?: string;\n baselineVariant?: string;\n defaults?: MatrixDefaults;\n}\n\n/** Named collection of benchmark matrices */\nexport interface MatrixSuite {\n name: string;\n matrices: BenchMatrix<any>[];\n}\n\n/** Results for a single variant across all cases */\nexport interface VariantResult {\n id: string;\n cases: CaseResult[];\n}\n\n/** Results for a single (variant, case) pair */\nexport interface CaseResult {\n caseId: string;\n measured: MeasuredResults;\n metadata?: Record<string, unknown>;\n baseline?: MeasuredResults;\n deltaPercent?: number;\n}\n\n/** Aggregated results from running a benchmark matrix */\nexport interface MatrixResults {\n name: string;\n variants: VariantResult[];\n}\n\n/** Options for {@link runMatrix} */\nexport interface RunMatrixOptions {\n /** Maximum iterations per benchmark */\n iterations?: number;\n /** Maximum time in ms per benchmark */\n maxTime?: number;\n /** Number of warmup iterations before measurement */\n warmup?: number;\n /** Use worker process isolation (default: true for variantDir) */\n useWorker?: boolean;\n /** Number of interleaved batches for baseline comparison */\n batches?: number;\n /** Include first batch in results (normally dropped for OS cache warmup) */\n warmupBatch?: boolean;\n /** Run only these cases (from --filter) */\n filteredCases?: string[];\n /** Run only these variants (from --filter) */\n filteredVariants?: string[];\n /** Force garbage collection between iterations */\n gcForce?: boolean;\n /** Track V8 optimization/deoptimization events */\n traceOpt?: boolean;\n /** Pause duration in ms before warmup begins */\n pauseWarmup?: number;\n /** Pause duration in ms before first measurement */\n pauseFirst?: number;\n /** Pause every N iterations during measurement */\n pauseInterval?: number;\n /** Duration of each pause in ms */\n pauseDuration?: number;\n /** Collect GC statistics via --trace-gc-nvp */\n gcStats?: boolean;\n /** Enable heap allocation profiling */\n alloc?: boolean;\n /** Heap sampling interval in bytes */\n allocInterval?: number;\n /** Maximum stack depth for allocation traces */\n allocDepth?: number;\n /** Enable CPU time profiling */\n profile?: boolean;\n /** CPU profiling sample interval in microseconds */\n profileInterval?: number;\n /** Track function call counts via V8 coverage */\n callCounts?: boolean;\n}\n\n/** Run a BenchMatrix with inline variants or variantDir */\nexport async function runMatrix<T>(\n matrix: BenchMatrix<T>,\n options: RunMatrixOptions = {},\n): Promise<MatrixResults> {\n if (matrix.baselineDir && matrix.baselineVariant)\n throw new Error(\n \"BenchMatrix cannot have both 'baselineDir' and 'baselineVariant'\",\n );\n if (!matrix.variantDir && !matrix.variants)\n throw new Error(\"BenchMatrix requires either 'variants' or 'variantDir'\");\n\n const effectiveOptions = { ...matrix.defaults, ...options };\n const result = matrix.variantDir\n ? await runMatrixWithDir(matrix, effectiveOptions)\n : await runMatrixInline(matrix, effectiveOptions);\n\n if (matrix.baselineVariant) {\n applyBaselineVariant(result.variants, matrix.baselineVariant);\n }\n return result;\n}\n\n/** Prepare a benchmark function from a variant, calling setup if stateful. */\nexport async function prepareBenchFn<T>(\n variant: Variant<T>,\n data: T,\n): Promise<() => void> {\n if (isStatefulVariant(variant)) {\n const state = await variant.setup(data);\n return () => variant.run(state);\n }\n return () => variant(data);\n}\n\n/** Type guard for StatefulVariant */\nexport function isStatefulVariant<T, S>(\n v: Variant<T, S>,\n): v is StatefulVariant<T, S> {\n return typeof v === \"object\" && \"setup\" in v && \"run\" in v;\n}\n\n/** Apply baselineVariant comparison - one variant is the reference for all others */\nexport function applyBaselineVariant(\n variants: VariantResult[],\n baselineVariantId: string,\n): void {\n const baselineVariant = variants.find(v => v.id === baselineVariantId);\n if (!baselineVariant) return;\n\n const baselineByCase = new Map(\n baselineVariant.cases.map(c => [c.caseId, c.measured]),\n );\n\n for (const variant of variants) {\n if (variant.id === baselineVariantId) continue;\n for (const cr of variant.cases) {\n const base = baselineByCase.get(cr.caseId);\n if (base) {\n cr.baseline = base;\n cr.deltaPercent = computeDeltaPercent(base, cr.measured);\n }\n }\n }\n}\n\n/** Load cases module and resolve filtered case IDs */\nexport async function resolveCases<T>(\n matrix: BenchMatrix<T>,\n options: RunMatrixOptions,\n): Promise<{ casesModule: CasesModule<T> | undefined; caseIds: string[] }> {\n const casesModule = matrix.casesModule\n ? await loadCasesModule<T>(matrix.casesModule)\n : undefined;\n const allCaseIds = casesModule?.cases ?? matrix.cases ?? [\"default\"];\n const caseIds = options.filteredCases ?? allCaseIds;\n return { casesModule, caseIds };\n}\n\n/** Map matrix options to runner options, applying defaults for maxTime and warmup */\nexport function buildRunnerOptions(opts: RunMatrixOptions): RunnerOptions {\n const {\n filteredCases,\n filteredVariants,\n useWorker,\n batches,\n warmupBatch,\n ...base\n } = opts;\n const { iterations, maxTime, warmup, ...rest } = base;\n return {\n maxIterations: iterations,\n maxTime: maxTime ?? (iterations ? undefined : 1000),\n warmup: warmup ?? 0,\n ...rest,\n };\n}\n\n/** Compute percentage change of current vs baseline mean */\nexport function computeDeltaPercent(\n base: MeasuredResults,\n cur: MeasuredResults,\n): number {\n const avg = average(base.samples);\n if (avg === 0) return 0;\n return ((average(cur.samples) - avg) / avg) * 100;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { Session } from "node:inspector/promises";
|
|
2
|
+
|
|
3
|
+
//#region src/runners/GcStats.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Aggregated GC statistics from V8 trace events.
|
|
6
|
+
* Node (--trace-gc-nvp) provides all fields; browser (CDP) provides counts, collected, and pause only.
|
|
7
|
+
*/
|
|
8
|
+
interface GcStats {
|
|
9
|
+
scavenges: number;
|
|
10
|
+
markCompacts: number;
|
|
11
|
+
/** Bytes freed by GC. */
|
|
12
|
+
totalCollected: number;
|
|
13
|
+
/** Total GC pause time in milliseconds. */
|
|
14
|
+
gcPauseTime: number;
|
|
15
|
+
/** Bytes allocated (Node only). */
|
|
16
|
+
totalAllocated?: number;
|
|
17
|
+
/** Bytes promoted to old generation (Node only). */
|
|
18
|
+
totalPromoted?: number;
|
|
19
|
+
/** Bytes survived in young generation (Node only). */
|
|
20
|
+
totalSurvived?: number;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/profiling/node/CoverageTypes.d.ts
|
|
24
|
+
/** Per-function execution counts from V8 Profiler.takePreciseCoverage (Node and CDP). */
|
|
25
|
+
interface CoverageData {
|
|
26
|
+
scripts: ScriptCoverage[];
|
|
27
|
+
}
|
|
28
|
+
/** Coverage data for a single script (file) */
|
|
29
|
+
interface ScriptCoverage {
|
|
30
|
+
url: string;
|
|
31
|
+
functions: FunctionCoverage[];
|
|
32
|
+
}
|
|
33
|
+
/** Coverage data for a single function within a script */
|
|
34
|
+
interface FunctionCoverage {
|
|
35
|
+
functionName: string;
|
|
36
|
+
ranges: CoverageRange[];
|
|
37
|
+
}
|
|
38
|
+
/** A byte-offset range within a function with its execution count */
|
|
39
|
+
interface CoverageRange {
|
|
40
|
+
startOffset: number;
|
|
41
|
+
endOffset: number;
|
|
42
|
+
count: number;
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/profiling/node/HeapSampler.d.ts
|
|
46
|
+
/** V8 call frame location within a profiled script */
|
|
47
|
+
interface CallFrame {
|
|
48
|
+
/** Function name (empty string for anonymous) */
|
|
49
|
+
functionName: string;
|
|
50
|
+
/** Script URL or file path */
|
|
51
|
+
url: string;
|
|
52
|
+
/** Zero-based line number */
|
|
53
|
+
lineNumber: number;
|
|
54
|
+
/** Zero-based column number */
|
|
55
|
+
columnNumber?: number;
|
|
56
|
+
}
|
|
57
|
+
/** Node in the V8 sampling heap profile tree */
|
|
58
|
+
interface ProfileNode {
|
|
59
|
+
/** Call site for this allocation node */
|
|
60
|
+
callFrame: CallFrame;
|
|
61
|
+
/** Bytes allocated directly at this node (not children) */
|
|
62
|
+
selfSize: number;
|
|
63
|
+
/** Unique node ID, links to {@link HeapSample.nodeId} */
|
|
64
|
+
id: number;
|
|
65
|
+
/** Child nodes in the call tree */
|
|
66
|
+
children?: ProfileNode[];
|
|
67
|
+
}
|
|
68
|
+
/** Individual heap allocation sample from V8's SamplingHeapProfiler */
|
|
69
|
+
interface HeapSample {
|
|
70
|
+
/** Links to {@link ProfileNode.id} for stack lookup */
|
|
71
|
+
nodeId: number;
|
|
72
|
+
/** Allocation size in bytes */
|
|
73
|
+
size: number;
|
|
74
|
+
/** Monotonically increasing, gives temporal ordering */
|
|
75
|
+
ordinal: number;
|
|
76
|
+
}
|
|
77
|
+
/** V8 sampling heap profile tree with optional per-allocation samples */
|
|
78
|
+
interface HeapProfile {
|
|
79
|
+
/** Root of the profile call tree */
|
|
80
|
+
head: ProfileNode;
|
|
81
|
+
/** Per-allocation samples, if collected */
|
|
82
|
+
samples?: HeapSample[];
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/profiling/node/TimeSampler.d.ts
|
|
86
|
+
/** V8 CPU profile node (flat array element, not tree) */
|
|
87
|
+
interface TimeProfileNode {
|
|
88
|
+
id: number;
|
|
89
|
+
callFrame: CallFrame;
|
|
90
|
+
hitCount?: number;
|
|
91
|
+
/** Child node IDs */
|
|
92
|
+
children?: number[];
|
|
93
|
+
}
|
|
94
|
+
/** V8 CPU profile returned by Profiler.stop */
|
|
95
|
+
interface TimeProfile {
|
|
96
|
+
nodes: TimeProfileNode[];
|
|
97
|
+
/** Microseconds */
|
|
98
|
+
startTime: number;
|
|
99
|
+
/** Microseconds */
|
|
100
|
+
endTime: number;
|
|
101
|
+
/** Node IDs sampled at each tick */
|
|
102
|
+
samples?: number[];
|
|
103
|
+
/** Microseconds between samples */
|
|
104
|
+
timeDeltas?: number[];
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region src/profiling/browser/BrowserProfiler.d.ts
|
|
108
|
+
/** Navigation timing metrics from the Performance API. */
|
|
109
|
+
interface NavTiming {
|
|
110
|
+
/** DOMContentLoaded time in ms */
|
|
111
|
+
domContentLoaded: number;
|
|
112
|
+
/** Load event time in ms */
|
|
113
|
+
loadEvent: number;
|
|
114
|
+
/** Largest Contentful Paint time in ms */
|
|
115
|
+
lcp?: number;
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/runners/NodeGC.d.ts
|
|
119
|
+
/** Individual GC event for visualization */
|
|
120
|
+
interface GcEvent {
|
|
121
|
+
/** Offset from collection start (ms) - can be negative for warmup GCs */
|
|
122
|
+
offset: number;
|
|
123
|
+
/** Duration of GC pause (ms) */
|
|
124
|
+
duration: number;
|
|
125
|
+
}
|
|
126
|
+
/** GC time measured by Node's performance hooks */
|
|
127
|
+
interface NodeGCTime {
|
|
128
|
+
inRun: number;
|
|
129
|
+
before: number;
|
|
130
|
+
after: number;
|
|
131
|
+
total: number;
|
|
132
|
+
collects: number;
|
|
133
|
+
/** Individual GC events during sample collection (for visualization) */
|
|
134
|
+
events: GcEvent[];
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
//#region src/runners/MeasuredResults.d.ts
|
|
138
|
+
/** Benchmark results: times in milliseconds, sizes in kilobytes */
|
|
139
|
+
interface MeasuredResults {
|
|
140
|
+
name: string;
|
|
141
|
+
/** Raw execution time samples for custom statistics */
|
|
142
|
+
samples: number[];
|
|
143
|
+
/** Warmup iteration timings (ms) - captured before gc/pause-warmup */
|
|
144
|
+
warmupSamples?: number[];
|
|
145
|
+
/** Raw allocation samples per iteration (KB) */
|
|
146
|
+
allocationSamples?: number[];
|
|
147
|
+
/** Heap size per sample (bytes) - used for charts */
|
|
148
|
+
heapSamples?: number[];
|
|
149
|
+
/** Execution time in milliseconds */
|
|
150
|
+
time: {
|
|
151
|
+
min: number;
|
|
152
|
+
max: number;
|
|
153
|
+
avg: number;
|
|
154
|
+
p25?: number;
|
|
155
|
+
p50: number;
|
|
156
|
+
p75: number;
|
|
157
|
+
p95?: number;
|
|
158
|
+
p99: number;
|
|
159
|
+
p999: number;
|
|
160
|
+
cv?: number;
|
|
161
|
+
mad?: number;
|
|
162
|
+
outlierRate?: number;
|
|
163
|
+
};
|
|
164
|
+
/** Heap size increase during test run (kilobytes) */
|
|
165
|
+
heapSize?: {
|
|
166
|
+
avg: number;
|
|
167
|
+
min: number;
|
|
168
|
+
max: number;
|
|
169
|
+
};
|
|
170
|
+
/** Time for explicit gc() call after test execution (ms), excludes in-run GC. */
|
|
171
|
+
gcTime?: {
|
|
172
|
+
avg: number;
|
|
173
|
+
min: number;
|
|
174
|
+
max: number;
|
|
175
|
+
};
|
|
176
|
+
/** Stop-the-world GC pause time (ms) via Node perf hooks (nodeObserveGC). */
|
|
177
|
+
nodeGcTime?: NodeGCTime;
|
|
178
|
+
/** Total time spent collecting samples (seconds) */
|
|
179
|
+
totalTime?: number;
|
|
180
|
+
/** Monotonic start time (μs, hrtime-based) for Perfetto trace alignment. */
|
|
181
|
+
startTime?: number;
|
|
182
|
+
/** Convergence information for adaptive mode */
|
|
183
|
+
convergence?: {
|
|
184
|
+
converged: boolean;
|
|
185
|
+
confidence: number;
|
|
186
|
+
reason: string;
|
|
187
|
+
};
|
|
188
|
+
/** V8 optimization tier tracking (requires --allow-natives-syntax) */
|
|
189
|
+
optStatus?: OptStatusInfo;
|
|
190
|
+
/** Per-sample V8 optimization status codes (for chart visualization) */
|
|
191
|
+
optSamples?: number[];
|
|
192
|
+
/** Points where pauses occurred for V8 optimization */
|
|
193
|
+
pausePoints?: PausePoint[];
|
|
194
|
+
/** Batch boundaries for block bootstrap (indices into samples where each batch starts) */
|
|
195
|
+
batchOffsets?: number[];
|
|
196
|
+
/** GC stats from V8's --trace-gc-nvp (requires --gc-stats and worker mode) */
|
|
197
|
+
gcStats?: GcStats;
|
|
198
|
+
/** Heap sampling allocation profile (requires --heap-sample and worker mode) */
|
|
199
|
+
heapProfile?: HeapProfile;
|
|
200
|
+
/** V8 CPU time sampling profile (requires --profile and worker mode) */
|
|
201
|
+
timeProfile?: TimeProfile;
|
|
202
|
+
/** Per-function execution counts (requires --call-counts) */
|
|
203
|
+
coverage?: CoverageData;
|
|
204
|
+
/** Navigation timings from page-load mode (one per iteration) */
|
|
205
|
+
navTimings?: NavTiming[];
|
|
206
|
+
}
|
|
207
|
+
/** Pause inserted during sample collection for V8 optimization settling. */
|
|
208
|
+
interface PausePoint {
|
|
209
|
+
sampleIndex: number;
|
|
210
|
+
durationMs: number;
|
|
211
|
+
}
|
|
212
|
+
/** V8 optimization tier distribution */
|
|
213
|
+
interface OptTierInfo {
|
|
214
|
+
count: number;
|
|
215
|
+
medianMs: number;
|
|
216
|
+
}
|
|
217
|
+
/** V8 optimization status summary */
|
|
218
|
+
interface OptStatusInfo {
|
|
219
|
+
/** Samples by tier name (e.g., "turbofan", "sparkplug") */
|
|
220
|
+
byTier: Record<string, OptTierInfo>;
|
|
221
|
+
/** Number of samples with deopt flag set */
|
|
222
|
+
deoptCount: number;
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
225
|
+
//#region src/runners/BenchmarkSpec.d.ts
|
|
226
|
+
/** Benchmark function with optional module path for worker-mode serialization. */
|
|
227
|
+
interface BenchmarkSpec<T = unknown> {
|
|
228
|
+
name: string;
|
|
229
|
+
fn: BenchmarkFunction<T>;
|
|
230
|
+
/** Path to module exporting the benchmark function (for worker mode) */
|
|
231
|
+
modulePath?: string;
|
|
232
|
+
/** Name of the exported function in the module (defaults to default export) */
|
|
233
|
+
exportName?: string;
|
|
234
|
+
/** Setup function export name - called once in worker, result passed to fn */
|
|
235
|
+
setupExportName?: string;
|
|
236
|
+
}
|
|
237
|
+
/** Benchmark function, optionally receiving setup parameters from the group. */
|
|
238
|
+
type BenchmarkFunction<T = unknown> = ((params: T) => void) | (() => void);
|
|
239
|
+
/** Group of benchmarks with shared setup and optional baseline. */
|
|
240
|
+
interface BenchGroup<T = unknown> {
|
|
241
|
+
name: string;
|
|
242
|
+
setup?: () => T | Promise<T>;
|
|
243
|
+
benchmarks: BenchmarkSpec<T>[];
|
|
244
|
+
baseline?: BenchmarkSpec<T>;
|
|
245
|
+
/** Metadata for reporting (e.g. lines of code). */
|
|
246
|
+
metadata?: Record<string, any>;
|
|
247
|
+
}
|
|
248
|
+
/** Named collection of benchmark groups. */
|
|
249
|
+
interface BenchSuite {
|
|
250
|
+
name: string;
|
|
251
|
+
groups: BenchGroup<any>[];
|
|
252
|
+
}
|
|
253
|
+
//#endregion
|
|
254
|
+
//#region src/runners/BenchRunner.d.ts
|
|
255
|
+
/** Configuration for benchmark execution: timing limits, warmup, profiling, and V8 options. */
|
|
256
|
+
interface RunnerOptions {
|
|
257
|
+
/** Minimum time to run each benchmark (milliseconds) */
|
|
258
|
+
minTime?: number;
|
|
259
|
+
/** Maximum time to run each benchmark - ignored by mitata (milliseconds) */
|
|
260
|
+
maxTime?: number;
|
|
261
|
+
/** Maximum iterations per benchmark - ignored by TinyBench */
|
|
262
|
+
maxIterations?: number;
|
|
263
|
+
/** Warmup iterations before measurement (default: 0) */
|
|
264
|
+
warmup?: number;
|
|
265
|
+
/** Warmup time before measurement (milliseconds) */
|
|
266
|
+
warmupTime?: number;
|
|
267
|
+
/** Warmup samples - mitata only, for reducing test time */
|
|
268
|
+
warmupSamples?: number;
|
|
269
|
+
/** Warmup threshold - mitata only (nanoseconds) */
|
|
270
|
+
warmupThreshold?: number;
|
|
271
|
+
/** Minimum samples required - mitata only */
|
|
272
|
+
minSamples?: number;
|
|
273
|
+
/** Force GC after each iteration (requires --expose-gc) */
|
|
274
|
+
gcForce?: boolean;
|
|
275
|
+
/** Trace V8 optimization tiers (requires --allow-natives-syntax) */
|
|
276
|
+
traceOpt?: boolean;
|
|
277
|
+
/** Post-warmup settle time in ms for V8 background compilation (0 to skip) */
|
|
278
|
+
pauseWarmup?: number;
|
|
279
|
+
/** Iterations before first pause (then pauseInterval applies) */
|
|
280
|
+
pauseFirst?: number;
|
|
281
|
+
/** Iterations between pauses for V8 optimization (0 to disable) */
|
|
282
|
+
pauseInterval?: number;
|
|
283
|
+
/** Pause duration in ms for V8 optimization */
|
|
284
|
+
pauseDuration?: number;
|
|
285
|
+
/** Collect GC stats via --trace-gc-nvp (requires worker mode) */
|
|
286
|
+
gcStats?: boolean;
|
|
287
|
+
/** Allocation sampling attribution */
|
|
288
|
+
alloc?: boolean;
|
|
289
|
+
/** Allocation sampling interval in bytes */
|
|
290
|
+
allocInterval?: number;
|
|
291
|
+
/** Allocation sampling stack depth */
|
|
292
|
+
allocDepth?: number;
|
|
293
|
+
/** V8 CPU time sampling */
|
|
294
|
+
profile?: boolean;
|
|
295
|
+
/** CPU sampling interval in microseconds (default 1000) */
|
|
296
|
+
profileInterval?: number;
|
|
297
|
+
/** Collect per-function execution counts via V8 precise coverage */
|
|
298
|
+
callCounts?: boolean;
|
|
299
|
+
}
|
|
300
|
+
//#endregion
|
|
301
|
+
export { MeasuredResults as a, HeapProfile as c, BenchmarkSpec as i, CoverageData as l, BenchGroup as n, PausePoint as o, BenchSuite as r, TimeProfile as s, RunnerOptions as t };
|
|
302
|
+
//# sourceMappingURL=BenchRunner-DglX1NOn.d.mts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Session } from "node:inspector/promises";
|
|
2
|
+
//#region src/profiling/node/CoverageSampler.ts
|
|
3
|
+
/** Run a function while collecting precise V8 coverage, return execution counts.
|
|
4
|
+
* The session passed to `fn` can be shared with TimeSampler to avoid resetting counters. */
|
|
5
|
+
async function withCoverageProfiling(fn) {
|
|
6
|
+
const session = new Session();
|
|
7
|
+
session.connect();
|
|
8
|
+
try {
|
|
9
|
+
await session.post("Profiler.enable");
|
|
10
|
+
await session.post("Profiler.startPreciseCoverage", {
|
|
11
|
+
callCount: true,
|
|
12
|
+
detailed: true
|
|
13
|
+
});
|
|
14
|
+
return {
|
|
15
|
+
result: await fn(session),
|
|
16
|
+
coverage: { scripts: (await session.post("Profiler.takePreciseCoverage")).result }
|
|
17
|
+
};
|
|
18
|
+
} finally {
|
|
19
|
+
await session.post("Profiler.stopPreciseCoverage");
|
|
20
|
+
await session.post("Profiler.disable");
|
|
21
|
+
session.disconnect();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { withCoverageProfiling };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=CoverageSampler-D5T9DRqe.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CoverageSampler-D5T9DRqe.mjs","names":[],"sources":["../src/profiling/node/CoverageSampler.ts"],"sourcesContent":["import { Session } from \"node:inspector/promises\";\nimport type { CoverageData, ScriptCoverage } from \"./CoverageTypes.ts\";\n\n/** Run a function while collecting precise V8 coverage, return execution counts.\n * The session passed to `fn` can be shared with TimeSampler to avoid resetting counters. */\nexport async function withCoverageProfiling<T>(\n fn: (session: Session) => Promise<T> | T,\n): Promise<{ result: T; coverage: CoverageData }> {\n const session = new Session();\n session.connect();\n\n try {\n await session.post(\"Profiler.enable\");\n await session.post(\"Profiler.startPreciseCoverage\", {\n callCount: true,\n detailed: true,\n });\n const result = await fn(session);\n const raw = await session.post(\"Profiler.takePreciseCoverage\");\n const scripts = raw.result as unknown as ScriptCoverage[];\n return { result, coverage: { scripts } };\n } finally {\n await session.post(\"Profiler.stopPreciseCoverage\");\n await session.post(\"Profiler.disable\");\n session.disconnect();\n }\n}\n"],"mappings":";;;;AAKA,eAAsB,sBACpB,IACgD;CAChD,MAAM,UAAU,IAAI,SAAS;AAC7B,SAAQ,SAAS;AAEjB,KAAI;AACF,QAAM,QAAQ,KAAK,kBAAkB;AACrC,QAAM,QAAQ,KAAK,iCAAiC;GAClD,WAAW;GACX,UAAU;GACX,CAAC;AAIF,SAAO;GAAE,QAHM,MAAM,GAAG,QAAQ;GAGf,UAAU,EAAE,UAFjB,MAAM,QAAQ,KAAK,+BAA+B,EAC1C,QACkB;GAAE;WAChC;AACR,QAAM,QAAQ,KAAK,+BAA+B;AAClD,QAAM,QAAQ,KAAK,mBAAmB;AACtC,UAAQ,YAAY"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { l as flipCI } from "./StatisticalUtils-BD92crgM.mjs";
|
|
2
|
+
import pico from "picocolors";
|
|
3
|
+
//#region src/report/Colors.ts
|
|
4
|
+
const isTest = process.env.NODE_ENV === "test" || process.env.VITEST === "true";
|
|
5
|
+
/** Picocolors instance that disables color in test environments */
|
|
6
|
+
const colors = pico.createColors(!isTest);
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/report/Formatters.ts
|
|
9
|
+
const { red, green } = colors;
|
|
10
|
+
const lowConfidence = 80;
|
|
11
|
+
/** Format time in milliseconds with appropriate units */
|
|
12
|
+
function timeMs(ms) {
|
|
13
|
+
if (typeof ms !== "number") return null;
|
|
14
|
+
if (ms < .001) return `${(ms * 1e6).toFixed(0)}ns`;
|
|
15
|
+
if (ms < .01) return `${(ms * 1e3).toFixed(1)}μs`;
|
|
16
|
+
if (ms >= 1e3) return `${(ms / 1e3).toFixed(2)}s`;
|
|
17
|
+
if (ms >= 10) return `${ms.toFixed(0)}ms`;
|
|
18
|
+
return `${ms.toFixed(2)}ms`;
|
|
19
|
+
}
|
|
20
|
+
/** Format integer with thousand separators */
|
|
21
|
+
function integer(x) {
|
|
22
|
+
if (typeof x !== "number") return null;
|
|
23
|
+
return new Intl.NumberFormat("en-US").format(Math.round(x));
|
|
24
|
+
}
|
|
25
|
+
/** Format fraction as percentage (0.473 → 47.3%) */
|
|
26
|
+
function percent(fraction, precision = 1) {
|
|
27
|
+
if (typeof fraction !== "number") return null;
|
|
28
|
+
return `${Math.abs(fraction * 100).toFixed(precision)}%`;
|
|
29
|
+
}
|
|
30
|
+
/** Format percentage difference between two values */
|
|
31
|
+
function diffPercent(main, base) {
|
|
32
|
+
if (typeof main !== "number" || typeof base !== "number") return " ";
|
|
33
|
+
return coloredPercent(main - base, base);
|
|
34
|
+
}
|
|
35
|
+
/** Format bytes with appropriate units. Use `space: true` for `1.5 KB` style. */
|
|
36
|
+
function formatBytes(bytes, opts) {
|
|
37
|
+
if (typeof bytes !== "number") return null;
|
|
38
|
+
const s = opts?.space ? " " : "";
|
|
39
|
+
const [kb, mb, gb] = [
|
|
40
|
+
1024,
|
|
41
|
+
1024 ** 2,
|
|
42
|
+
1024 ** 3
|
|
43
|
+
];
|
|
44
|
+
if (bytes < kb) return `${bytes.toFixed(0)}${s}B`;
|
|
45
|
+
if (bytes < mb) return `${(bytes / kb).toFixed(1)}${s}KB`;
|
|
46
|
+
if (bytes < gb) return `${(bytes / mb).toFixed(1)}${s}MB`;
|
|
47
|
+
return `${(bytes / gb).toFixed(1)}${s}GB`;
|
|
48
|
+
}
|
|
49
|
+
/** Format percentage difference with confidence interval.
|
|
50
|
+
* When higherIsBetter is true, flips the CI so positive = improvement. */
|
|
51
|
+
function formatDiffWithCI(value, higherIsBetter) {
|
|
52
|
+
if (!isDifferenceCI(value)) return null;
|
|
53
|
+
const ci = higherIsBetter ? flipCI(value) : value;
|
|
54
|
+
const suffix = value.ciLevel === "sample" ? " *" : "";
|
|
55
|
+
return colorByDirection(diffCIText(ci.percent, ci.ci) + suffix, ci.direction);
|
|
56
|
+
}
|
|
57
|
+
/** @return truncated string with ellipsis if over maxLen */
|
|
58
|
+
function truncate(str, maxLen = 30) {
|
|
59
|
+
return str.length > maxLen ? str.slice(0, maxLen - 3) + "..." : str;
|
|
60
|
+
}
|
|
61
|
+
/** @return signed percentage string (e.g. "+1.2%", "-3.4%") */
|
|
62
|
+
function formatSignedPercent(v) {
|
|
63
|
+
return `${v >= 0 ? "+" : ""}${v.toFixed(1)}%`;
|
|
64
|
+
}
|
|
65
|
+
/** @return convergence percentage with color for low values */
|
|
66
|
+
function formatConvergence(v) {
|
|
67
|
+
if (typeof v !== "number") return "—";
|
|
68
|
+
const pct = `${Math.round(v)}%`;
|
|
69
|
+
return v < lowConfidence ? red(pct) : pct;
|
|
70
|
+
}
|
|
71
|
+
/** Format fraction as colored +/- percentage (positive = green, negative = red) */
|
|
72
|
+
function coloredPercent(numerator, denominator) {
|
|
73
|
+
const fraction = numerator / denominator;
|
|
74
|
+
if (!Number.isFinite(fraction)) return " ";
|
|
75
|
+
const percentStr = `${fraction >= 0 ? "+" : "-"}${percent(fraction)}`;
|
|
76
|
+
return fraction >= 0 ? green(percentStr) : red(percentStr);
|
|
77
|
+
}
|
|
78
|
+
function isDifferenceCI(x) {
|
|
79
|
+
return typeof x === "object" && x !== null && "ci" in x && "direction" in x;
|
|
80
|
+
}
|
|
81
|
+
/** @return formatted "pct [lo, hi]" text for a diff with CI */
|
|
82
|
+
function diffCIText(pct, ci) {
|
|
83
|
+
const [lo, hi] = ci.map(formatSignedPercent);
|
|
84
|
+
return `${formatSignedPercent(pct)} [${lo}, ${hi}]`;
|
|
85
|
+
}
|
|
86
|
+
/** @return text colored green for faster/equivalent, red for slower */
|
|
87
|
+
function colorByDirection(text, direction) {
|
|
88
|
+
if (direction === "faster" || direction === "equivalent") return green(text);
|
|
89
|
+
if (direction === "slower") return red(text);
|
|
90
|
+
return text;
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
export { formatSignedPercent as a, timeMs as c, formatDiffWithCI as i, truncate as l, formatBytes as n, integer as o, formatConvergence as r, percent as s, diffPercent as t, colors as u };
|
|
94
|
+
|
|
95
|
+
//# sourceMappingURL=Formatters-BWj3d4sv.mjs.map
|