benchforge 0.1.11 → 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 -294
  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-BzyUfiyB.d.mts → BenchRunner-DglX1NOn.d.mts} +119 -66
  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 +711 -558
  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 +77 -105
  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 -27
  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 -51
  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 +132 -866
  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 +64 -99
  78. package/src/export/SpeedscopeTypes.ts +98 -0
  79. package/src/export/TimeExport.ts +115 -0
  80. package/src/index.ts +86 -67
  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 +49 -47
  85. package/src/matrix/MatrixInlineRunner.ts +50 -0
  86. package/src/matrix/MatrixReport.ts +90 -250
  87. package/src/matrix/VariantLoader.ts +5 -5
  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 +1 -2
  101. package/src/{heap-sample → profiling/node}/ResolvedProfile.ts +18 -9
  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 +116 -236
  115. package/src/runners/BenchRunner.ts +20 -15
  116. package/src/{Benchmark.ts → runners/BenchmarkSpec.ts} +5 -6
  117. package/src/runners/CreateRunner.ts +5 -7
  118. package/src/runners/GcStats.ts +47 -50
  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 +127 -243
  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 +135 -151
  128. package/src/stats/BootstrapDifference.ts +282 -0
  129. package/src/{PermutationTest.ts → stats/PermutationTest.ts} +8 -17
  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 +2 -2
  135. package/src/{tests → test}/BenchMatrix.test.ts +19 -16
  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 +14 -14
  144. package/src/{tests → test}/MatrixFilter.test.ts +1 -1
  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 +39 -38
  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 +12 -7
  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 +33 -38
  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/BrowserHeapSampler-B6asLKWQ.mjs +0 -202
  205. package/dist/BrowserHeapSampler-B6asLKWQ.mjs.map +0 -1
  206. package/dist/GcStats-wX7Xyblu.mjs +0 -77
  207. package/dist/GcStats-wX7Xyblu.mjs.map +0 -1
  208. package/dist/HeapSampler-B8dtKHn1.mjs.map +0 -1
  209. package/dist/TimingUtils-DwOwkc8G.mjs +0 -597
  210. package/dist/TimingUtils-DwOwkc8G.mjs.map +0 -1
  211. package/dist/browser/index.js +0 -914
  212. package/dist/src-B-DDaCa9.mjs +0 -3108
  213. package/dist/src-B-DDaCa9.mjs.map +0 -1
  214. package/src/BenchMatrix.ts +0 -380
  215. package/src/BenchmarkReport.ts +0 -161
  216. package/src/HtmlDataPrep.ts +0 -148
  217. package/src/StandardSections.ts +0 -261
  218. package/src/StatisticalUtils.ts +0 -175
  219. package/src/TypeUtil.ts +0 -8
  220. package/src/browser/BrowserGcStats.ts +0 -44
  221. package/src/browser/BrowserHeapSampler.ts +0 -271
  222. package/src/export/JsonExport.ts +0 -103
  223. package/src/export/JsonFormat.ts +0 -91
  224. package/src/export/SpeedscopeExport.ts +0 -202
  225. package/src/heap-sample/HeapSampleReport.ts +0 -269
  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 -157
  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 +0 -0
