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,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
- };
@@ -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,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
- 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
- }