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.
Files changed (253) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +99 -260
  3. package/bin/benchforge +1 -2
  4. package/dist/AnalyzeArchive-8NCJhmhS.mjs +145 -0
  5. package/dist/AnalyzeArchive-8NCJhmhS.mjs.map +1 -0
  6. package/dist/BenchMatrix-BZVrBB_h.mjs +1050 -0
  7. package/dist/BenchMatrix-BZVrBB_h.mjs.map +1 -0
  8. package/dist/BenchRunner-DglX1NOn.d.mts +302 -0
  9. package/dist/CoverageSampler-D5T9DRqe.mjs +27 -0
  10. package/dist/CoverageSampler-D5T9DRqe.mjs.map +1 -0
  11. package/dist/Formatters-BWj3d4sv.mjs +95 -0
  12. package/dist/Formatters-BWj3d4sv.mjs.map +1 -0
  13. package/dist/{HeapSampler-B8dtKHn1.mjs → HeapSampler-Dq-hpXem.mjs} +4 -4
  14. package/dist/HeapSampler-Dq-hpXem.mjs.map +1 -0
  15. package/dist/RunBenchCLI-C17DrJz8.mjs +3075 -0
  16. package/dist/RunBenchCLI-C17DrJz8.mjs.map +1 -0
  17. package/dist/StatisticalUtils-BD92crgM.mjs +255 -0
  18. package/dist/StatisticalUtils-BD92crgM.mjs.map +1 -0
  19. package/dist/TimeSampler-Ds8n7l2B.mjs +29 -0
  20. package/dist/TimeSampler-Ds8n7l2B.mjs.map +1 -0
  21. package/dist/ViewerServer-BJhdnxlN.mjs +639 -0
  22. package/dist/ViewerServer-BJhdnxlN.mjs.map +1 -0
  23. package/dist/ViewerServer-CuMNdNBz.mjs +2 -0
  24. package/dist/bin/benchforge.mjs +4 -5
  25. package/dist/bin/benchforge.mjs.map +1 -1
  26. package/dist/index.d.mts +731 -522
  27. package/dist/index.mjs +98 -3
  28. package/dist/index.mjs.map +1 -0
  29. package/dist/runners/WorkerScript.d.mts +12 -4
  30. package/dist/runners/WorkerScript.mjs +92 -120
  31. package/dist/runners/WorkerScript.mjs.map +1 -1
  32. package/dist/viewer/assets/CIPlot-BkOvMoMa.js +1 -0
  33. package/dist/viewer/assets/HistogramKde-CmSyUFY0.js +1 -0
  34. package/dist/viewer/assets/LegendUtils-BJpbn_jr.js +55 -0
  35. package/dist/viewer/assets/SampleTimeSeries-C4VBhXr3.js +1 -0
  36. package/dist/viewer/assets/index-Br9bp_cX.js +153 -0
  37. package/dist/viewer/assets/index-NzXXe_CC.css +1 -0
  38. package/dist/viewer/index.html +19 -0
  39. package/dist/viewer/speedscope/LICENSE +21 -0
  40. package/dist/viewer/speedscope/SourceCodePro-Regular.ttf-ILST5JV6.woff2 +0 -0
  41. package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js +2 -0
  42. package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js.map +7 -0
  43. package/dist/viewer/speedscope/favicon-16x16-VSI62OPJ.png +0 -0
  44. package/dist/viewer/speedscope/favicon-32x32-3EB2YCUY.png +0 -0
  45. package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js +2 -0
  46. package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js.map +7 -0
  47. package/dist/viewer/speedscope/favicon-FOKUP5Y5.ico +0 -0
  48. package/dist/viewer/speedscope/favicon-M34RF7BI.js +2 -0
  49. package/dist/viewer/speedscope/favicon-M34RF7BI.js.map +7 -0
  50. package/dist/viewer/speedscope/file-format-schema.json +274 -0
  51. package/dist/viewer/speedscope/index.html +19 -0
  52. package/dist/viewer/speedscope/jfrview_bg-BLJXNNQB.wasm +0 -0
  53. package/dist/viewer/speedscope/perf-vertx-stacks-01-collapsed-all-ZNUIGAJL.txt +199 -0
  54. package/dist/viewer/speedscope/release.txt +3 -0
  55. package/dist/viewer/speedscope/source-code-pro.LICENSE.md +93 -0
  56. package/dist/viewer/speedscope/speedscope-GHPHNKXC.css +2 -0
  57. package/dist/viewer/speedscope/speedscope-GHPHNKXC.css.map +7 -0
  58. package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js +212 -0
  59. package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js.map +7 -0
  60. package/package.json +52 -26
  61. package/src/bin/benchforge.ts +2 -2
  62. package/src/cli/AnalyzeArchive.ts +232 -0
  63. package/src/cli/BrowserBench.ts +322 -0
  64. package/src/cli/CliArgs.ts +164 -48
  65. package/src/cli/CliExport.ts +179 -0
  66. package/src/cli/CliOptions.ts +147 -0
  67. package/src/cli/CliReport.ts +197 -0
  68. package/src/cli/FilterBenchmarks.ts +18 -30
  69. package/src/cli/RunBenchCLI.ts +138 -844
  70. package/src/cli/SuiteRunner.ts +160 -0
  71. package/src/cli/ViewerServer.ts +282 -0
  72. package/src/export/AllocExport.ts +121 -0
  73. package/src/export/ArchiveExport.ts +146 -0
  74. package/src/export/ArchiveFormat.ts +50 -0
  75. package/src/export/CoverageExport.ts +148 -0
  76. package/src/export/EditorUri.ts +10 -0
  77. package/src/export/PerfettoExport.ts +91 -126
  78. package/src/export/SpeedscopeTypes.ts +98 -0
  79. package/src/export/TimeExport.ts +115 -0
  80. package/src/index.ts +87 -62
  81. package/src/matrix/BenchMatrix.ts +230 -0
  82. package/src/matrix/CaseLoader.ts +8 -6
  83. package/src/matrix/MatrixDirRunner.ts +153 -0
  84. package/src/matrix/MatrixFilter.ts +55 -53
  85. package/src/matrix/MatrixInlineRunner.ts +50 -0
  86. package/src/matrix/MatrixReport.ts +94 -254
  87. package/src/matrix/VariantLoader.ts +9 -9
  88. package/src/profiling/browser/BenchLoop.ts +51 -0
  89. package/src/profiling/browser/BrowserCDP.ts +133 -0
  90. package/src/profiling/browser/BrowserGcStats.ts +33 -0
  91. package/src/profiling/browser/BrowserProfiler.ts +160 -0
  92. package/src/profiling/browser/CdpClient.ts +82 -0
  93. package/src/profiling/browser/CdpPage.ts +138 -0
  94. package/src/profiling/browser/ChromeLauncher.ts +158 -0
  95. package/src/profiling/browser/ChromeTraceEvent.ts +28 -0
  96. package/src/profiling/browser/PageLoadMode.ts +61 -0
  97. package/src/profiling/node/CoverageSampler.ts +27 -0
  98. package/src/profiling/node/CoverageTypes.ts +23 -0
  99. package/src/profiling/node/HeapSampleReport.ts +261 -0
  100. package/src/{heap-sample → profiling/node}/HeapSampler.ts +55 -13
  101. package/src/profiling/node/ResolvedProfile.ts +98 -0
  102. package/src/profiling/node/TimeSampler.ts +57 -0
  103. package/src/report/BenchmarkReport.ts +146 -0
  104. package/src/report/Colors.ts +9 -0
  105. package/src/report/Formatters.ts +110 -0
  106. package/src/report/GcSections.ts +151 -0
  107. package/src/{GitUtils.ts → report/GitUtils.ts} +18 -19
  108. package/src/report/HtmlReport.ts +223 -0
  109. package/src/report/ParseStats.ts +73 -0
  110. package/src/report/StandardSections.ts +147 -0
  111. package/src/report/ViewerSections.ts +286 -0
  112. package/src/report/text/TableReport.ts +253 -0
  113. package/src/report/text/TextReport.ts +123 -0
  114. package/src/runners/AdaptiveWrapper.ts +167 -287
  115. package/src/runners/BenchRunner.ts +27 -22
  116. package/src/{Benchmark.ts → runners/BenchmarkSpec.ts} +5 -6
  117. package/src/runners/CreateRunner.ts +5 -7
  118. package/src/runners/GcStats.ts +58 -61
  119. package/src/{MeasuredResults.ts → runners/MeasuredResults.ts} +43 -37
  120. package/src/runners/MergeBatches.ts +123 -0
  121. package/src/{NodeGC.ts → runners/NodeGC.ts} +2 -3
  122. package/src/runners/RunnerOrchestrator.ts +180 -296
  123. package/src/runners/RunnerUtils.ts +75 -1
  124. package/src/runners/SampleStats.ts +100 -0
  125. package/src/runners/TimingRunner.ts +244 -0
  126. package/src/runners/TimingUtils.ts +3 -2
  127. package/src/runners/WorkerScript.ts +162 -178
  128. package/src/stats/BootstrapDifference.ts +282 -0
  129. package/src/{PermutationTest.ts → stats/PermutationTest.ts} +31 -40
  130. package/src/stats/StatisticalUtils.ts +445 -0
  131. package/src/{tests → test}/AdaptiveConvergence.test.ts +10 -10
  132. package/src/test/AdaptiveRunner.test.ts +39 -41
  133. package/src/{tests → test}/AdaptiveSampling.test.ts +9 -9
  134. package/src/test/AdaptiveStatistics.integration.ts +9 -41
  135. package/src/{tests → test}/BenchMatrix.test.ts +31 -28
  136. package/src/test/BenchmarkReport.test.ts +63 -13
  137. package/src/test/BrowserBench.e2e.test.ts +186 -17
  138. package/src/test/BrowserBench.test.ts +10 -5
  139. package/src/test/BuildTimeSection.test.ts +130 -0
  140. package/src/test/CapSamples.test.ts +82 -0
  141. package/src/test/CoverageExport.test.ts +115 -0
  142. package/src/test/CoverageSampler.test.ts +33 -0
  143. package/src/test/HeapAttribution.test.ts +51 -0
  144. package/src/{tests → test}/MatrixFilter.test.ts +16 -16
  145. package/src/{tests → test}/MatrixReport.test.ts +1 -1
  146. package/src/test/PermutationTest.test.ts +1 -1
  147. package/src/{tests → test}/RealDataValidation.test.ts +6 -6
  148. package/src/test/RunBenchCLI.test.ts +57 -56
  149. package/src/test/RunnerOrchestrator.test.ts +12 -12
  150. package/src/test/StatisticalUtils.test.ts +48 -12
  151. package/src/{table-util/test → test}/TableReport.test.ts +2 -2
  152. package/src/test/TestUtils.ts +35 -30
  153. package/src/test/TimeExport.test.ts +139 -0
  154. package/src/test/TimeSampler.test.ts +37 -0
  155. package/src/test/ViewerLive.e2e.test.ts +159 -0
  156. package/src/test/ViewerStatic.static.e2e.test.ts +137 -0
  157. package/src/{tests → test}/fixtures/baseline/impl.ts +1 -1
  158. package/src/{tests → test}/fixtures/bevy30-samples.ts +3 -1
  159. package/src/test/fixtures/cases/asyncCases.ts +9 -0
  160. package/src/{tests → test}/fixtures/cases/cases.ts +5 -2
  161. package/src/test/fixtures/cases/variants/product.ts +2 -0
  162. package/src/test/fixtures/cases/variants/sum.ts +2 -0
  163. package/src/test/fixtures/discover/fast.ts +1 -0
  164. package/src/{tests → test}/fixtures/discover/slow.ts +1 -1
  165. package/src/test/fixtures/invalid/bad.ts +1 -0
  166. package/src/test/fixtures/loader/fast.ts +1 -0
  167. package/src/{tests → test}/fixtures/loader/slow.ts +1 -1
  168. package/src/test/fixtures/loader/stateful.ts +2 -0
  169. package/src/test/fixtures/stateful/stateful.ts +2 -0
  170. package/src/test/fixtures/variants/extra.ts +1 -0
  171. package/src/test/fixtures/variants/impl.ts +1 -0
  172. package/src/test/fixtures/worker/fast.ts +1 -0
  173. package/src/{tests → test}/fixtures/worker/slow.ts +1 -1
  174. package/src/viewer/DateFormat.ts +30 -0
  175. package/src/viewer/Helpers.ts +23 -0
  176. package/src/viewer/LineData.ts +120 -0
  177. package/src/viewer/Providers.ts +191 -0
  178. package/src/viewer/ReportData.ts +123 -0
  179. package/src/viewer/State.ts +49 -0
  180. package/src/viewer/Theme.ts +15 -0
  181. package/src/viewer/components/App.tsx +73 -0
  182. package/src/viewer/components/DropZone.tsx +71 -0
  183. package/src/viewer/components/LazyPlot.ts +33 -0
  184. package/src/viewer/components/SamplesPanel.tsx +214 -0
  185. package/src/viewer/components/Shell.tsx +26 -0
  186. package/src/viewer/components/SourcePanel.tsx +216 -0
  187. package/src/viewer/components/SummaryPanel.tsx +332 -0
  188. package/src/viewer/components/TabBar.tsx +131 -0
  189. package/src/viewer/components/TabContent.tsx +46 -0
  190. package/src/viewer/components/ThemeToggle.tsx +50 -0
  191. package/src/viewer/index.html +20 -0
  192. package/src/viewer/main.tsx +4 -0
  193. package/src/viewer/plots/CIPlot.ts +313 -0
  194. package/src/{html/browser → viewer/plots}/HistogramKde.ts +42 -47
  195. package/src/viewer/plots/LegendUtils.ts +134 -0
  196. package/src/viewer/plots/PlotTypes.ts +85 -0
  197. package/src/viewer/plots/RenderPlots.ts +230 -0
  198. package/src/viewer/plots/SampleTimeSeries.ts +306 -0
  199. package/src/viewer/plots/SvgHelpers.ts +136 -0
  200. package/src/viewer/plots/TimeSeriesMarks.ts +319 -0
  201. package/src/viewer/report.css +427 -0
  202. package/src/viewer/shell.css +357 -0
  203. package/src/viewer/tsconfig.json +11 -0
  204. package/dist/BenchRunner-CSKN9zPy.d.mts +0 -225
  205. package/dist/BrowserHeapSampler-DCeL42RE.mjs +0 -202
  206. package/dist/BrowserHeapSampler-DCeL42RE.mjs.map +0 -1
  207. package/dist/GcStats-ByEovUi1.mjs +0 -77
  208. package/dist/GcStats-ByEovUi1.mjs.map +0 -1
  209. package/dist/HeapSampler-B8dtKHn1.mjs.map +0 -1
  210. package/dist/TimingUtils-ClclVQ7E.mjs +0 -597
  211. package/dist/TimingUtils-ClclVQ7E.mjs.map +0 -1
  212. package/dist/browser/index.js +0 -914
  213. package/dist/src-Cf_LXwlp.mjs +0 -2873
  214. package/dist/src-Cf_LXwlp.mjs.map +0 -1
  215. package/src/BenchMatrix.ts +0 -380
  216. package/src/BenchmarkReport.ts +0 -156
  217. package/src/HtmlDataPrep.ts +0 -148
  218. package/src/StandardSections.ts +0 -261
  219. package/src/StatisticalUtils.ts +0 -176
  220. package/src/TypeUtil.ts +0 -8
  221. package/src/browser/BrowserGcStats.ts +0 -44
  222. package/src/browser/BrowserHeapSampler.ts +0 -271
  223. package/src/export/JsonExport.ts +0 -103
  224. package/src/export/JsonFormat.ts +0 -91
  225. package/src/heap-sample/HeapSampleReport.ts +0 -196
  226. package/src/html/HtmlReport.ts +0 -131
  227. package/src/html/HtmlTemplate.ts +0 -284
  228. package/src/html/Types.ts +0 -88
  229. package/src/html/browser/CIPlot.ts +0 -287
  230. package/src/html/browser/LegendUtils.ts +0 -163
  231. package/src/html/browser/RenderPlots.ts +0 -263
  232. package/src/html/browser/SampleTimeSeries.ts +0 -389
  233. package/src/html/browser/Types.ts +0 -96
  234. package/src/html/browser/index.ts +0 -1
  235. package/src/html/index.ts +0 -17
  236. package/src/runners/BasicRunner.ts +0 -364
  237. package/src/table-util/ConvergenceFormatters.ts +0 -19
  238. package/src/table-util/Formatters.ts +0 -152
  239. package/src/table-util/README.md +0 -70
  240. package/src/table-util/TableReport.ts +0 -293
  241. package/src/tests/fixtures/cases/asyncCases.ts +0 -7
  242. package/src/tests/fixtures/cases/variants/product.ts +0 -2
  243. package/src/tests/fixtures/cases/variants/sum.ts +0 -2
  244. package/src/tests/fixtures/discover/fast.ts +0 -1
  245. package/src/tests/fixtures/invalid/bad.ts +0 -1
  246. package/src/tests/fixtures/loader/fast.ts +0 -1
  247. package/src/tests/fixtures/loader/stateful.ts +0 -2
  248. package/src/tests/fixtures/stateful/stateful.ts +0 -2
  249. package/src/tests/fixtures/variants/extra.ts +0 -1
  250. package/src/tests/fixtures/variants/impl.ts +0 -1
  251. package/src/tests/fixtures/worker/fast.ts +0 -1
  252. package/src/{table-util/test → test}/TableValueExtractor.test.ts +0 -0
  253. package/src/{table-util/test → test}/TableValueExtractor.ts +9 -9