@@ -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
- export interface GcSectionStats {
19
- gc?: number; // GC time as fraction of total bench time
20
- }
21
-
22
- export interface GcStatsInfo {
23
- allocPerIter?: number;
24
- collected?: number;
25
- scavenges?: number;
26
- fullGCs?: number;
27
- promoPercent?: number;
28
- pausePerIter?: number;
29
- }
30
-
31
- export interface CpuStats {
32
- cpuCacheMiss?: number;
33
- cpuStall?: number;
34
- }
35
-
36
- export interface RunStats {
37
- runs?: number;
38
- }
39
-
40
- export interface AdaptiveStats {
41
- median?: number;
42
- mean?: number;
43
- p99?: number;
44
- convergence?: number;
45
- }
46
-
47
- export interface OptStats {
48
- tiers?: string; // tier distribution summary
49
- deopt?: number; // deopt count
50
- }
51
-
52
- /** Section: mean, p50, p99 timing */
53
- export const timeSection: ResultsMapper<TimeStats> = {
54
- extract: (results: MeasuredResults) => ({
55
- mean: results.time?.avg,
56
- p50: results.time?.p50,
57
- p99: results.time?.p99,
58
- }),
59
- columns: (): ReportColumnGroup<TimeStats>[] => [
60
- {
61
- groupTitle: "time",
62
- columns: [
63
- { key: "mean", title: "mean", formatter: timeMs, comparable: true },
64
- { key: "p50", title: "p50", formatter: timeMs, comparable: true },
65
- { key: "p99", title: "p99", formatter: timeMs, comparable: true },
66
- ],
67
- },
68
- ],
69
- };
70
-
71
- /** Section: GC time as fraction of total benchmark time (Node performance hooks) */
72
- export const gcSection: ResultsMapper<GcSectionStats> = {
73
- extract: (results: MeasuredResults) => {
74
- const { nodeGcTime, time, samples } = results;
75
- if (!nodeGcTime || !time?.avg) return { gc: undefined };
76
- const totalBenchTime = time.avg * samples.length;
77
- if (totalBenchTime <= 0) return { gc: undefined };
78
- const gcTime = nodeGcTime.inRun / totalBenchTime;
79
- // GC time can't exceed total time
80
- return { gc: gcTime <= 1 ? gcTime : undefined };
81
- },
82
- columns: (): ReportColumnGroup<GcSectionStats>[] => [
83
- {
84
- groupTitle: "gc",
85
- columns: [
86
- { key: "gc", title: "mean", formatter: percent, comparable: true },
87
- ],
88
- },
89
- ],
90
- };
91
-
92
- /** Section: detailed GC stats from --trace-gc-nvp (allocation, promotion, pauses) */
93
- export const gcStatsSection: ResultsMapper<GcStatsInfo> = {
94
- extract: (results: MeasuredResults) => {
95
- const { gcStats, samples } = results;
96
- if (!gcStats) return {};
97
- const iterations = samples.length || 1;
98
- const { totalAllocated, totalPromoted } = gcStats;
99
- const hasAlloc = totalAllocated && totalAllocated > 0;
100
- const promoPercent = hasAlloc
101
- ? (totalPromoted ?? 0) / totalAllocated
102
- : undefined;
103
- return {
104
- allocPerIter:
105
- totalAllocated != null ? totalAllocated / iterations : undefined,
106
- collected: gcStats.totalCollected || undefined,
107
- scavenges: gcStats.scavenges,
108
- fullGCs: gcStats.markCompacts,
109
- promoPercent,
110
- pausePerIter: gcStats.gcPauseTime / iterations,
111
- };
112
- },
113
- columns: (): ReportColumnGroup<GcStatsInfo>[] => [
114
- {
115
- groupTitle: "gc",
116
- columns: [
117
- { key: "allocPerIter", title: "alloc/iter", formatter: formatBytes },
118
- { key: "collected", title: "collected", formatter: formatBytes },
119
- { key: "scavenges", title: "scav", formatter: integer },
120
- { key: "fullGCs", title: "full", formatter: integer },
121
- { key: "promoPercent", title: "promo%", formatter: percent },
122
- { key: "pausePerIter", title: "pause/iter", formatter: timeMs },
123
- ],
124
- },
125
- ],
126
- };
127
-
128
- /** Browser GC section: only fields available from CDP tracing */
129
- export const browserGcStatsSection: ResultsMapper<GcStatsInfo> = {
130
- extract: gcStatsSection.extract,
131
- columns: (): ReportColumnGroup<GcStatsInfo>[] => [
132
- {
133
- groupTitle: "gc",
134
- columns: [
135
- { key: "collected", title: "collected", formatter: formatBytes },
136
- { key: "scavenges", title: "scav", formatter: integer },
137
- { key: "fullGCs", title: "full", formatter: integer },
138
- { key: "pausePerIter", title: "pause", formatter: timeMs },
139
- ],
140
- },
141
- ],
142
- };
143
-
144
- /** Section: CPU L1 cache miss rate and stall rate (requires @mitata/counters) */
145
- export const cpuSection: ResultsMapper<CpuStats> = {
146
- extract: (results: MeasuredResults) => ({
147
- cpuCacheMiss: results.cpuCacheMiss,
148
- cpuStall: results.cpuStall,
149
- }),
150
- columns: (): ReportColumnGroup<CpuStats>[] => [
151
- {
152
- groupTitle: "cpu",
153
- columns: [
154
- { key: "cpuCacheMiss", title: "L1 miss", formatter: percent },
155
- { key: "cpuStall", title: "stalls", formatter: percentPrecision(2) },
156
- ],
157
- },
158
- ],
159
- };
160
-
161
- /** Section: number of sample iterations */
162
- export const runsSection: ResultsMapper<RunStats> = {
163
- extract: (results: MeasuredResults) => ({
164
- runs: results.samples.length,
165
- }),
166
- columns: (): ReportColumnGroup<RunStats>[] => [
167
- { columns: [{ key: "runs", title: "runs", formatter: integer }] },
168
- ],
169
- };
170
-
171
- /** Section: total sampling duration in seconds (brackets if >= 30s) */
172
- export const totalTimeSection: ResultsMapper<{ totalTime?: number }> = {
173
- extract: (results: MeasuredResults) => ({
174
- totalTime: results.totalTime,
175
- }),
176
- columns: (): ReportColumnGroup<{ totalTime?: number }>[] => [
177
- {
178
- columns: [
179
- {
180
- key: "totalTime",
181
- title: "time",
182
- formatter: v => {
183
- if (typeof v !== "number") return "";
184
- return v >= 30 ? `[${v.toFixed(1)}s]` : `${v.toFixed(1)}s`;
185
- },
186
- },
187
- ],
188
- },
189
- ],
190
- };
191
-
192
- /** Section: median, mean, p99, and convergence for adaptive mode */
193
- export const adaptiveSection: ResultsMapper<AdaptiveStats> = {
194
- extract: (results: MeasuredResults) => ({
195
- median: results.time?.p50,
196
- mean: results.time?.avg,
197
- p99: results.time?.p99,
198
- convergence: results.convergence?.confidence,
199
- }),
200
- columns: (): ReportColumnGroup<AdaptiveStats>[] => [
201
- {
202
- groupTitle: "time",
203
- columns: [
204
- { key: "median", title: "median", formatter: timeMs, comparable: true },
205
- { key: "mean", title: "mean", formatter: timeMs, comparable: true },
206
- { key: "p99", title: "p99", formatter: timeMs },
207
- ],
208
- },
209
- {
210
- columns: [
211
- { key: "convergence", title: "conv%", formatter: formatConvergence },
212
- ],
213
- },
214
- ],
215
- };
216
-
217
- /** Section: V8 optimization tier distribution and deopt count */
218
- export const optSection: ResultsMapper<OptStats> = {
219
- extract: (results: MeasuredResults) => {
220
- const opt = results.optStatus;
221
- if (!opt) return {};
222
-
223
- const total = Object.values(opt.byTier).reduce((s, t) => s + t.count, 0);
224
- const tierParts = Object.entries(opt.byTier)
225
- .sort((a, b) => b[1].count - a[1].count)
226
- .map(([name, t]) => `${name}:${((t.count / total) * 100).toFixed(0)}%`);
227
-
228
- return {
229
- tiers: tierParts.join(" "),
230
- deopt: opt.deoptCount > 0 ? opt.deoptCount : undefined,
231
- };
232
- },
233
- columns: (): ReportColumnGroup<OptStats>[] => [
234
- {
235
- groupTitle: "v8 opt",
236
- columns: [
237
- {
238
- key: "tiers",
239
- title: "tiers",
240
- formatter: v => (typeof v === "string" ? v : ""),
241
- },
242
- {
243
- key: "deopt",
244
- title: "deopt",
245
- formatter: v => (typeof v === "number" ? String(v) : ""),
246
- },
247
- ],
248
- },
249
- ],
250
- };
251
-
252
- /** Build generic sections based on CLI flags */
253
- export function buildGenericSections(args: {
254
- "gc-stats"?: boolean;
255
- "heap-sample"?: boolean;
256
- }): ResultsMapper[] {
257
- const sections: ResultsMapper[] = [];
258
- if (args["gc-stats"]) sections.push(gcStatsSection);
259
- sections.push(runsSection);
260
- return sections;
261
- }
@@ -1,175 +0,0 @@
1
- /** Bootstrap estimate with confidence interval and raw resample data */
2
- export interface BootstrapResult {
3
- estimate: number;
4
- ci: [number, number];
5
- samples: number[];
6
- }
7
-
8
- export type CIDirection = "faster" | "slower" | "uncertain";
9
-
10
- /** Binned histogram for efficient transfer to browser */
11
- export interface HistogramBin {
12
- x: number; // bin center
13
- count: number;
14
- }
15
-
16
- /** Bootstrap confidence interval for percentage difference between two samples */
17
- export interface DifferenceCI {
18
- percent: number;
19
- ci: [number, number];
20
- direction: CIDirection;
21
- /** Histogram of bootstrap distribution for visualization */
22
- histogram?: HistogramBin[];
23
- }
24
-
25
- /** Options for bootstrap resampling methods */
26
- type BootstrapOptions = {
27
- resamples?: number;
28
- confidence?: number;
29
- };
30
- const confidence = 0.95;
31
- const outlierMultiplier = 1.5; // Tukey's fence multiplier
32
- const bootstrapSamples = 10000;
33
-
34
- /** @return relative standard deviation (coefficient of variation) */
35
- export function coefficientOfVariation(samples: number[]): number {
36
- const mean = average(samples);
37
- if (mean === 0) return 0;
38
- const stdDev = standardDeviation(samples);
39
- return stdDev / mean;
40
- }
41
-
42
- /** @return median absolute deviation for robust variability measure */
43
- export function medianAbsoluteDeviation(samples: number[]): number {
44
- const median = percentile(samples, 0.5);
45
- const deviations = samples.map(x => Math.abs(x - median));
46
- return percentile(deviations, 0.5);
47
- }
48
-
49
- /** @return outliers detected via Tukey's interquartile range method */
50
- export function findOutliers(samples: number[]): {
51
- rate: number;
52
- indices: number[];
53
- } {
54
- const q1 = percentile(samples, 0.25);
55
- const q3 = percentile(samples, 0.75);
56
- const iqr = q3 - q1;
57
- const lowerBound = q1 - outlierMultiplier * iqr;
58
- const upperBound = q3 + outlierMultiplier * iqr;
59
-
60
- const indices = samples
61
- .map((v, i) => (v < lowerBound || v > upperBound ? i : -1))
62
- .filter(i => i >= 0);
63
- return { rate: indices.length / samples.length, indices };
64
- }
65
-
66
- /** @return bootstrap confidence interval for median */
67
- export function bootstrapMedian(
68
- samples: number[],
69
- options: BootstrapOptions = {},
70
- ): BootstrapResult {
71
- const { resamples = bootstrapSamples, confidence: conf = confidence } =
72
- options;
73
- const medians = generateMedians(samples, resamples);
74
- const ci = computeInterval(medians, conf);
75
-
76
- return {
77
- estimate: percentile(samples, 0.5),
78
- ci,
79
- samples: medians,
80
- };
81
- }
82
-
83
- /** @return mean of values */
84
- export function average(values: number[]): number {
85
- const sum = values.reduce((a, b) => a + b, 0);
86
- return sum / values.length;
87
- }
88
-
89
- /** @return standard deviation with Bessel's correction */
90
- export function standardDeviation(samples: number[]): number {
91
- if (samples.length <= 1) return 0;
92
- const mean = average(samples);
93
- const variance =
94
- samples.reduce((sum, x) => sum + (x - mean) ** 2, 0) / (samples.length - 1);
95
- return Math.sqrt(variance);
96
- }
97
-
98
- /** @return value at percentile p (0-1) */
99
- export function percentile(values: number[], p: number): number {
100
- const sorted = [...values].sort((a, b) => a - b);
101
- const index = Math.ceil(sorted.length * p) - 1;
102
- return sorted[Math.max(0, index)];
103
- }
104
-
105
- /** @return bootstrap resample with replacement */
106
- export function createResample(samples: number[]): number[] {
107
- const n = samples.length;
108
- const rand = () => samples[Math.floor(Math.random() * n)];
109
- return Array.from({ length: n }, rand);
110
- }
111
-
112
- /** @return bootstrap CI for percentage difference between baseline and current medians */
113
- export function bootstrapDifferenceCI(
114
- baseline: number[],
115
- current: number[],
116
- options: BootstrapOptions = {},
117
- ): DifferenceCI {
118
- const { resamples = bootstrapSamples, confidence: conf = confidence } =
119
- options;
120
-
121
- const baselineMedian = percentile(baseline, 0.5);
122
- const currentMedian = percentile(current, 0.5);
123
- const observedPercent =
124
- ((currentMedian - baselineMedian) / baselineMedian) * 100;
125
-
126
- const diffs: number[] = [];
127
- for (let i = 0; i < resamples; i++) {
128
- const resB = createResample(baseline);
129
- const resC = createResample(current);
130
- const medB = percentile(resB, 0.5);
131
- const medC = percentile(resC, 0.5);
132
- diffs.push(((medC - medB) / medB) * 100);
133
- }
134
-
135
- const ci = computeInterval(diffs, conf);
136
- const excludesZero = ci[0] > 0 || ci[1] < 0;
137
- let direction: CIDirection = "uncertain";
138
- if (excludesZero) direction = observedPercent < 0 ? "faster" : "slower";
139
- const histogram = binValues(diffs);
140
- return { percent: observedPercent, ci, direction, histogram };
141
- }
142
-
143
- /** @return medians from bootstrap resamples */
144
- function generateMedians(samples: number[], resamples: number): number[] {
145
- return Array.from({ length: resamples }, () =>
146
- percentile(createResample(samples), 0.5),
147
- );
148
- }
149
-
150
- /** @return confidence interval [lower, upper] */
151
- function computeInterval(
152
- medians: number[],
153
- confidence: number,
154
- ): [number, number] {
155
- const alpha = (1 - confidence) / 2;
156
- const lower = percentile(medians, alpha);
157
- const upper = percentile(medians, 1 - alpha);
158
- return [lower, upper];
159
- }
160
-
161
- /** Bin values into histogram for compact visualization */
162
- function binValues(values: number[], binCount = 30): HistogramBin[] {
163
- const sorted = [...values].sort((a, b) => a - b);
164
- const min = sorted[0];
165
- const max = sorted[sorted.length - 1];
166
- if (min === max) return [{ x: min, count: values.length }];
167
-
168
- const step = (max - min) / binCount;
169
- const counts = new Array(binCount).fill(0);
170
- for (const v of values) {
171
- const bin = Math.min(Math.floor((v - min) / step), binCount - 1);
172
- counts[bin]++;
173
- }
174
- return counts.map((count, i) => ({ x: min + (i + 0.5) * step, count }));
175
- }
package/src/TypeUtil.ts DELETED
@@ -1,8 +0,0 @@
1
- /** Convert union to intersection - https://mighdoll.dev/blog/modern-typescript-intersection/ */
2
- export type UnionToIntersection<U> = (
3
- U extends any
4
- ? (k: U) => void
5
- : never
6
- ) extends (k: infer I extends U) => void
7
- ? I
8
- : never;
@@ -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
- /** Parse CDP trace events and aggregate into GcStats */
36
- export function browserGcStats(traceEvents: TraceEvent[]): GcStats {
37
- return aggregateGcStats(parseGcTraceEvents(traceEvents));
38
- }
39
-
40
- function gcType(name: string): GcEvent["type"] | undefined {
41
- if (name === "MinorGC") return "scavenge";
42
- if (name === "MajorGC") return "mark-compact";
43
- return undefined;
44
- }