benchforge 0.1.9 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +99 -260
- package/bin/benchforge +1 -2
- package/dist/AnalyzeArchive-8NCJhmhS.mjs +145 -0
- package/dist/AnalyzeArchive-8NCJhmhS.mjs.map +1 -0
- package/dist/BenchMatrix-BZVrBB_h.mjs +1050 -0
- package/dist/BenchMatrix-BZVrBB_h.mjs.map +1 -0
- package/dist/BenchRunner-DglX1NOn.d.mts +302 -0
- package/dist/CoverageSampler-D5T9DRqe.mjs +27 -0
- package/dist/CoverageSampler-D5T9DRqe.mjs.map +1 -0
- package/dist/Formatters-BWj3d4sv.mjs +95 -0
- package/dist/Formatters-BWj3d4sv.mjs.map +1 -0
- package/dist/{HeapSampler-B8dtKHn1.mjs → HeapSampler-Dq-hpXem.mjs} +4 -4
- package/dist/HeapSampler-Dq-hpXem.mjs.map +1 -0
- package/dist/RunBenchCLI-C17DrJz8.mjs +3075 -0
- package/dist/RunBenchCLI-C17DrJz8.mjs.map +1 -0
- package/dist/StatisticalUtils-BD92crgM.mjs +255 -0
- package/dist/StatisticalUtils-BD92crgM.mjs.map +1 -0
- package/dist/TimeSampler-Ds8n7l2B.mjs +29 -0
- package/dist/TimeSampler-Ds8n7l2B.mjs.map +1 -0
- package/dist/ViewerServer-BJhdnxlN.mjs +639 -0
- package/dist/ViewerServer-BJhdnxlN.mjs.map +1 -0
- package/dist/ViewerServer-CuMNdNBz.mjs +2 -0
- package/dist/bin/benchforge.mjs +4 -5
- package/dist/bin/benchforge.mjs.map +1 -1
- package/dist/index.d.mts +731 -522
- package/dist/index.mjs +98 -3
- package/dist/index.mjs.map +1 -0
- package/dist/runners/WorkerScript.d.mts +12 -4
- package/dist/runners/WorkerScript.mjs +92 -120
- package/dist/runners/WorkerScript.mjs.map +1 -1
- package/dist/viewer/assets/CIPlot-BkOvMoMa.js +1 -0
- package/dist/viewer/assets/HistogramKde-CmSyUFY0.js +1 -0
- package/dist/viewer/assets/LegendUtils-BJpbn_jr.js +55 -0
- package/dist/viewer/assets/SampleTimeSeries-C4VBhXr3.js +1 -0
- package/dist/viewer/assets/index-Br9bp_cX.js +153 -0
- package/dist/viewer/assets/index-NzXXe_CC.css +1 -0
- package/dist/viewer/index.html +19 -0
- package/dist/viewer/speedscope/LICENSE +21 -0
- package/dist/viewer/speedscope/SourceCodePro-Regular.ttf-ILST5JV6.woff2 +0 -0
- package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js +2 -0
- package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js.map +7 -0
- package/dist/viewer/speedscope/favicon-16x16-VSI62OPJ.png +0 -0
- package/dist/viewer/speedscope/favicon-32x32-3EB2YCUY.png +0 -0
- package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js +2 -0
- package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js.map +7 -0
- package/dist/viewer/speedscope/favicon-FOKUP5Y5.ico +0 -0
- package/dist/viewer/speedscope/favicon-M34RF7BI.js +2 -0
- package/dist/viewer/speedscope/favicon-M34RF7BI.js.map +7 -0
- package/dist/viewer/speedscope/file-format-schema.json +274 -0
- package/dist/viewer/speedscope/index.html +19 -0
- package/dist/viewer/speedscope/jfrview_bg-BLJXNNQB.wasm +0 -0
- package/dist/viewer/speedscope/perf-vertx-stacks-01-collapsed-all-ZNUIGAJL.txt +199 -0
- package/dist/viewer/speedscope/release.txt +3 -0
- package/dist/viewer/speedscope/source-code-pro.LICENSE.md +93 -0
- package/dist/viewer/speedscope/speedscope-GHPHNKXC.css +2 -0
- package/dist/viewer/speedscope/speedscope-GHPHNKXC.css.map +7 -0
- package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js +212 -0
- package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js.map +7 -0
- package/package.json +52 -26
- package/src/bin/benchforge.ts +2 -2
- package/src/cli/AnalyzeArchive.ts +232 -0
- package/src/cli/BrowserBench.ts +322 -0
- package/src/cli/CliArgs.ts +164 -48
- package/src/cli/CliExport.ts +179 -0
- package/src/cli/CliOptions.ts +147 -0
- package/src/cli/CliReport.ts +197 -0
- package/src/cli/FilterBenchmarks.ts +18 -30
- package/src/cli/RunBenchCLI.ts +138 -844
- package/src/cli/SuiteRunner.ts +160 -0
- package/src/cli/ViewerServer.ts +282 -0
- package/src/export/AllocExport.ts +121 -0
- package/src/export/ArchiveExport.ts +146 -0
- package/src/export/ArchiveFormat.ts +50 -0
- package/src/export/CoverageExport.ts +148 -0
- package/src/export/EditorUri.ts +10 -0
- package/src/export/PerfettoExport.ts +91 -126
- package/src/export/SpeedscopeTypes.ts +98 -0
- package/src/export/TimeExport.ts +115 -0
- package/src/index.ts +87 -62
- package/src/matrix/BenchMatrix.ts +230 -0
- package/src/matrix/CaseLoader.ts +8 -6
- package/src/matrix/MatrixDirRunner.ts +153 -0
- package/src/matrix/MatrixFilter.ts +55 -53
- package/src/matrix/MatrixInlineRunner.ts +50 -0
- package/src/matrix/MatrixReport.ts +94 -254
- package/src/matrix/VariantLoader.ts +9 -9
- package/src/profiling/browser/BenchLoop.ts +51 -0
- package/src/profiling/browser/BrowserCDP.ts +133 -0
- package/src/profiling/browser/BrowserGcStats.ts +33 -0
- package/src/profiling/browser/BrowserProfiler.ts +160 -0
- package/src/profiling/browser/CdpClient.ts +82 -0
- package/src/profiling/browser/CdpPage.ts +138 -0
- package/src/profiling/browser/ChromeLauncher.ts +158 -0
- package/src/profiling/browser/ChromeTraceEvent.ts +28 -0
- package/src/profiling/browser/PageLoadMode.ts +61 -0
- package/src/profiling/node/CoverageSampler.ts +27 -0
- package/src/profiling/node/CoverageTypes.ts +23 -0
- package/src/profiling/node/HeapSampleReport.ts +261 -0
- package/src/{heap-sample → profiling/node}/HeapSampler.ts +55 -13
- package/src/profiling/node/ResolvedProfile.ts +98 -0
- package/src/profiling/node/TimeSampler.ts +57 -0
- package/src/report/BenchmarkReport.ts +146 -0
- package/src/report/Colors.ts +9 -0
- package/src/report/Formatters.ts +110 -0
- package/src/report/GcSections.ts +151 -0
- package/src/{GitUtils.ts → report/GitUtils.ts} +18 -19
- package/src/report/HtmlReport.ts +223 -0
- package/src/report/ParseStats.ts +73 -0
- package/src/report/StandardSections.ts +147 -0
- package/src/report/ViewerSections.ts +286 -0
- package/src/report/text/TableReport.ts +253 -0
- package/src/report/text/TextReport.ts +123 -0
- package/src/runners/AdaptiveWrapper.ts +167 -287
- package/src/runners/BenchRunner.ts +27 -22
- package/src/{Benchmark.ts → runners/BenchmarkSpec.ts} +5 -6
- package/src/runners/CreateRunner.ts +5 -7
- package/src/runners/GcStats.ts +58 -61
- package/src/{MeasuredResults.ts → runners/MeasuredResults.ts} +43 -37
- package/src/runners/MergeBatches.ts +123 -0
- package/src/{NodeGC.ts → runners/NodeGC.ts} +2 -3
- package/src/runners/RunnerOrchestrator.ts +180 -296
- package/src/runners/RunnerUtils.ts +75 -1
- package/src/runners/SampleStats.ts +100 -0
- package/src/runners/TimingRunner.ts +244 -0
- package/src/runners/TimingUtils.ts +3 -2
- package/src/runners/WorkerScript.ts +162 -178
- package/src/stats/BootstrapDifference.ts +282 -0
- package/src/{PermutationTest.ts → stats/PermutationTest.ts} +31 -40
- package/src/stats/StatisticalUtils.ts +445 -0
- package/src/{tests → test}/AdaptiveConvergence.test.ts +10 -10
- package/src/test/AdaptiveRunner.test.ts +39 -41
- package/src/{tests → test}/AdaptiveSampling.test.ts +9 -9
- package/src/test/AdaptiveStatistics.integration.ts +9 -41
- package/src/{tests → test}/BenchMatrix.test.ts +31 -28
- package/src/test/BenchmarkReport.test.ts +63 -13
- package/src/test/BrowserBench.e2e.test.ts +186 -17
- package/src/test/BrowserBench.test.ts +10 -5
- package/src/test/BuildTimeSection.test.ts +130 -0
- package/src/test/CapSamples.test.ts +82 -0
- package/src/test/CoverageExport.test.ts +115 -0
- package/src/test/CoverageSampler.test.ts +33 -0
- package/src/test/HeapAttribution.test.ts +51 -0
- package/src/{tests → test}/MatrixFilter.test.ts +16 -16
- package/src/{tests → test}/MatrixReport.test.ts +1 -1
- package/src/test/PermutationTest.test.ts +1 -1
- package/src/{tests → test}/RealDataValidation.test.ts +6 -6
- package/src/test/RunBenchCLI.test.ts +57 -56
- package/src/test/RunnerOrchestrator.test.ts +12 -12
- package/src/test/StatisticalUtils.test.ts +48 -12
- package/src/{table-util/test → test}/TableReport.test.ts +2 -2
- package/src/test/TestUtils.ts +35 -30
- package/src/test/TimeExport.test.ts +139 -0
- package/src/test/TimeSampler.test.ts +37 -0
- package/src/test/ViewerLive.e2e.test.ts +159 -0
- package/src/test/ViewerStatic.static.e2e.test.ts +137 -0
- package/src/{tests → test}/fixtures/baseline/impl.ts +1 -1
- package/src/{tests → test}/fixtures/bevy30-samples.ts +3 -1
- package/src/test/fixtures/cases/asyncCases.ts +9 -0
- package/src/{tests → test}/fixtures/cases/cases.ts +5 -2
- package/src/test/fixtures/cases/variants/product.ts +2 -0
- package/src/test/fixtures/cases/variants/sum.ts +2 -0
- package/src/test/fixtures/discover/fast.ts +1 -0
- package/src/{tests → test}/fixtures/discover/slow.ts +1 -1
- package/src/test/fixtures/invalid/bad.ts +1 -0
- package/src/test/fixtures/loader/fast.ts +1 -0
- package/src/{tests → test}/fixtures/loader/slow.ts +1 -1
- package/src/test/fixtures/loader/stateful.ts +2 -0
- package/src/test/fixtures/stateful/stateful.ts +2 -0
- package/src/test/fixtures/variants/extra.ts +1 -0
- package/src/test/fixtures/variants/impl.ts +1 -0
- package/src/test/fixtures/worker/fast.ts +1 -0
- package/src/{tests → test}/fixtures/worker/slow.ts +1 -1
- package/src/viewer/DateFormat.ts +30 -0
- package/src/viewer/Helpers.ts +23 -0
- package/src/viewer/LineData.ts +120 -0
- package/src/viewer/Providers.ts +191 -0
- package/src/viewer/ReportData.ts +123 -0
- package/src/viewer/State.ts +49 -0
- package/src/viewer/Theme.ts +15 -0
- package/src/viewer/components/App.tsx +73 -0
- package/src/viewer/components/DropZone.tsx +71 -0
- package/src/viewer/components/LazyPlot.ts +33 -0
- package/src/viewer/components/SamplesPanel.tsx +214 -0
- package/src/viewer/components/Shell.tsx +26 -0
- package/src/viewer/components/SourcePanel.tsx +216 -0
- package/src/viewer/components/SummaryPanel.tsx +332 -0
- package/src/viewer/components/TabBar.tsx +131 -0
- package/src/viewer/components/TabContent.tsx +46 -0
- package/src/viewer/components/ThemeToggle.tsx +50 -0
- package/src/viewer/index.html +20 -0
- package/src/viewer/main.tsx +4 -0
- package/src/viewer/plots/CIPlot.ts +313 -0
- package/src/{html/browser → viewer/plots}/HistogramKde.ts +42 -47
- package/src/viewer/plots/LegendUtils.ts +134 -0
- package/src/viewer/plots/PlotTypes.ts +85 -0
- package/src/viewer/plots/RenderPlots.ts +230 -0
- package/src/viewer/plots/SampleTimeSeries.ts +306 -0
- package/src/viewer/plots/SvgHelpers.ts +136 -0
- package/src/viewer/plots/TimeSeriesMarks.ts +319 -0
- package/src/viewer/report.css +427 -0
- package/src/viewer/shell.css +357 -0
- package/src/viewer/tsconfig.json +11 -0
- package/dist/BenchRunner-CSKN9zPy.d.mts +0 -225
- package/dist/BrowserHeapSampler-DCeL42RE.mjs +0 -202
- package/dist/BrowserHeapSampler-DCeL42RE.mjs.map +0 -1
- package/dist/GcStats-ByEovUi1.mjs +0 -77
- package/dist/GcStats-ByEovUi1.mjs.map +0 -1
- package/dist/HeapSampler-B8dtKHn1.mjs.map +0 -1
- package/dist/TimingUtils-ClclVQ7E.mjs +0 -597
- package/dist/TimingUtils-ClclVQ7E.mjs.map +0 -1
- package/dist/browser/index.js +0 -914
- package/dist/src-Cf_LXwlp.mjs +0 -2873
- package/dist/src-Cf_LXwlp.mjs.map +0 -1
- package/src/BenchMatrix.ts +0 -380
- package/src/BenchmarkReport.ts +0 -156
- package/src/HtmlDataPrep.ts +0 -148
- package/src/StandardSections.ts +0 -261
- package/src/StatisticalUtils.ts +0 -176
- package/src/TypeUtil.ts +0 -8
- package/src/browser/BrowserGcStats.ts +0 -44
- package/src/browser/BrowserHeapSampler.ts +0 -271
- package/src/export/JsonExport.ts +0 -103
- package/src/export/JsonFormat.ts +0 -91
- package/src/heap-sample/HeapSampleReport.ts +0 -196
- package/src/html/HtmlReport.ts +0 -131
- package/src/html/HtmlTemplate.ts +0 -284
- package/src/html/Types.ts +0 -88
- package/src/html/browser/CIPlot.ts +0 -287
- package/src/html/browser/LegendUtils.ts +0 -163
- package/src/html/browser/RenderPlots.ts +0 -263
- package/src/html/browser/SampleTimeSeries.ts +0 -389
- package/src/html/browser/Types.ts +0 -96
- package/src/html/browser/index.ts +0 -1
- package/src/html/index.ts +0 -17
- package/src/runners/BasicRunner.ts +0 -364
- package/src/table-util/ConvergenceFormatters.ts +0 -19
- package/src/table-util/Formatters.ts +0 -152
- package/src/table-util/README.md +0 -70
- package/src/table-util/TableReport.ts +0 -293
- package/src/tests/fixtures/cases/asyncCases.ts +0 -7
- package/src/tests/fixtures/cases/variants/product.ts +0 -2
- package/src/tests/fixtures/cases/variants/sum.ts +0 -2
- package/src/tests/fixtures/discover/fast.ts +0 -1
- package/src/tests/fixtures/invalid/bad.ts +0 -1
- package/src/tests/fixtures/loader/fast.ts +0 -1
- package/src/tests/fixtures/loader/stateful.ts +0 -2
- package/src/tests/fixtures/stateful/stateful.ts +0 -2
- package/src/tests/fixtures/variants/extra.ts +0 -1
- package/src/tests/fixtures/variants/impl.ts +0 -1
- package/src/tests/fixtures/worker/fast.ts +0 -1
- package/src/{table-util/test → test}/TableValueExtractor.test.ts +0 -0
- package/src/{table-util/test → test}/TableValueExtractor.ts +9 -9
package/src/StandardSections.ts
DELETED
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
import type { ReportColumnGroup, ResultsMapper } from "./BenchmarkReport.ts";
|
|
2
|
-
import type { MeasuredResults } from "./MeasuredResults.ts";
|
|
3
|
-
import { formatConvergence } from "./table-util/ConvergenceFormatters.ts";
|
|
4
|
-
import {
|
|
5
|
-
formatBytes,
|
|
6
|
-
integer,
|
|
7
|
-
percent,
|
|
8
|
-
percentPrecision,
|
|
9
|
-
timeMs,
|
|
10
|
-
} from "./table-util/Formatters.ts";
|
|
11
|
-
|
|
12
|
-
export interface TimeStats {
|
|
13
|
-
mean?: number;
|
|
14
|
-
p50?: number;
|
|
15
|
-
p99?: number;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/** Section: mean, p50, p99 timing */
|
|
19
|
-
export const timeSection: ResultsMapper<TimeStats> = {
|
|
20
|
-
extract: (results: MeasuredResults) => ({
|
|
21
|
-
mean: results.time?.avg,
|
|
22
|
-
p50: results.time?.p50,
|
|
23
|
-
p99: results.time?.p99,
|
|
24
|
-
}),
|
|
25
|
-
columns: (): ReportColumnGroup<TimeStats>[] => [
|
|
26
|
-
{
|
|
27
|
-
groupTitle: "time",
|
|
28
|
-
columns: [
|
|
29
|
-
{ key: "mean", title: "mean", formatter: timeMs, comparable: true },
|
|
30
|
-
{ key: "p50", title: "p50", formatter: timeMs, comparable: true },
|
|
31
|
-
{ key: "p99", title: "p99", formatter: timeMs, comparable: true },
|
|
32
|
-
],
|
|
33
|
-
},
|
|
34
|
-
],
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export interface GcSectionStats {
|
|
38
|
-
gc?: number; // GC time as fraction of total bench time
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Section: GC time as fraction of total benchmark time (Node performance hooks) */
|
|
42
|
-
export const gcSection: ResultsMapper<GcSectionStats> = {
|
|
43
|
-
extract: (results: MeasuredResults) => {
|
|
44
|
-
const { nodeGcTime, time, samples } = results;
|
|
45
|
-
if (!nodeGcTime || !time?.avg) return { gc: undefined };
|
|
46
|
-
const totalBenchTime = time.avg * samples.length;
|
|
47
|
-
if (totalBenchTime <= 0) return { gc: undefined };
|
|
48
|
-
const gcTime = nodeGcTime.inRun / totalBenchTime;
|
|
49
|
-
// GC time can't exceed total time
|
|
50
|
-
return { gc: gcTime <= 1 ? gcTime : undefined };
|
|
51
|
-
},
|
|
52
|
-
columns: (): ReportColumnGroup<GcSectionStats>[] => [
|
|
53
|
-
{
|
|
54
|
-
groupTitle: "gc",
|
|
55
|
-
columns: [
|
|
56
|
-
{ key: "gc", title: "mean", formatter: percent, comparable: true },
|
|
57
|
-
],
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export interface GcStatsInfo {
|
|
63
|
-
allocPerIter?: number;
|
|
64
|
-
collected?: number;
|
|
65
|
-
scavenges?: number;
|
|
66
|
-
fullGCs?: number;
|
|
67
|
-
promoPercent?: number;
|
|
68
|
-
pausePerIter?: number;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** Section: detailed GC stats from --trace-gc-nvp (allocation, promotion, pauses) */
|
|
72
|
-
export const gcStatsSection: ResultsMapper<GcStatsInfo> = {
|
|
73
|
-
extract: (results: MeasuredResults) => {
|
|
74
|
-
const { gcStats, samples } = results;
|
|
75
|
-
if (!gcStats) return {};
|
|
76
|
-
const iterations = samples.length || 1;
|
|
77
|
-
const { totalAllocated, totalPromoted } = gcStats;
|
|
78
|
-
const hasAlloc = totalAllocated && totalAllocated > 0;
|
|
79
|
-
const promoPercent = hasAlloc
|
|
80
|
-
? (totalPromoted ?? 0) / totalAllocated
|
|
81
|
-
: undefined;
|
|
82
|
-
return {
|
|
83
|
-
allocPerIter:
|
|
84
|
-
totalAllocated != null ? totalAllocated / iterations : undefined,
|
|
85
|
-
collected: gcStats.totalCollected || undefined,
|
|
86
|
-
scavenges: gcStats.scavenges,
|
|
87
|
-
fullGCs: gcStats.markCompacts,
|
|
88
|
-
promoPercent,
|
|
89
|
-
pausePerIter: gcStats.gcPauseTime / iterations,
|
|
90
|
-
};
|
|
91
|
-
},
|
|
92
|
-
columns: (): ReportColumnGroup<GcStatsInfo>[] => [
|
|
93
|
-
{
|
|
94
|
-
groupTitle: "gc",
|
|
95
|
-
columns: [
|
|
96
|
-
{ key: "allocPerIter", title: "alloc/iter", formatter: formatBytes },
|
|
97
|
-
{ key: "collected", title: "collected", formatter: formatBytes },
|
|
98
|
-
{ key: "scavenges", title: "scav", formatter: integer },
|
|
99
|
-
{ key: "fullGCs", title: "full", formatter: integer },
|
|
100
|
-
{ key: "promoPercent", title: "promo%", formatter: percent },
|
|
101
|
-
{ key: "pausePerIter", title: "pause/iter", formatter: timeMs },
|
|
102
|
-
],
|
|
103
|
-
},
|
|
104
|
-
],
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
/** Browser GC section: only fields available from CDP tracing */
|
|
108
|
-
export const browserGcStatsSection: ResultsMapper<GcStatsInfo> = {
|
|
109
|
-
extract: gcStatsSection.extract,
|
|
110
|
-
columns: (): ReportColumnGroup<GcStatsInfo>[] => [
|
|
111
|
-
{
|
|
112
|
-
groupTitle: "gc",
|
|
113
|
-
columns: [
|
|
114
|
-
{ key: "collected", title: "collected", formatter: formatBytes },
|
|
115
|
-
{ key: "scavenges", title: "scav", formatter: integer },
|
|
116
|
-
{ key: "fullGCs", title: "full", formatter: integer },
|
|
117
|
-
{ key: "pausePerIter", title: "pause", formatter: timeMs },
|
|
118
|
-
],
|
|
119
|
-
},
|
|
120
|
-
],
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
export interface CpuStats {
|
|
124
|
-
cpuCacheMiss?: number;
|
|
125
|
-
cpuStall?: number;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/** Section: CPU L1 cache miss rate and stall rate (requires @mitata/counters) */
|
|
129
|
-
export const cpuSection: ResultsMapper<CpuStats> = {
|
|
130
|
-
extract: (results: MeasuredResults) => ({
|
|
131
|
-
cpuCacheMiss: results.cpuCacheMiss,
|
|
132
|
-
cpuStall: results.cpuStall,
|
|
133
|
-
}),
|
|
134
|
-
columns: (): ReportColumnGroup<CpuStats>[] => [
|
|
135
|
-
{
|
|
136
|
-
groupTitle: "cpu",
|
|
137
|
-
columns: [
|
|
138
|
-
{ key: "cpuCacheMiss", title: "L1 miss", formatter: percent },
|
|
139
|
-
{ key: "cpuStall", title: "stalls", formatter: percentPrecision(2) },
|
|
140
|
-
],
|
|
141
|
-
},
|
|
142
|
-
],
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
export interface RunStats {
|
|
146
|
-
runs?: number;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/** Section: number of sample iterations */
|
|
150
|
-
export const runsSection: ResultsMapper<RunStats> = {
|
|
151
|
-
extract: (results: MeasuredResults) => ({
|
|
152
|
-
runs: results.samples.length,
|
|
153
|
-
}),
|
|
154
|
-
columns: (): ReportColumnGroup<RunStats>[] => [
|
|
155
|
-
{ columns: [{ key: "runs", title: "runs", formatter: integer }] },
|
|
156
|
-
],
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
/** Section: total sampling duration in seconds (brackets if >= 30s) */
|
|
160
|
-
export const totalTimeSection: ResultsMapper<{ totalTime?: number }> = {
|
|
161
|
-
extract: (results: MeasuredResults) => ({
|
|
162
|
-
totalTime: results.totalTime,
|
|
163
|
-
}),
|
|
164
|
-
columns: (): ReportColumnGroup<{ totalTime?: number }>[] => [
|
|
165
|
-
{
|
|
166
|
-
columns: [
|
|
167
|
-
{
|
|
168
|
-
key: "totalTime",
|
|
169
|
-
title: "time",
|
|
170
|
-
formatter: v => {
|
|
171
|
-
if (typeof v !== "number") return "";
|
|
172
|
-
return v >= 30 ? `[${v.toFixed(1)}s]` : `${v.toFixed(1)}s`;
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
],
|
|
176
|
-
},
|
|
177
|
-
],
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
export interface AdaptiveStats {
|
|
181
|
-
median?: number;
|
|
182
|
-
mean?: number;
|
|
183
|
-
p99?: number;
|
|
184
|
-
convergence?: number;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/** Section: median, mean, p99, and convergence for adaptive mode */
|
|
188
|
-
export const adaptiveSection: ResultsMapper<AdaptiveStats> = {
|
|
189
|
-
extract: (results: MeasuredResults) => ({
|
|
190
|
-
median: results.time?.p50,
|
|
191
|
-
mean: results.time?.avg,
|
|
192
|
-
p99: results.time?.p99,
|
|
193
|
-
convergence: results.convergence?.confidence,
|
|
194
|
-
}),
|
|
195
|
-
columns: (): ReportColumnGroup<AdaptiveStats>[] => [
|
|
196
|
-
{
|
|
197
|
-
groupTitle: "time",
|
|
198
|
-
columns: [
|
|
199
|
-
{ key: "median", title: "median", formatter: timeMs, comparable: true },
|
|
200
|
-
{ key: "mean", title: "mean", formatter: timeMs, comparable: true },
|
|
201
|
-
{ key: "p99", title: "p99", formatter: timeMs },
|
|
202
|
-
],
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
columns: [
|
|
206
|
-
{ key: "convergence", title: "conv%", formatter: formatConvergence },
|
|
207
|
-
],
|
|
208
|
-
},
|
|
209
|
-
],
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
/** Build generic sections based on CLI flags */
|
|
213
|
-
export function buildGenericSections(args: {
|
|
214
|
-
"gc-stats"?: boolean;
|
|
215
|
-
"heap-sample"?: boolean;
|
|
216
|
-
}): ResultsMapper[] {
|
|
217
|
-
const sections: ResultsMapper[] = [];
|
|
218
|
-
if (args["gc-stats"]) sections.push(gcStatsSection);
|
|
219
|
-
sections.push(runsSection);
|
|
220
|
-
return sections;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
export interface OptStats {
|
|
224
|
-
tiers?: string; // tier distribution summary
|
|
225
|
-
deopt?: number; // deopt count
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/** Section: V8 optimization tier distribution and deopt count */
|
|
229
|
-
export const optSection: ResultsMapper<OptStats> = {
|
|
230
|
-
extract: (results: MeasuredResults) => {
|
|
231
|
-
const opt = results.optStatus;
|
|
232
|
-
if (!opt) return {};
|
|
233
|
-
|
|
234
|
-
const total = Object.values(opt.byTier).reduce((s, t) => s + t.count, 0);
|
|
235
|
-
const tierParts = Object.entries(opt.byTier)
|
|
236
|
-
.sort((a, b) => b[1].count - a[1].count)
|
|
237
|
-
.map(([name, t]) => `${name}:${((t.count / total) * 100).toFixed(0)}%`);
|
|
238
|
-
|
|
239
|
-
return {
|
|
240
|
-
tiers: tierParts.join(" "),
|
|
241
|
-
deopt: opt.deoptCount > 0 ? opt.deoptCount : undefined,
|
|
242
|
-
};
|
|
243
|
-
},
|
|
244
|
-
columns: (): ReportColumnGroup<OptStats>[] => [
|
|
245
|
-
{
|
|
246
|
-
groupTitle: "v8 opt",
|
|
247
|
-
columns: [
|
|
248
|
-
{
|
|
249
|
-
key: "tiers",
|
|
250
|
-
title: "tiers",
|
|
251
|
-
formatter: v => (typeof v === "string" ? v : ""),
|
|
252
|
-
},
|
|
253
|
-
{
|
|
254
|
-
key: "deopt",
|
|
255
|
-
title: "deopt",
|
|
256
|
-
formatter: v => (typeof v === "number" ? String(v) : ""),
|
|
257
|
-
},
|
|
258
|
-
],
|
|
259
|
-
},
|
|
260
|
-
],
|
|
261
|
-
};
|
package/src/StatisticalUtils.ts
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
const outlierMultiplier = 1.5; // Tukey's fence multiplier
|
|
2
|
-
const bootstrapSamples = 10000;
|
|
3
|
-
const confidence = 0.95;
|
|
4
|
-
|
|
5
|
-
/** Options for bootstrap resampling methods */
|
|
6
|
-
type BootstrapOptions = {
|
|
7
|
-
resamples?: number;
|
|
8
|
-
confidence?: number;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
/** Bootstrap estimate with confidence interval and raw resample data */
|
|
12
|
-
export interface BootstrapResult {
|
|
13
|
-
estimate: number;
|
|
14
|
-
ci: [number, number];
|
|
15
|
-
samples: number[];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/** @return relative standard deviation (coefficient of variation) */
|
|
19
|
-
export function coefficientOfVariation(samples: number[]): number {
|
|
20
|
-
const mean = average(samples);
|
|
21
|
-
if (mean === 0) return 0;
|
|
22
|
-
const stdDev = standardDeviation(samples);
|
|
23
|
-
return stdDev / mean;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** @return median absolute deviation for robust variability measure */
|
|
27
|
-
export function medianAbsoluteDeviation(samples: number[]): number {
|
|
28
|
-
const median = percentile(samples, 0.5);
|
|
29
|
-
const deviations = samples.map(x => Math.abs(x - median));
|
|
30
|
-
return percentile(deviations, 0.5);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** @return outliers detected via Tukey's interquartile range method */
|
|
34
|
-
export function findOutliers(samples: number[]): {
|
|
35
|
-
rate: number;
|
|
36
|
-
indices: number[];
|
|
37
|
-
} {
|
|
38
|
-
const q1 = percentile(samples, 0.25);
|
|
39
|
-
const q3 = percentile(samples, 0.75);
|
|
40
|
-
const iqr = q3 - q1;
|
|
41
|
-
const lowerBound = q1 - outlierMultiplier * iqr;
|
|
42
|
-
const upperBound = q3 + outlierMultiplier * iqr;
|
|
43
|
-
|
|
44
|
-
const indices = samples
|
|
45
|
-
.map((v, i) => (v < lowerBound || v > upperBound ? i : -1))
|
|
46
|
-
.filter(i => i >= 0);
|
|
47
|
-
return { rate: indices.length / samples.length, indices };
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** @return bootstrap confidence interval for median */
|
|
51
|
-
export function bootstrapMedian(
|
|
52
|
-
samples: number[],
|
|
53
|
-
options: BootstrapOptions = {},
|
|
54
|
-
): BootstrapResult {
|
|
55
|
-
const { resamples = bootstrapSamples, confidence: conf = confidence } =
|
|
56
|
-
options;
|
|
57
|
-
const medians = generateMedians(samples, resamples);
|
|
58
|
-
const ci = computeInterval(medians, conf);
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
estimate: percentile(samples, 0.5),
|
|
62
|
-
ci,
|
|
63
|
-
samples: medians,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** @return mean of values */
|
|
68
|
-
export function average(values: number[]): number {
|
|
69
|
-
const sum = values.reduce((a, b) => a + b, 0);
|
|
70
|
-
return sum / values.length;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** @return standard deviation with Bessel's correction */
|
|
74
|
-
export function standardDeviation(samples: number[]): number {
|
|
75
|
-
if (samples.length <= 1) return 0;
|
|
76
|
-
const mean = average(samples);
|
|
77
|
-
const variance =
|
|
78
|
-
samples.reduce((sum, x) => sum + (x - mean) ** 2, 0) / (samples.length - 1);
|
|
79
|
-
return Math.sqrt(variance);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/** @return value at percentile p (0-1) */
|
|
83
|
-
export function percentile(values: number[], p: number): number {
|
|
84
|
-
const sorted = [...values].sort((a, b) => a - b);
|
|
85
|
-
const index = Math.ceil(sorted.length * p) - 1;
|
|
86
|
-
return sorted[Math.max(0, index)];
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** @return medians from bootstrap resamples */
|
|
90
|
-
function generateMedians(samples: number[], resamples: number): number[] {
|
|
91
|
-
return Array.from({ length: resamples }, () =>
|
|
92
|
-
percentile(createResample(samples), 0.5),
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** @return bootstrap resample with replacement */
|
|
97
|
-
export function createResample(samples: number[]): number[] {
|
|
98
|
-
const n = samples.length;
|
|
99
|
-
const rand = () => samples[Math.floor(Math.random() * n)];
|
|
100
|
-
return Array.from({ length: n }, rand);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** @return confidence interval [lower, upper] */
|
|
104
|
-
function computeInterval(
|
|
105
|
-
medians: number[],
|
|
106
|
-
confidence: number,
|
|
107
|
-
): [number, number] {
|
|
108
|
-
const alpha = (1 - confidence) / 2;
|
|
109
|
-
const lower = percentile(medians, alpha);
|
|
110
|
-
const upper = percentile(medians, 1 - alpha);
|
|
111
|
-
return [lower, upper];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export type CIDirection = "faster" | "slower" | "uncertain";
|
|
115
|
-
|
|
116
|
-
/** Binned histogram for efficient transfer to browser */
|
|
117
|
-
export interface HistogramBin {
|
|
118
|
-
x: number; // bin center
|
|
119
|
-
count: number;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/** Bootstrap confidence interval for percentage difference between two samples */
|
|
123
|
-
export interface DifferenceCI {
|
|
124
|
-
percent: number;
|
|
125
|
-
ci: [number, number];
|
|
126
|
-
direction: CIDirection;
|
|
127
|
-
/** Histogram of bootstrap distribution for visualization */
|
|
128
|
-
histogram?: HistogramBin[];
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/** Bin values into histogram for compact visualization */
|
|
132
|
-
function binValues(values: number[], binCount = 30): HistogramBin[] {
|
|
133
|
-
const sorted = [...values].sort((a, b) => a - b);
|
|
134
|
-
const min = sorted[0];
|
|
135
|
-
const max = sorted[sorted.length - 1];
|
|
136
|
-
if (min === max) return [{ x: min, count: values.length }];
|
|
137
|
-
|
|
138
|
-
const step = (max - min) / binCount;
|
|
139
|
-
const counts = new Array(binCount).fill(0);
|
|
140
|
-
for (const v of values) {
|
|
141
|
-
const bin = Math.min(Math.floor((v - min) / step), binCount - 1);
|
|
142
|
-
counts[bin]++;
|
|
143
|
-
}
|
|
144
|
-
return counts.map((count, i) => ({ x: min + (i + 0.5) * step, count }));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** @return bootstrap CI for percentage difference between baseline and current medians */
|
|
148
|
-
export function bootstrapDifferenceCI(
|
|
149
|
-
baseline: number[],
|
|
150
|
-
current: number[],
|
|
151
|
-
options: BootstrapOptions = {},
|
|
152
|
-
): DifferenceCI {
|
|
153
|
-
const { resamples = bootstrapSamples, confidence: conf = confidence } =
|
|
154
|
-
options;
|
|
155
|
-
|
|
156
|
-
const baselineMedian = percentile(baseline, 0.5);
|
|
157
|
-
const currentMedian = percentile(current, 0.5);
|
|
158
|
-
const observedPercent =
|
|
159
|
-
((currentMedian - baselineMedian) / baselineMedian) * 100;
|
|
160
|
-
|
|
161
|
-
const diffs: number[] = [];
|
|
162
|
-
for (let i = 0; i < resamples; i++) {
|
|
163
|
-
const resB = createResample(baseline);
|
|
164
|
-
const resC = createResample(current);
|
|
165
|
-
const medB = percentile(resB, 0.5);
|
|
166
|
-
const medC = percentile(resC, 0.5);
|
|
167
|
-
diffs.push(((medC - medB) / medB) * 100);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const ci = computeInterval(diffs, conf);
|
|
171
|
-
const excludesZero = ci[0] > 0 || ci[1] < 0;
|
|
172
|
-
let direction: CIDirection = "uncertain";
|
|
173
|
-
if (excludesZero) direction = observedPercent < 0 ? "faster" : "slower";
|
|
174
|
-
const histogram = binValues(diffs);
|
|
175
|
-
return { percent: observedPercent, ci, direction, histogram };
|
|
176
|
-
}
|
package/src/TypeUtil.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
aggregateGcStats,
|
|
3
|
-
type GcEvent,
|
|
4
|
-
type GcStats,
|
|
5
|
-
} from "../runners/GcStats.ts";
|
|
6
|
-
|
|
7
|
-
/** CDP trace event from Tracing.dataCollected */
|
|
8
|
-
export interface TraceEvent {
|
|
9
|
-
cat: string;
|
|
10
|
-
name: string;
|
|
11
|
-
ph: string;
|
|
12
|
-
dur?: number; // microseconds
|
|
13
|
-
args?: Record<string, any>;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** Parse CDP trace events (MinorGC/MajorGC) into GcEvent[] */
|
|
17
|
-
export function parseGcTraceEvents(traceEvents: TraceEvent[]): GcEvent[] {
|
|
18
|
-
return traceEvents.flatMap(e => {
|
|
19
|
-
if (e.ph !== "X") return [];
|
|
20
|
-
const type = gcType(e.name);
|
|
21
|
-
if (!type) return [];
|
|
22
|
-
const durUs = e.dur ?? 0;
|
|
23
|
-
const heapBefore: number = e.args?.usedHeapSizeBefore ?? 0;
|
|
24
|
-
const heapAfter: number = e.args?.usedHeapSizeAfter ?? 0;
|
|
25
|
-
return [
|
|
26
|
-
{
|
|
27
|
-
type,
|
|
28
|
-
pauseMs: durUs / 1000,
|
|
29
|
-
collected: Math.max(0, heapBefore - heapAfter),
|
|
30
|
-
},
|
|
31
|
-
];
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function gcType(name: string): GcEvent["type"] | undefined {
|
|
36
|
-
if (name === "MinorGC") return "scavenge";
|
|
37
|
-
if (name === "MajorGC") return "mark-compact";
|
|
38
|
-
return undefined;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Parse CDP trace events and aggregate into GcStats */
|
|
42
|
-
export function browserGcStats(traceEvents: TraceEvent[]): GcStats {
|
|
43
|
-
return aggregateGcStats(parseGcTraceEvents(traceEvents));
|
|
44
|
-
}
|