@@ -1,389 +0,0 @@
1
- import * as Plot from "@observablehq/plot";
2
- import * as d3 from "d3";
3
- import { buildLegend, type LegendItem } from "./LegendUtils.ts";
4
- import type {
5
- GcEvent,
6
- HeapPoint,
7
- PausePoint,
8
- TimeSeriesPoint,
9
- } from "./Types.ts";
10
-
11
- const OPT_STATUS_NAMES: Record<number, string> = {
12
- 1: "interpreted",
13
- 129: "sparkplug",
14
- 17: "turbofan",
15
- 33: "maglev",
16
- 49: "turbofan+maglev",
17
- 32769: "optimized",
18
- };
19
- const OPT_TIER_COLORS: Record<string, string> = {
20
- turbofan: "#22c55e",
21
- optimized: "#22c55e",
22
- "turbofan+maglev": "#22c55e",
23
- maglev: "#eab308",
24
- sparkplug: "#f97316",
25
- interpreted: "#dc3545",
26
- };
27
-
28
- interface SampleData {
29
- benchmark: string;
30
- sample: number;
31
- value: number;
32
- displayValue: number;
33
- isBaseline: boolean;
34
- isWarmup: boolean;
35
- optTier: string | null;
36
- }
37
-
38
- interface PlotContext {
39
- convertedData: SampleData[];
40
- xMin: number;
41
- xMax: number;
42
- yMin: number;
43
- yMax: number;
44
- unitSuffix: string;
45
- formatValue: (d: number) => string;
46
- convertValue: (ms: number) => number;
47
- hasWarmup: boolean;
48
- optTiers: string[];
49
- benchmarks: string[];
50
- }
51
-
52
- /** Create sample time series showing each sample in order */
53
- export function createSampleTimeSeries(
54
- timeSeries: TimeSeriesPoint[],
55
- gcEvents: GcEvent[] = [],
56
- pausePoints: PausePoint[] = [],
57
- heapSeries: HeapPoint[] = [],
58
- ): SVGSVGElement | HTMLElement {
59
- const ctx = buildPlotContext(timeSeries);
60
- const heapData = prepareHeapData(heapSeries, ctx.yMin, ctx.yMax);
61
-
62
- return Plot.plot({
63
- marginTop: 24,
64
- marginLeft: 70,
65
- marginBottom: 60,
66
- marginRight: 110,
67
- width: 550,
68
- height: 300,
69
- style: { fontSize: "14px" },
70
- x: {
71
- label: "Sample",
72
- labelAnchor: "center",
73
- labelOffset: 45,
74
- grid: true,
75
- domain: [ctx.xMin, ctx.xMax],
76
- },
77
- y: {
78
- label: `Time (${ctx.unitSuffix})`,
79
- labelAnchor: "top",
80
- labelArrow: false,
81
- grid: true,
82
- domain: [ctx.yMin, ctx.yMax],
83
- tickFormat: ctx.formatValue,
84
- },
85
- color: { legend: false, scheme: "observable10" },
86
- marks: [
87
- ...heapMarks(heapData, ctx.yMin),
88
- ...(ctx.hasWarmup
89
- ? [
90
- Plot.ruleX([0], {
91
- stroke: "#999",
92
- strokeWidth: 1,
93
- strokeDasharray: "4,4",
94
- }),
95
- ]
96
- : []),
97
- gcMark(gcEvents, ctx.yMin, ctx.convertValue),
98
- ...pauseMarks(pausePoints, ctx.yMin, ctx.yMax),
99
- ...sampleDotMarks(ctx),
100
- Plot.ruleY([ctx.yMin], { stroke: "black", strokeWidth: 1 }),
101
- ...buildLegend(
102
- { xMin: ctx.xMin, xMax: ctx.xMax, yMax: ctx.yMax },
103
- buildLegendItems(
104
- ctx.hasWarmup,
105
- gcEvents.length,
106
- pausePoints.length,
107
- heapData.length > 0,
108
- ctx.optTiers,
109
- ctx.benchmarks,
110
- ),
111
- ),
112
- ],
113
- });
114
- }
115
-
116
- function buildPlotContext(timeSeries: TimeSeriesPoint[]): PlotContext {
117
- const benchmarks = [...new Set(timeSeries.map(d => d.benchmark))];
118
- const sampleData = buildSampleData(timeSeries, benchmarks);
119
- const { unitSuffix, convertValue, formatValue } = getTimeUnit(
120
- sampleData.map(d => d.value),
121
- );
122
- const convertedData = sampleData.map(d => ({
123
- ...d,
124
- displayValue: convertValue(d.value),
125
- }));
126
- const { yMin, yMax } = computeYRange(convertedData.map(d => d.displayValue));
127
- const xMin = d3.min(convertedData, d => d.sample)!;
128
- const xMax = d3.max(convertedData, d => d.sample)!;
129
- const hasWarmup = convertedData.some(d => d.isWarmup);
130
- const tierSet = new Set(
131
- convertedData.filter(d => d.optTier && !d.isWarmup).map(d => d.optTier),
132
- );
133
- const optTiers = [...tierSet].filter((t): t is string => t !== null);
134
- return {
135
- convertedData,
136
- xMin,
137
- xMax,
138
- yMin,
139
- yMax,
140
- unitSuffix,
141
- formatValue,
142
- convertValue,
143
- hasWarmup,
144
- optTiers,
145
- benchmarks,
146
- };
147
- }
148
-
149
- function buildSampleData(
150
- timeSeries: TimeSeriesPoint[],
151
- benchmarks: string[],
152
- ): Omit<SampleData, "displayValue">[] {
153
- const result: Omit<SampleData, "displayValue">[] = [];
154
- for (const benchmark of benchmarks) {
155
- const isBaseline = benchmark.includes("(baseline)");
156
- for (const d of timeSeries.filter(t => t.benchmark === benchmark)) {
157
- const optTier =
158
- d.optStatus !== undefined
159
- ? OPT_STATUS_NAMES[d.optStatus] || "unknown"
160
- : null;
161
- result.push({
162
- benchmark,
163
- sample: d.iteration,
164
- value: d.value,
165
- isBaseline,
166
- isWarmup: d.isWarmup || false,
167
- optTier,
168
- });
169
- }
170
- }
171
- return result;
172
- }
173
-
174
- /** Pick display unit (ns/us/ms) based on average value magnitude */
175
- function getTimeUnit(values: number[]) {
176
- const avg = d3.mean(values)!;
177
- const fmt0 = (d: number) => d3.format(",.0f")(d);
178
- const fmt1 = (d: number) => d3.format(",.1f")(d);
179
- if (avg < 0.001)
180
- return {
181
- unitSuffix: "ns",
182
- convertValue: (ms: number) => ms * 1e6,
183
- formatValue: fmt0,
184
- };
185
- if (avg < 1)
186
- return {
187
- unitSuffix: "μs",
188
- convertValue: (ms: number) => ms * 1e3,
189
- formatValue: fmt1,
190
- };
191
- return {
192
- unitSuffix: "ms",
193
- convertValue: (ms: number) => ms,
194
- formatValue: fmt1,
195
- };
196
- }
197
-
198
- /** Compute Y axis range with padding, snapping yMin to a round number */
199
- function computeYRange(values: number[]) {
200
- const dataMin = d3.min(values)!;
201
- const dataMax = d3.max(values)!;
202
- const dataRange = dataMax - dataMin;
203
- const padding = dataRange * 0.15;
204
- let yMin = dataMin - padding;
205
- const magnitude = 10 ** Math.floor(Math.log10(Math.abs(yMin)));
206
- yMin = Math.floor(yMin / magnitude) * magnitude;
207
- if (dataMin > 0 && yMin < 0) yMin = 0;
208
- return { yMin, yMax: dataMax + dataRange * 0.05 };
209
- }
210
-
211
- /** Scale heap byte values into the plot's Y coordinate range */
212
- function prepareHeapData(heapSeries: HeapPoint[], yMin: number, yMax: number) {
213
- if (heapSeries.length === 0) return [];
214
- const heapMin = d3.min(heapSeries, d => d.value)!;
215
- const heapRange = d3.max(heapSeries, d => d.value)! - heapMin || 1;
216
- const scale = ((yMax - yMin) * 0.25) / heapRange;
217
- return heapSeries.map(d => ({
218
- sample: d.iteration,
219
- y: yMin + (d.value - heapMin) * scale,
220
- heapMB: d.value / 1024 / 1024,
221
- }));
222
- }
223
-
224
- function heapMarks(
225
- heapData: { sample: number; y: number; heapMB: number }[],
226
- yMin: number,
227
- ): any[] {
228
- if (heapData.length === 0) return [];
229
- return [
230
- Plot.areaY(heapData, {
231
- x: "sample",
232
- y: "y",
233
- y1: yMin,
234
- fill: "#9333ea",
235
- fillOpacity: 0.15,
236
- stroke: "#9333ea",
237
- strokeWidth: 1,
238
- strokeOpacity: 0.4,
239
- }),
240
- Plot.tip(
241
- heapData,
242
- Plot.pointerX({
243
- x: "sample",
244
- y: "y",
245
- title: (d: { heapMB: number }) => `Heap: ${d.heapMB.toFixed(1)} MB`,
246
- }),
247
- ),
248
- ];
249
- }
250
-
251
- function gcMark(
252
- gcEvents: GcEvent[],
253
- yMin: number,
254
- convertValue: (ms: number) => number,
255
- ): any {
256
- const data = gcEvents.map(gc => ({
257
- x1: gc.sampleIndex,
258
- y1: yMin,
259
- x2: gc.sampleIndex,
260
- y2: yMin + convertValue(gc.duration),
261
- duration: gc.duration,
262
- }));
263
- return Plot.link(data, {
264
- x1: "x1",
265
- y1: "y1",
266
- x2: "x2",
267
- y2: "y2",
268
- stroke: "#22c55e",
269
- strokeWidth: 2,
270
- strokeOpacity: 0.8,
271
- title: (d: { duration: number }) => `GC: ${d.duration.toFixed(2)}ms`,
272
- });
273
- }
274
-
275
- function pauseMarks(
276
- pausePoints: PausePoint[],
277
- yMin: number,
278
- yMax: number,
279
- ): any[] {
280
- return pausePoints.map(p =>
281
- Plot.ruleX([p.sampleIndex], {
282
- y1: yMin,
283
- y2: yMax,
284
- stroke: "#888",
285
- strokeWidth: 1,
286
- strokeDasharray: "4,4",
287
- strokeOpacity: 0.7,
288
- title: `Pause: ${p.durationMs}ms`,
289
- }),
290
- );
291
- }
292
-
293
- function sampleDotMarks(ctx: PlotContext): any[] {
294
- const { convertedData, unitSuffix, formatValue } = ctx;
295
- const tipTitle = (d: SampleData) =>
296
- d.optTier
297
- ? `Sample ${d.sample}: ${formatValue(d.displayValue)}${unitSuffix} [${d.optTier}]`
298
- : `Sample ${d.sample}: ${formatValue(d.displayValue)}${unitSuffix}`;
299
- return [
300
- Plot.dot(
301
- convertedData.filter(d => d.isWarmup),
302
- {
303
- x: "sample",
304
- y: "displayValue",
305
- stroke: "#dc3545",
306
- fill: "none",
307
- strokeWidth: 1.5,
308
- r: 3,
309
- opacity: 0.7,
310
- title: (d: SampleData) =>
311
- `Warmup ${d.sample}: ${formatValue(d.displayValue)}${unitSuffix}`,
312
- },
313
- ),
314
- Plot.dot(
315
- convertedData.filter(d => d.isBaseline && !d.isWarmup),
316
- {
317
- x: "sample",
318
- y: "displayValue",
319
- stroke: "#ffa500",
320
- fill: "none",
321
- strokeWidth: 2,
322
- r: 3,
323
- opacity: 0.8,
324
- title: tipTitle,
325
- },
326
- ),
327
- Plot.dot(
328
- convertedData.filter(d => !d.isBaseline && !d.isWarmup),
329
- {
330
- x: "sample",
331
- y: "displayValue",
332
- fill: (d: SampleData) =>
333
- d.optTier ? OPT_TIER_COLORS[d.optTier] || "#4682b4" : "#4682b4",
334
- r: 3,
335
- opacity: 0.8,
336
- title: tipTitle,
337
- },
338
- ),
339
- ];
340
- }
341
-
342
- function buildLegendItems(
343
- hasWarmup: boolean,
344
- gcCount: number,
345
- pauseCount: number,
346
- hasHeap: boolean,
347
- optTiers: string[],
348
- benchmarks: string[],
349
- ): LegendItem[] {
350
- const items: LegendItem[] = [];
351
- if (hasWarmup)
352
- items.push({ color: "#dc3545", label: "warmup", style: "hollow-dot" });
353
- if (gcCount > 0)
354
- items.push({
355
- color: "#22c55e",
356
- label: `gc (${gcCount})`,
357
- style: "vertical-line",
358
- });
359
- if (pauseCount > 0)
360
- items.push({
361
- color: "#888",
362
- label: `pause (${pauseCount})`,
363
- style: "vertical-line",
364
- strokeDash: "4,4",
365
- });
366
- if (hasHeap) items.push({ color: "#9333ea", label: "heap", style: "rect" });
367
- for (const tier of optTiers)
368
- items.push({
369
- color: OPT_TIER_COLORS[tier] || "#4682b4",
370
- label: tier,
371
- style: "filled-dot",
372
- });
373
- if (optTiers.length === 0) {
374
- const sorted = [...benchmarks].sort((a, b) => {
375
- const aBase = a.includes("(baseline)");
376
- const bBase = b.includes("(baseline)");
377
- return aBase === bBase ? 0 : aBase ? 1 : -1;
378
- });
379
- for (const bm of sorted) {
380
- const isBase = bm.includes("(baseline)");
381
- items.push({
382
- color: isBase ? "#ffa500" : "#4682b4",
383
- label: bm,
384
- style: isBase ? "hollow-dot" : "filled-dot",
385
- });
386
- }
387
- }
388
- return items;
389
- }
@@ -1,96 +0,0 @@
1
- export interface Sample {
2
- benchmark: string;
3
- value: number;
4
- iteration: number;
5
- }
6
-
7
- export interface TimeSeriesPoint {
8
- benchmark: string;
9
- iteration: number;
10
- value: number;
11
- isWarmup: boolean;
12
- optStatus?: number;
13
- }
14
-
15
- export interface GcEvent {
16
- benchmark: string;
17
- sampleIndex: number;
18
- duration: number;
19
- }
20
-
21
- export interface PausePoint {
22
- benchmark: string;
23
- sampleIndex: number;
24
- durationMs: number;
25
- }
26
-
27
- export interface HeapPoint {
28
- benchmark: string;
29
- iteration: number;
30
- value: number;
31
- }
32
-
33
- export interface BenchmarkStats {
34
- min: number;
35
- max: number;
36
- avg: number;
37
- p50: number;
38
- p75: number;
39
- p99: number;
40
- }
41
-
42
- export interface SectionStat {
43
- groupTitle?: string;
44
- label: string;
45
- value: string;
46
- }
47
-
48
- export interface HistogramBin {
49
- x: number;
50
- count: number;
51
- }
52
-
53
- /** Bootstrap confidence interval for A/B comparison */
54
- export interface ComparisonCI {
55
- percent: number;
56
- ci: [number, number];
57
- direction: "faster" | "slower" | "uncertain";
58
- histogram?: HistogramBin[];
59
- }
60
-
61
- /** One benchmark's raw data, statistics, and optional comparison results */
62
- export interface BenchmarkEntry {
63
- name: string;
64
- samples: number[];
65
- warmupSamples?: number[];
66
- heapSamples?: number[];
67
- gcEvents?: { offset: number; duration: number }[];
68
- optSamples?: number[];
69
- pausePoints?: { sampleIndex: number; durationMs: number }[];
70
- stats: BenchmarkStats;
71
- sectionStats?: SectionStat[];
72
- comparisonCI?: ComparisonCI;
73
- isBaseline: boolean;
74
- }
75
-
76
- export interface BenchmarkGroup {
77
- baseline?: BenchmarkEntry;
78
- benchmarks: BenchmarkEntry[];
79
- }
80
-
81
- export interface GitVersion {
82
- hash: string;
83
- date: string;
84
- dirty?: boolean;
85
- }
86
-
87
- /** Top-level data structure for the HTML benchmark report */
88
- export interface ReportData {
89
- metadata: {
90
- cliArgs?: Record<string, unknown>;
91
- gcTrackingEnabled?: boolean;
92
- currentVersion?: GitVersion;
93
- baselineVersion?: GitVersion;
94
- };
95
- groups: BenchmarkGroup[];
96
- }
@@ -1 +0,0 @@
1
- export { renderPlots } from "./RenderPlots.ts";
package/src/html/index.ts DELETED
@@ -1,17 +0,0 @@
1
- export { generateHtmlReport } from "./HtmlReport.ts";
2
- export {
3
- formatDateWithTimezone,
4
- formatRelativeTime,
5
- } from "./HtmlTemplate.ts";
6
- export type {
7
- BenchmarkData,
8
- DifferenceCI,
9
- FormattedStat,
10
- GcEvent,
11
- GitVersion,
12
- GroupData,
13
- HtmlReportOptions,
14
- HtmlReportResult,
15
- PausePoint,
16
- ReportData,
17
- } from "./Types.ts";