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.
- package/LICENSE +20 -0
- package/README.md +99 -294
- 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-BzyUfiyB.d.mts → BenchRunner-DglX1NOn.d.mts} +119 -66
- 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 +711 -558
- 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 +77 -105
- 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 -27
- 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 -51
- 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 +132 -866
- 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 +64 -99
- package/src/export/SpeedscopeTypes.ts +98 -0
- package/src/export/TimeExport.ts +115 -0
- package/src/index.ts +86 -67
- 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 +49 -47
- package/src/matrix/MatrixInlineRunner.ts +50 -0
- package/src/matrix/MatrixReport.ts +90 -250
- package/src/matrix/VariantLoader.ts +5 -5
- 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 +1 -2
- package/src/{heap-sample → profiling/node}/ResolvedProfile.ts +18 -9
- 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 +116 -236
- package/src/runners/BenchRunner.ts +20 -15
- package/src/{Benchmark.ts → runners/BenchmarkSpec.ts} +5 -6
- package/src/runners/CreateRunner.ts +5 -7
- package/src/runners/GcStats.ts +47 -50
- 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 +127 -243
- 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 +135 -151
- package/src/stats/BootstrapDifference.ts +282 -0
- package/src/{PermutationTest.ts → stats/PermutationTest.ts} +8 -17
- 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 +2 -2
- package/src/{tests → test}/BenchMatrix.test.ts +19 -16
- 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 +14 -14
- package/src/{tests → test}/MatrixFilter.test.ts +1 -1
- 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 +39 -38
- 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 +12 -7
- 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 +33 -38
- 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/BrowserHeapSampler-B6asLKWQ.mjs +0 -202
- package/dist/BrowserHeapSampler-B6asLKWQ.mjs.map +0 -1
- package/dist/GcStats-wX7Xyblu.mjs +0 -77
- package/dist/GcStats-wX7Xyblu.mjs.map +0 -1
- package/dist/HeapSampler-B8dtKHn1.mjs.map +0 -1
- package/dist/TimingUtils-DwOwkc8G.mjs +0 -597
- package/dist/TimingUtils-DwOwkc8G.mjs.map +0 -1
- package/dist/browser/index.js +0 -914
- package/dist/src-B-DDaCa9.mjs +0 -3108
- package/dist/src-B-DDaCa9.mjs.map +0 -1
- package/src/BenchMatrix.ts +0 -380
- package/src/BenchmarkReport.ts +0 -161
- package/src/HtmlDataPrep.ts +0 -148
- package/src/StandardSections.ts +0 -261
- package/src/StatisticalUtils.ts +0 -175
- 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/export/SpeedscopeExport.ts +0 -202
- package/src/heap-sample/HeapSampleReport.ts +0 -269
- 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 -157
- 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 +0 -0
package/src/runners/GcStats.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* Aggregated GC statistics from V8 trace events.
|
|
3
|
+
* Node (--trace-gc-nvp) provides all fields; browser (CDP) provides counts, collected, and pause only.
|
|
4
|
+
*/
|
|
4
5
|
export interface GcStats {
|
|
5
6
|
scavenges: number;
|
|
6
7
|
markCompacts: number;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
/** Bytes freed by GC. */
|
|
9
|
+
totalCollected: number;
|
|
10
|
+
/** Total GC pause time in milliseconds. */
|
|
11
|
+
gcPauseTime: number;
|
|
12
|
+
/** Bytes allocated (Node only). */
|
|
13
|
+
totalAllocated?: number;
|
|
14
|
+
/** Bytes promoted to old generation (Node only). */
|
|
15
|
+
totalPromoted?: number;
|
|
16
|
+
/** Bytes survived in young generation (Node only). */
|
|
17
|
+
totalSurvived?: number;
|
|
12
18
|
}
|
|
13
19
|
|
|
14
20
|
/** Single GC event. Node provides all fields; browser provides type, pauseMs, collected. */
|
|
@@ -16,57 +22,58 @@ export interface GcEvent {
|
|
|
16
22
|
type: "scavenge" | "mark-compact" | "minor-ms" | "unknown";
|
|
17
23
|
pauseMs: number;
|
|
18
24
|
collected: number;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
25
|
+
/** Node only. */
|
|
26
|
+
allocated?: number;
|
|
27
|
+
/** Node only. */
|
|
28
|
+
promoted?: number;
|
|
29
|
+
/** Node only. */
|
|
30
|
+
survived?: number;
|
|
22
31
|
}
|
|
23
32
|
|
|
24
|
-
/** Parse a single --trace-gc-nvp stderr line */
|
|
33
|
+
/** Parse a single --trace-gc-nvp stderr line into a GcEvent. */
|
|
25
34
|
export function parseGcLine(line: string): GcEvent | undefined {
|
|
26
|
-
// V8 format: [pid:addr:gen] N ms: pause=X gc=s ... allocated=N promoted=N ...
|
|
27
35
|
if (!line.includes("pause=")) return undefined;
|
|
28
36
|
|
|
29
37
|
const fields = parseNvpFields(line);
|
|
30
38
|
if (!fields.gc) return undefined;
|
|
31
39
|
|
|
32
|
-
const
|
|
40
|
+
const intField = (name: string) => Number.parseInt(fields[name] || "0", 10);
|
|
33
41
|
const type = parseGcType(fields.gc);
|
|
34
42
|
const pauseMs = Number.parseFloat(fields.pause || "0");
|
|
35
|
-
const allocated = int("allocated");
|
|
36
|
-
const promoted = int("promoted");
|
|
37
|
-
// V8 uses "new_space_survived" not "survived"
|
|
38
|
-
const survived = int("new_space_survived") || int("survived");
|
|
39
|
-
// Calculate collected from start/end object size if available
|
|
40
|
-
const startSize = int("start_object_size");
|
|
41
|
-
const endSize = int("end_object_size");
|
|
42
|
-
const collected = startSize > endSize ? startSize - endSize : 0;
|
|
43
|
-
|
|
44
43
|
if (Number.isNaN(pauseMs)) return undefined;
|
|
45
44
|
|
|
45
|
+
const allocated = intField("allocated");
|
|
46
|
+
const promoted = intField("promoted");
|
|
47
|
+
// V8 uses "new_space_survived" not "survived"
|
|
48
|
+
const survived = intField("new_space_survived") || intField("survived");
|
|
49
|
+
const start = intField("start_object_size");
|
|
50
|
+
const end = intField("end_object_size");
|
|
51
|
+
const collected = start > end ? start - end : 0;
|
|
52
|
+
|
|
46
53
|
return { type, pauseMs, allocated, collected, promoted, survived };
|
|
47
54
|
}
|
|
48
55
|
|
|
49
|
-
/** Aggregate GC events into summary
|
|
56
|
+
/** Aggregate a list of GC events into summary statistics. */
|
|
50
57
|
export function aggregateGcStats(events: GcEvent[]): GcStats {
|
|
51
58
|
let scavenges = 0;
|
|
52
59
|
let markCompacts = 0;
|
|
53
60
|
let gcPauseTime = 0;
|
|
54
61
|
let totalCollected = 0;
|
|
55
|
-
let
|
|
62
|
+
let hasNode = false;
|
|
56
63
|
let totalAllocated = 0;
|
|
57
64
|
let totalPromoted = 0;
|
|
58
65
|
let totalSurvived = 0;
|
|
59
66
|
|
|
60
|
-
for (const
|
|
61
|
-
if (
|
|
62
|
-
else if (
|
|
63
|
-
gcPauseTime +=
|
|
64
|
-
totalCollected +=
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
totalAllocated +=
|
|
68
|
-
totalPromoted +=
|
|
69
|
-
totalSurvived +=
|
|
67
|
+
for (const event of events) {
|
|
68
|
+
if (event.type === "scavenge" || event.type === "minor-ms") scavenges++;
|
|
69
|
+
else if (event.type === "mark-compact") markCompacts++;
|
|
70
|
+
gcPauseTime += event.pauseMs;
|
|
71
|
+
totalCollected += event.collected;
|
|
72
|
+
if (event.allocated != null) {
|
|
73
|
+
hasNode = true;
|
|
74
|
+
totalAllocated += event.allocated;
|
|
75
|
+
totalPromoted += event.promoted ?? 0;
|
|
76
|
+
totalSurvived += event.survived ?? 0;
|
|
70
77
|
}
|
|
71
78
|
}
|
|
72
79
|
|
|
@@ -75,27 +82,17 @@ export function aggregateGcStats(events: GcEvent[]): GcStats {
|
|
|
75
82
|
markCompacts,
|
|
76
83
|
totalCollected,
|
|
77
84
|
gcPauseTime,
|
|
78
|
-
...(
|
|
85
|
+
...(hasNode && { totalAllocated, totalPromoted, totalSurvived }),
|
|
79
86
|
};
|
|
80
87
|
}
|
|
81
88
|
|
|
82
|
-
/**
|
|
83
|
-
export function emptyGcStats(): GcStats {
|
|
84
|
-
return { scavenges: 0, markCompacts: 0, totalCollected: 0, gcPauseTime: 0 };
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** Parse name=value pairs from trace-gc-nvp line */
|
|
89
|
+
/** Parse name=value pairs from a trace-gc-nvp line. */
|
|
88
90
|
function parseNvpFields(line: string): Record<string, string> {
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
const matches = line.matchAll(/(\w+)=([^\s,]+)/g);
|
|
92
|
-
for (const [, key, value] of matches) {
|
|
93
|
-
fields[key] = value;
|
|
94
|
-
}
|
|
95
|
-
return fields;
|
|
91
|
+
const pairs = [...line.matchAll(/(\w+)=([^\s,]+)/g)];
|
|
92
|
+
return Object.fromEntries(pairs.map(([, key, value]) => [key, value]));
|
|
96
93
|
}
|
|
97
94
|
|
|
98
|
-
/** Map V8 gc type codes to
|
|
95
|
+
/** Map V8 gc type codes to normalized event types. */
|
|
99
96
|
function parseGcType(gcField: string): GcEvent["type"] {
|
|
100
97
|
// V8 uses: s=scavenge, mc=mark-compact, mmc=minor-mc (young gen mark-compact)
|
|
101
98
|
if (gcField === "s" || gcField === "scavenge") return "scavenge";
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
instructions?: number;
|
|
8
|
-
cycles?: number;
|
|
9
|
-
branchMisses?: number;
|
|
10
|
-
}
|
|
1
|
+
import type { NavTiming } from "../profiling/browser/BrowserProfiler.ts";
|
|
2
|
+
import type { CoverageData } from "../profiling/node/CoverageTypes.ts";
|
|
3
|
+
import type { HeapProfile } from "../profiling/node/HeapSampler.ts";
|
|
4
|
+
import type { TimeProfile } from "../profiling/node/TimeSampler.ts";
|
|
5
|
+
import type { GcStats } from "../runners/GcStats.ts";
|
|
6
|
+
import type { NodeGCTime } from "../runners/NodeGC.ts";
|
|
11
7
|
|
|
12
8
|
/** Benchmark results: times in milliseconds, sizes in kilobytes */
|
|
13
9
|
export interface MeasuredResults {
|
|
@@ -16,7 +12,7 @@ export interface MeasuredResults {
|
|
|
16
12
|
/** Raw execution time samples for custom statistics */
|
|
17
13
|
samples: number[];
|
|
18
14
|
|
|
19
|
-
/** Warmup iteration timings (ms) - captured before gc/
|
|
15
|
+
/** Warmup iteration timings (ms) - captured before gc/pause-warmup */
|
|
20
16
|
warmupSamples?: number[];
|
|
21
17
|
|
|
22
18
|
/** Raw allocation samples per iteration (KB) */
|
|
@@ -25,10 +21,7 @@ export interface MeasuredResults {
|
|
|
25
21
|
/** Heap size per sample (bytes) - used for charts */
|
|
26
22
|
heapSamples?: number[];
|
|
27
23
|
|
|
28
|
-
/**
|
|
29
|
-
timestamps?: number[];
|
|
30
|
-
|
|
31
|
-
/** Execution time in milliseconds (measurement overhead excluded by mitata) */
|
|
24
|
+
/** Execution time in milliseconds */
|
|
32
25
|
time: {
|
|
33
26
|
min: number;
|
|
34
27
|
max: number;
|
|
@@ -51,36 +44,22 @@ export interface MeasuredResults {
|
|
|
51
44
|
max: number;
|
|
52
45
|
};
|
|
53
46
|
|
|
54
|
-
/**
|
|
55
|
-
* Time for explicit gc() call after test execution (milliseconds).
|
|
56
|
-
* Does not include GC time during test execution.
|
|
57
|
-
* Only reported by mitata runner.
|
|
58
|
-
*/
|
|
47
|
+
/** Time for explicit gc() call after test execution (ms), excludes in-run GC. */
|
|
59
48
|
gcTime?: {
|
|
60
49
|
avg: number;
|
|
61
50
|
min: number;
|
|
62
51
|
max: number;
|
|
63
52
|
};
|
|
64
53
|
|
|
65
|
-
/**
|
|
66
|
-
cpu?: CpuCounts;
|
|
67
|
-
|
|
68
|
-
/** L1 cache miss rate */
|
|
69
|
-
cpuCacheMiss?: number;
|
|
70
|
-
|
|
71
|
-
/** CPU stall rate (macOS only) */
|
|
72
|
-
cpuStall?: number;
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Stop-the-world GC time blocking main thread (milliseconds).
|
|
76
|
-
* Measured via Node's performance hooks when nodeObserveGC is true.
|
|
77
|
-
* Excludes parallel thread collection time and indirect slowdowns.
|
|
78
|
-
*/
|
|
54
|
+
/** Stop-the-world GC pause time (ms) via Node perf hooks (nodeObserveGC). */
|
|
79
55
|
nodeGcTime?: NodeGCTime;
|
|
80
56
|
|
|
81
57
|
/** Total time spent collecting samples (seconds) */
|
|
82
58
|
totalTime?: number;
|
|
83
59
|
|
|
60
|
+
/** Monotonic start time (μs, hrtime-based) for Perfetto trace alignment. */
|
|
61
|
+
startTime?: number;
|
|
62
|
+
|
|
84
63
|
/** Convergence information for adaptive mode */
|
|
85
64
|
convergence?: {
|
|
86
65
|
converged: boolean;
|
|
@@ -97,18 +76,28 @@ export interface MeasuredResults {
|
|
|
97
76
|
/** Points where pauses occurred for V8 optimization */
|
|
98
77
|
pausePoints?: PausePoint[];
|
|
99
78
|
|
|
79
|
+
/** Batch boundaries for block bootstrap (indices into samples where each batch starts) */
|
|
80
|
+
batchOffsets?: number[];
|
|
81
|
+
|
|
100
82
|
/** GC stats from V8's --trace-gc-nvp (requires --gc-stats and worker mode) */
|
|
101
83
|
gcStats?: GcStats;
|
|
102
84
|
|
|
103
85
|
/** Heap sampling allocation profile (requires --heap-sample and worker mode) */
|
|
104
86
|
heapProfile?: HeapProfile;
|
|
87
|
+
|
|
88
|
+
/** V8 CPU time sampling profile (requires --profile and worker mode) */
|
|
89
|
+
timeProfile?: TimeProfile;
|
|
90
|
+
|
|
91
|
+
/** Per-function execution counts (requires --call-counts) */
|
|
92
|
+
coverage?: CoverageData;
|
|
93
|
+
|
|
94
|
+
/** Navigation timings from page-load mode (one per iteration) */
|
|
95
|
+
navTimings?: NavTiming[];
|
|
105
96
|
}
|
|
106
97
|
|
|
107
|
-
/**
|
|
98
|
+
/** Pause inserted during sample collection for V8 optimization settling. */
|
|
108
99
|
export interface PausePoint {
|
|
109
|
-
/** Sample index where pause occurred (after this iteration) */
|
|
110
100
|
sampleIndex: number;
|
|
111
|
-
/** Pause duration in milliseconds */
|
|
112
101
|
durationMs: number;
|
|
113
102
|
}
|
|
114
103
|
|
|
@@ -125,3 +114,20 @@ export interface OptStatusInfo {
|
|
|
125
114
|
/** Number of samples with deopt flag set */
|
|
126
115
|
deoptCount: number;
|
|
127
116
|
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* V8 GetOptimizationStatus() return codes ==> human-readable tier names.
|
|
120
|
+
* Bit 0 (1): is_function
|
|
121
|
+
* Bit 4 (16): is_optimized (TurboFan)
|
|
122
|
+
* Bit 5 (32): is_optimized (Maglev)
|
|
123
|
+
* Bit 7 (128): is_baseline (Sparkplug)
|
|
124
|
+
* Bit 3 (8): maybe_deoptimized
|
|
125
|
+
*/
|
|
126
|
+
export const optStatusNames: Record<number, string> = {
|
|
127
|
+
1: "interpreted",
|
|
128
|
+
129: "sparkplug", // 1 + 128
|
|
129
|
+
17: "turbofan", // 1 + 16
|
|
130
|
+
33: "maglev", // 1 + 32
|
|
131
|
+
49: "turbofan+maglev", // 1 + 16 + 32
|
|
132
|
+
32769: "optimized", // common optimized status
|
|
133
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { GcStats } from "./GcStats.ts";
|
|
2
|
+
import type { MeasuredResults } from "./MeasuredResults.ts";
|
|
3
|
+
import { computeStats } from "./SampleStats.ts";
|
|
4
|
+
|
|
5
|
+
/** Progress update emitted after each batch run. */
|
|
6
|
+
export interface BatchProgress {
|
|
7
|
+
batch: number;
|
|
8
|
+
batches: number;
|
|
9
|
+
label: "baseline" | "current";
|
|
10
|
+
elapsed: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type SamplesFn = (r: MeasuredResults) => number[] | undefined;
|
|
14
|
+
|
|
15
|
+
/** Merge multiple batch results, concatenating samples and tracking batch boundaries. */
|
|
16
|
+
export function mergeBatchResults(results: MeasuredResults[]): MeasuredResults {
|
|
17
|
+
if (results.length === 0) {
|
|
18
|
+
throw new Error("Cannot merge empty results array");
|
|
19
|
+
}
|
|
20
|
+
if (results.length === 1) return { ...results[0], batchOffsets: [0] };
|
|
21
|
+
|
|
22
|
+
const allSamples = results.flatMap(r => r.samples);
|
|
23
|
+
const time = computeStats(allSamples);
|
|
24
|
+
|
|
25
|
+
const batchOffsets: number[] = [];
|
|
26
|
+
const offsetPauses: MeasuredResults["pausePoints"] = [];
|
|
27
|
+
let offset = 0;
|
|
28
|
+
for (const r of results) {
|
|
29
|
+
batchOffsets.push(offset);
|
|
30
|
+
for (const p of r.pausePoints ?? []) {
|
|
31
|
+
const sampleIndex = p.sampleIndex + offset;
|
|
32
|
+
offsetPauses.push({ sampleIndex, durationMs: p.durationMs });
|
|
33
|
+
}
|
|
34
|
+
offset += r.samples.length;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// last batch as base ==> new MeasuredResults fields get "take last"
|
|
38
|
+
// semantics by default instead of silently disappearing
|
|
39
|
+
return {
|
|
40
|
+
...results[results.length - 1],
|
|
41
|
+
name: results[0].name,
|
|
42
|
+
samples: allSamples,
|
|
43
|
+
warmupSamples: concatOptional(results, r => r.warmupSamples),
|
|
44
|
+
allocationSamples: concatOptional(results, r => r.allocationSamples),
|
|
45
|
+
heapSamples: concatOptional(results, r => r.heapSamples),
|
|
46
|
+
optSamples: concatOptional(results, r => r.optSamples),
|
|
47
|
+
time,
|
|
48
|
+
startTime: results[0].startTime,
|
|
49
|
+
totalTime: results.reduce((sum, r) => sum + (r.totalTime || 0), 0),
|
|
50
|
+
pausePoints: offsetPauses.length ? offsetPauses : undefined,
|
|
51
|
+
batchOffsets,
|
|
52
|
+
gcStats: mergeGcStats(results),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Sum GcStats across batches, or undefined if none collected. */
|
|
57
|
+
export function mergeGcStats(
|
|
58
|
+
results: { gcStats?: GcStats }[],
|
|
59
|
+
): GcStats | undefined {
|
|
60
|
+
const stats = results.map(r => r.gcStats).filter(Boolean) as GcStats[];
|
|
61
|
+
if (!stats.length) return undefined;
|
|
62
|
+
const sum = (fn: (s: GcStats) => number | undefined) =>
|
|
63
|
+
stats.reduce((acc, s) => acc + (fn(s) ?? 0), 0);
|
|
64
|
+
return {
|
|
65
|
+
scavenges: sum(s => s.scavenges),
|
|
66
|
+
markCompacts: sum(s => s.markCompacts),
|
|
67
|
+
totalCollected: sum(s => s.totalCollected),
|
|
68
|
+
gcPauseTime: sum(s => s.gcPauseTime),
|
|
69
|
+
totalAllocated: sum(s => s.totalAllocated) || undefined,
|
|
70
|
+
totalPromoted: sum(s => s.totalPromoted) || undefined,
|
|
71
|
+
totalSurvived: sum(s => s.totalSurvived) || undefined,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Run N benchmarks + optional baseline in batched alternation, merge results. */
|
|
76
|
+
export async function runBatched(
|
|
77
|
+
runners: (() => Promise<MeasuredResults>)[],
|
|
78
|
+
baseline: (() => Promise<MeasuredResults>) | undefined,
|
|
79
|
+
batches: number,
|
|
80
|
+
warmupBatch = false,
|
|
81
|
+
onProgress?: (p: BatchProgress) => void,
|
|
82
|
+
): Promise<{ results: MeasuredResults[]; baseline?: MeasuredResults }> {
|
|
83
|
+
const runnerBatches: MeasuredResults[][] = runners.map(() => []);
|
|
84
|
+
const baselineBatches: MeasuredResults[] = [];
|
|
85
|
+
const start = performance.now();
|
|
86
|
+
|
|
87
|
+
const report = (batch: number, label: BatchProgress["label"]) =>
|
|
88
|
+
onProgress?.({ batch, batches, label, elapsed: performance.now() - start });
|
|
89
|
+
|
|
90
|
+
for (let i = 0; i < batches; i++) {
|
|
91
|
+
const reverse = i % 2 === 1;
|
|
92
|
+
// baseline runs before benchmarks on even batches, after on odd (alternation)
|
|
93
|
+
if (!reverse && baseline) {
|
|
94
|
+
baselineBatches.push(await baseline());
|
|
95
|
+
report(i, "baseline");
|
|
96
|
+
}
|
|
97
|
+
for (let j = 0; j < runners.length; j++) {
|
|
98
|
+
runnerBatches[j].push(await runners[j]());
|
|
99
|
+
report(i, "current");
|
|
100
|
+
}
|
|
101
|
+
if (reverse && baseline) {
|
|
102
|
+
baselineBatches.push(await baseline());
|
|
103
|
+
report(i, "baseline");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!warmupBatch && batches > 1) {
|
|
108
|
+
for (const b of runnerBatches) b.shift();
|
|
109
|
+
baselineBatches.shift();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const results = runnerBatches.map(b => mergeBatchResults(b));
|
|
113
|
+
const mergedBaseline = baselineBatches.length
|
|
114
|
+
? mergeBatchResults(baselineBatches)
|
|
115
|
+
: undefined;
|
|
116
|
+
return { results, baseline: mergedBaseline };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Concat optional number arrays across batches. */
|
|
120
|
+
function concatOptional(results: MeasuredResults[], fn: SamplesFn) {
|
|
121
|
+
const all = results.flatMap(r => fn(r) || []);
|
|
122
|
+
return all.length ? all : undefined;
|
|
123
|
+
}
|
|
@@ -31,8 +31,7 @@ export function analyzeGCEntries(
|
|
|
31
31
|
let collects = 0;
|
|
32
32
|
const events: GcEvent[] = [];
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
const { duration, startTime } = record;
|
|
34
|
+
for (const { duration, startTime } of gcRecords) {
|
|
36
35
|
if (startTime < start) {
|
|
37
36
|
before += duration;
|
|
38
37
|
} else if (startTime > end) {
|
|
@@ -42,7 +41,7 @@ export function analyzeGCEntries(
|
|
|
42
41
|
collects++;
|
|
43
42
|
events.push({ offset: startTime - start, duration });
|
|
44
43
|
}
|
|
45
|
-
}
|
|
44
|
+
}
|
|
46
45
|
const total = inRun + before + after;
|
|
47
46
|
return { inRun, before, after, total, collects, events };
|
|
48
47
|
}
|