benchforge 0.1.9 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +99 -260
- package/bin/benchforge +1 -2
- package/dist/AnalyzeArchive-8NCJhmhS.mjs +145 -0
- package/dist/AnalyzeArchive-8NCJhmhS.mjs.map +1 -0
- package/dist/BenchMatrix-BZVrBB_h.mjs +1050 -0
- package/dist/BenchMatrix-BZVrBB_h.mjs.map +1 -0
- package/dist/BenchRunner-DglX1NOn.d.mts +302 -0
- package/dist/CoverageSampler-D5T9DRqe.mjs +27 -0
- package/dist/CoverageSampler-D5T9DRqe.mjs.map +1 -0
- package/dist/Formatters-BWj3d4sv.mjs +95 -0
- package/dist/Formatters-BWj3d4sv.mjs.map +1 -0
- package/dist/{HeapSampler-B8dtKHn1.mjs → HeapSampler-Dq-hpXem.mjs} +4 -4
- package/dist/HeapSampler-Dq-hpXem.mjs.map +1 -0
- package/dist/RunBenchCLI-C17DrJz8.mjs +3075 -0
- package/dist/RunBenchCLI-C17DrJz8.mjs.map +1 -0
- package/dist/StatisticalUtils-BD92crgM.mjs +255 -0
- package/dist/StatisticalUtils-BD92crgM.mjs.map +1 -0
- package/dist/TimeSampler-Ds8n7l2B.mjs +29 -0
- package/dist/TimeSampler-Ds8n7l2B.mjs.map +1 -0
- package/dist/ViewerServer-BJhdnxlN.mjs +639 -0
- package/dist/ViewerServer-BJhdnxlN.mjs.map +1 -0
- package/dist/ViewerServer-CuMNdNBz.mjs +2 -0
- package/dist/bin/benchforge.mjs +4 -5
- package/dist/bin/benchforge.mjs.map +1 -1
- package/dist/index.d.mts +731 -522
- package/dist/index.mjs +98 -3
- package/dist/index.mjs.map +1 -0
- package/dist/runners/WorkerScript.d.mts +12 -4
- package/dist/runners/WorkerScript.mjs +92 -120
- package/dist/runners/WorkerScript.mjs.map +1 -1
- package/dist/viewer/assets/CIPlot-BkOvMoMa.js +1 -0
- package/dist/viewer/assets/HistogramKde-CmSyUFY0.js +1 -0
- package/dist/viewer/assets/LegendUtils-BJpbn_jr.js +55 -0
- package/dist/viewer/assets/SampleTimeSeries-C4VBhXr3.js +1 -0
- package/dist/viewer/assets/index-Br9bp_cX.js +153 -0
- package/dist/viewer/assets/index-NzXXe_CC.css +1 -0
- package/dist/viewer/index.html +19 -0
- package/dist/viewer/speedscope/LICENSE +21 -0
- package/dist/viewer/speedscope/SourceCodePro-Regular.ttf-ILST5JV6.woff2 +0 -0
- package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js +2 -0
- package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js.map +7 -0
- package/dist/viewer/speedscope/favicon-16x16-VSI62OPJ.png +0 -0
- package/dist/viewer/speedscope/favicon-32x32-3EB2YCUY.png +0 -0
- package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js +2 -0
- package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js.map +7 -0
- package/dist/viewer/speedscope/favicon-FOKUP5Y5.ico +0 -0
- package/dist/viewer/speedscope/favicon-M34RF7BI.js +2 -0
- package/dist/viewer/speedscope/favicon-M34RF7BI.js.map +7 -0
- package/dist/viewer/speedscope/file-format-schema.json +274 -0
- package/dist/viewer/speedscope/index.html +19 -0
- package/dist/viewer/speedscope/jfrview_bg-BLJXNNQB.wasm +0 -0
- package/dist/viewer/speedscope/perf-vertx-stacks-01-collapsed-all-ZNUIGAJL.txt +199 -0
- package/dist/viewer/speedscope/release.txt +3 -0
- package/dist/viewer/speedscope/source-code-pro.LICENSE.md +93 -0
- package/dist/viewer/speedscope/speedscope-GHPHNKXC.css +2 -0
- package/dist/viewer/speedscope/speedscope-GHPHNKXC.css.map +7 -0
- package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js +212 -0
- package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js.map +7 -0
- package/package.json +52 -26
- package/src/bin/benchforge.ts +2 -2
- package/src/cli/AnalyzeArchive.ts +232 -0
- package/src/cli/BrowserBench.ts +322 -0
- package/src/cli/CliArgs.ts +164 -48
- package/src/cli/CliExport.ts +179 -0
- package/src/cli/CliOptions.ts +147 -0
- package/src/cli/CliReport.ts +197 -0
- package/src/cli/FilterBenchmarks.ts +18 -30
- package/src/cli/RunBenchCLI.ts +138 -844
- package/src/cli/SuiteRunner.ts +160 -0
- package/src/cli/ViewerServer.ts +282 -0
- package/src/export/AllocExport.ts +121 -0
- package/src/export/ArchiveExport.ts +146 -0
- package/src/export/ArchiveFormat.ts +50 -0
- package/src/export/CoverageExport.ts +148 -0
- package/src/export/EditorUri.ts +10 -0
- package/src/export/PerfettoExport.ts +91 -126
- package/src/export/SpeedscopeTypes.ts +98 -0
- package/src/export/TimeExport.ts +115 -0
- package/src/index.ts +87 -62
- package/src/matrix/BenchMatrix.ts +230 -0
- package/src/matrix/CaseLoader.ts +8 -6
- package/src/matrix/MatrixDirRunner.ts +153 -0
- package/src/matrix/MatrixFilter.ts +55 -53
- package/src/matrix/MatrixInlineRunner.ts +50 -0
- package/src/matrix/MatrixReport.ts +94 -254
- package/src/matrix/VariantLoader.ts +9 -9
- package/src/profiling/browser/BenchLoop.ts +51 -0
- package/src/profiling/browser/BrowserCDP.ts +133 -0
- package/src/profiling/browser/BrowserGcStats.ts +33 -0
- package/src/profiling/browser/BrowserProfiler.ts +160 -0
- package/src/profiling/browser/CdpClient.ts +82 -0
- package/src/profiling/browser/CdpPage.ts +138 -0
- package/src/profiling/browser/ChromeLauncher.ts +158 -0
- package/src/profiling/browser/ChromeTraceEvent.ts +28 -0
- package/src/profiling/browser/PageLoadMode.ts +61 -0
- package/src/profiling/node/CoverageSampler.ts +27 -0
- package/src/profiling/node/CoverageTypes.ts +23 -0
- package/src/profiling/node/HeapSampleReport.ts +261 -0
- package/src/{heap-sample → profiling/node}/HeapSampler.ts +55 -13
- package/src/profiling/node/ResolvedProfile.ts +98 -0
- package/src/profiling/node/TimeSampler.ts +57 -0
- package/src/report/BenchmarkReport.ts +146 -0
- package/src/report/Colors.ts +9 -0
- package/src/report/Formatters.ts +110 -0
- package/src/report/GcSections.ts +151 -0
- package/src/{GitUtils.ts → report/GitUtils.ts} +18 -19
- package/src/report/HtmlReport.ts +223 -0
- package/src/report/ParseStats.ts +73 -0
- package/src/report/StandardSections.ts +147 -0
- package/src/report/ViewerSections.ts +286 -0
- package/src/report/text/TableReport.ts +253 -0
- package/src/report/text/TextReport.ts +123 -0
- package/src/runners/AdaptiveWrapper.ts +167 -287
- package/src/runners/BenchRunner.ts +27 -22
- package/src/{Benchmark.ts → runners/BenchmarkSpec.ts} +5 -6
- package/src/runners/CreateRunner.ts +5 -7
- package/src/runners/GcStats.ts +58 -61
- package/src/{MeasuredResults.ts → runners/MeasuredResults.ts} +43 -37
- package/src/runners/MergeBatches.ts +123 -0
- package/src/{NodeGC.ts → runners/NodeGC.ts} +2 -3
- package/src/runners/RunnerOrchestrator.ts +180 -296
- package/src/runners/RunnerUtils.ts +75 -1
- package/src/runners/SampleStats.ts +100 -0
- package/src/runners/TimingRunner.ts +244 -0
- package/src/runners/TimingUtils.ts +3 -2
- package/src/runners/WorkerScript.ts +162 -178
- package/src/stats/BootstrapDifference.ts +282 -0
- package/src/{PermutationTest.ts → stats/PermutationTest.ts} +31 -40
- package/src/stats/StatisticalUtils.ts +445 -0
- package/src/{tests → test}/AdaptiveConvergence.test.ts +10 -10
- package/src/test/AdaptiveRunner.test.ts +39 -41
- package/src/{tests → test}/AdaptiveSampling.test.ts +9 -9
- package/src/test/AdaptiveStatistics.integration.ts +9 -41
- package/src/{tests → test}/BenchMatrix.test.ts +31 -28
- package/src/test/BenchmarkReport.test.ts +63 -13
- package/src/test/BrowserBench.e2e.test.ts +186 -17
- package/src/test/BrowserBench.test.ts +10 -5
- package/src/test/BuildTimeSection.test.ts +130 -0
- package/src/test/CapSamples.test.ts +82 -0
- package/src/test/CoverageExport.test.ts +115 -0
- package/src/test/CoverageSampler.test.ts +33 -0
- package/src/test/HeapAttribution.test.ts +51 -0
- package/src/{tests → test}/MatrixFilter.test.ts +16 -16
- package/src/{tests → test}/MatrixReport.test.ts +1 -1
- package/src/test/PermutationTest.test.ts +1 -1
- package/src/{tests → test}/RealDataValidation.test.ts +6 -6
- package/src/test/RunBenchCLI.test.ts +57 -56
- package/src/test/RunnerOrchestrator.test.ts +12 -12
- package/src/test/StatisticalUtils.test.ts +48 -12
- package/src/{table-util/test → test}/TableReport.test.ts +2 -2
- package/src/test/TestUtils.ts +35 -30
- package/src/test/TimeExport.test.ts +139 -0
- package/src/test/TimeSampler.test.ts +37 -0
- package/src/test/ViewerLive.e2e.test.ts +159 -0
- package/src/test/ViewerStatic.static.e2e.test.ts +137 -0
- package/src/{tests → test}/fixtures/baseline/impl.ts +1 -1
- package/src/{tests → test}/fixtures/bevy30-samples.ts +3 -1
- package/src/test/fixtures/cases/asyncCases.ts +9 -0
- package/src/{tests → test}/fixtures/cases/cases.ts +5 -2
- package/src/test/fixtures/cases/variants/product.ts +2 -0
- package/src/test/fixtures/cases/variants/sum.ts +2 -0
- package/src/test/fixtures/discover/fast.ts +1 -0
- package/src/{tests → test}/fixtures/discover/slow.ts +1 -1
- package/src/test/fixtures/invalid/bad.ts +1 -0
- package/src/test/fixtures/loader/fast.ts +1 -0
- package/src/{tests → test}/fixtures/loader/slow.ts +1 -1
- package/src/test/fixtures/loader/stateful.ts +2 -0
- package/src/test/fixtures/stateful/stateful.ts +2 -0
- package/src/test/fixtures/variants/extra.ts +1 -0
- package/src/test/fixtures/variants/impl.ts +1 -0
- package/src/test/fixtures/worker/fast.ts +1 -0
- package/src/{tests → test}/fixtures/worker/slow.ts +1 -1
- package/src/viewer/DateFormat.ts +30 -0
- package/src/viewer/Helpers.ts +23 -0
- package/src/viewer/LineData.ts +120 -0
- package/src/viewer/Providers.ts +191 -0
- package/src/viewer/ReportData.ts +123 -0
- package/src/viewer/State.ts +49 -0
- package/src/viewer/Theme.ts +15 -0
- package/src/viewer/components/App.tsx +73 -0
- package/src/viewer/components/DropZone.tsx +71 -0
- package/src/viewer/components/LazyPlot.ts +33 -0
- package/src/viewer/components/SamplesPanel.tsx +214 -0
- package/src/viewer/components/Shell.tsx +26 -0
- package/src/viewer/components/SourcePanel.tsx +216 -0
- package/src/viewer/components/SummaryPanel.tsx +332 -0
- package/src/viewer/components/TabBar.tsx +131 -0
- package/src/viewer/components/TabContent.tsx +46 -0
- package/src/viewer/components/ThemeToggle.tsx +50 -0
- package/src/viewer/index.html +20 -0
- package/src/viewer/main.tsx +4 -0
- package/src/viewer/plots/CIPlot.ts +313 -0
- package/src/{html/browser → viewer/plots}/HistogramKde.ts +42 -47
- package/src/viewer/plots/LegendUtils.ts +134 -0
- package/src/viewer/plots/PlotTypes.ts +85 -0
- package/src/viewer/plots/RenderPlots.ts +230 -0
- package/src/viewer/plots/SampleTimeSeries.ts +306 -0
- package/src/viewer/plots/SvgHelpers.ts +136 -0
- package/src/viewer/plots/TimeSeriesMarks.ts +319 -0
- package/src/viewer/report.css +427 -0
- package/src/viewer/shell.css +357 -0
- package/src/viewer/tsconfig.json +11 -0
- package/dist/BenchRunner-CSKN9zPy.d.mts +0 -225
- package/dist/BrowserHeapSampler-DCeL42RE.mjs +0 -202
- package/dist/BrowserHeapSampler-DCeL42RE.mjs.map +0 -1
- package/dist/GcStats-ByEovUi1.mjs +0 -77
- package/dist/GcStats-ByEovUi1.mjs.map +0 -1
- package/dist/HeapSampler-B8dtKHn1.mjs.map +0 -1
- package/dist/TimingUtils-ClclVQ7E.mjs +0 -597
- package/dist/TimingUtils-ClclVQ7E.mjs.map +0 -1
- package/dist/browser/index.js +0 -914
- package/dist/src-Cf_LXwlp.mjs +0 -2873
- package/dist/src-Cf_LXwlp.mjs.map +0 -1
- package/src/BenchMatrix.ts +0 -380
- package/src/BenchmarkReport.ts +0 -156
- package/src/HtmlDataPrep.ts +0 -148
- package/src/StandardSections.ts +0 -261
- package/src/StatisticalUtils.ts +0 -176
- package/src/TypeUtil.ts +0 -8
- package/src/browser/BrowserGcStats.ts +0 -44
- package/src/browser/BrowserHeapSampler.ts +0 -271
- package/src/export/JsonExport.ts +0 -103
- package/src/export/JsonFormat.ts +0 -91
- package/src/heap-sample/HeapSampleReport.ts +0 -196
- package/src/html/HtmlReport.ts +0 -131
- package/src/html/HtmlTemplate.ts +0 -284
- package/src/html/Types.ts +0 -88
- package/src/html/browser/CIPlot.ts +0 -287
- package/src/html/browser/LegendUtils.ts +0 -163
- package/src/html/browser/RenderPlots.ts +0 -263
- package/src/html/browser/SampleTimeSeries.ts +0 -389
- package/src/html/browser/Types.ts +0 -96
- package/src/html/browser/index.ts +0 -1
- package/src/html/index.ts +0 -17
- package/src/runners/BasicRunner.ts +0 -364
- package/src/table-util/ConvergenceFormatters.ts +0 -19
- package/src/table-util/Formatters.ts +0 -152
- package/src/table-util/README.md +0 -70
- package/src/table-util/TableReport.ts +0 -293
- package/src/tests/fixtures/cases/asyncCases.ts +0 -7
- package/src/tests/fixtures/cases/variants/product.ts +0 -2
- package/src/tests/fixtures/cases/variants/sum.ts +0 -2
- package/src/tests/fixtures/discover/fast.ts +0 -1
- package/src/tests/fixtures/invalid/bad.ts +0 -1
- package/src/tests/fixtures/loader/fast.ts +0 -1
- package/src/tests/fixtures/loader/stateful.ts +0 -2
- package/src/tests/fixtures/stateful/stateful.ts +0 -2
- package/src/tests/fixtures/variants/extra.ts +0 -1
- package/src/tests/fixtures/variants/impl.ts +0 -1
- package/src/tests/fixtures/worker/fast.ts +0 -1
- package/src/{table-util/test → test}/TableValueExtractor.test.ts +0 -0
- package/src/{table-util/test → test}/TableValueExtractor.ts +9 -9
|
@@ -2,8 +2,8 @@ import { expect, test } from "vitest";
|
|
|
2
2
|
import {
|
|
3
3
|
browserGcStats,
|
|
4
4
|
parseGcTraceEvents,
|
|
5
|
-
|
|
6
|
-
} from "../browser/
|
|
5
|
+
} from "../profiling/browser/BrowserGcStats.ts";
|
|
6
|
+
import type { TraceEvent } from "../profiling/browser/ChromeTraceEvent.ts";
|
|
7
7
|
|
|
8
8
|
test("parseGcTraceEvents parses MinorGC and MajorGC events", () => {
|
|
9
9
|
const events: TraceEvent[] = [
|
|
@@ -11,6 +11,7 @@ test("parseGcTraceEvents parses MinorGC and MajorGC events", () => {
|
|
|
11
11
|
cat: "v8.gc",
|
|
12
12
|
name: "MinorGC",
|
|
13
13
|
ph: "X",
|
|
14
|
+
ts: 0,
|
|
14
15
|
dur: 500,
|
|
15
16
|
args: { usedHeapSizeBefore: 10000, usedHeapSizeAfter: 8000 },
|
|
16
17
|
},
|
|
@@ -18,6 +19,7 @@ test("parseGcTraceEvents parses MinorGC and MajorGC events", () => {
|
|
|
18
19
|
cat: "v8.gc",
|
|
19
20
|
name: "MajorGC",
|
|
20
21
|
ph: "X",
|
|
22
|
+
ts: 0,
|
|
21
23
|
dur: 12000,
|
|
22
24
|
args: { usedHeapSizeBefore: 50000, usedHeapSizeAfter: 30000 },
|
|
23
25
|
},
|
|
@@ -38,9 +40,9 @@ test("parseGcTraceEvents parses MinorGC and MajorGC events", () => {
|
|
|
38
40
|
|
|
39
41
|
test("parseGcTraceEvents ignores non-complete and non-GC events", () => {
|
|
40
42
|
const events: TraceEvent[] = [
|
|
41
|
-
{ cat: "v8.gc", name: "MinorGC", ph: "B", dur: 500 }, // not complete
|
|
42
|
-
{ cat: "v8", name: "V8.Execute", ph: "X", dur: 100 }, // not GC
|
|
43
|
-
{ cat: "v8.gc", name: "MinorGC", ph: "X" }, // valid, missing dur/args
|
|
43
|
+
{ cat: "v8.gc", name: "MinorGC", ph: "B", ts: 0, dur: 500 }, // not complete
|
|
44
|
+
{ cat: "v8", name: "V8.Execute", ph: "X", ts: 0, dur: 100 }, // not GC
|
|
45
|
+
{ cat: "v8.gc", name: "MinorGC", ph: "X", ts: 0 }, // valid, missing dur/args
|
|
44
46
|
];
|
|
45
47
|
const parsed = parseGcTraceEvents(events);
|
|
46
48
|
expect(parsed).toHaveLength(1);
|
|
@@ -53,6 +55,7 @@ test("browserGcStats aggregates trace events into GcStats", () => {
|
|
|
53
55
|
cat: "v8.gc",
|
|
54
56
|
name: "MinorGC",
|
|
55
57
|
ph: "X",
|
|
58
|
+
ts: 0,
|
|
56
59
|
dur: 300,
|
|
57
60
|
args: { usedHeapSizeBefore: 5000, usedHeapSizeAfter: 3000 },
|
|
58
61
|
},
|
|
@@ -60,6 +63,7 @@ test("browserGcStats aggregates trace events into GcStats", () => {
|
|
|
60
63
|
cat: "v8.gc",
|
|
61
64
|
name: "MinorGC",
|
|
62
65
|
ph: "X",
|
|
66
|
+
ts: 0,
|
|
63
67
|
dur: 200,
|
|
64
68
|
args: { usedHeapSizeBefore: 6000, usedHeapSizeAfter: 4000 },
|
|
65
69
|
},
|
|
@@ -67,6 +71,7 @@ test("browserGcStats aggregates trace events into GcStats", () => {
|
|
|
67
71
|
cat: "v8.gc",
|
|
68
72
|
name: "MajorGC",
|
|
69
73
|
ph: "X",
|
|
74
|
+
ts: 0,
|
|
70
75
|
dur: 8000,
|
|
71
76
|
args: { usedHeapSizeBefore: 40000, usedHeapSizeAfter: 20000 },
|
|
72
77
|
},
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import type { BenchmarkReport } from "../report/BenchmarkReport.ts";
|
|
3
|
+
import { computeColumnValues } from "../report/BenchmarkReport.ts";
|
|
4
|
+
import { buildTimeSection } from "../report/StandardSections.ts";
|
|
5
|
+
import { reportResults, valuesForReports } from "../report/text/TextReport.ts";
|
|
6
|
+
import type { MeasuredResults } from "../runners/MeasuredResults.ts";
|
|
7
|
+
|
|
8
|
+
/** @return minimal MeasuredResults with the given samples (time fields derived trivially). */
|
|
9
|
+
function measured(samples: number[]): MeasuredResults {
|
|
10
|
+
const sorted = [...samples].sort((a, b) => a - b);
|
|
11
|
+
return {
|
|
12
|
+
name: "t",
|
|
13
|
+
samples,
|
|
14
|
+
time: {
|
|
15
|
+
min: sorted[0],
|
|
16
|
+
max: sorted[sorted.length - 1],
|
|
17
|
+
avg: samples.reduce((a, b) => a + b, 0) / samples.length,
|
|
18
|
+
p50: sorted[Math.floor(sorted.length * 0.5)],
|
|
19
|
+
p75: sorted[Math.floor(sorted.length * 0.75)],
|
|
20
|
+
p99: sorted[Math.floor(sorted.length * 0.99)],
|
|
21
|
+
p999: sorted[Math.floor(sorted.length * 0.999)],
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function report(name: string, samples: number[]): BenchmarkReport {
|
|
27
|
+
return { name, measuredResults: measured(samples) };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function range(n: number): number[] {
|
|
31
|
+
return Array.from({ length: n }, (_, i) => i + 1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
test("default buildTimeSection produces mean, p50, p99 columns", () => {
|
|
35
|
+
const section = buildTimeSection();
|
|
36
|
+
expect(section.columns.map(c => c.key ?? c.title)).toEqual([
|
|
37
|
+
"mean",
|
|
38
|
+
"p50",
|
|
39
|
+
"p99",
|
|
40
|
+
]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("computeColumnValues computes values from samples", () => {
|
|
44
|
+
const section = buildTimeSection("mean,p50,max,min");
|
|
45
|
+
const row = computeColumnValues(section, measured([10, 20, 30, 40, 50]));
|
|
46
|
+
expect(row.mean).toBe(30);
|
|
47
|
+
expect(row.min).toBe(10);
|
|
48
|
+
expect(row.max).toBe(50);
|
|
49
|
+
expect(row.p50).toBeGreaterThanOrEqual(20);
|
|
50
|
+
expect(row.p50).toBeLessThanOrEqual(40);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("p70 returns value near 70th percentile of [1..100]", () => {
|
|
54
|
+
const section = buildTimeSection("p70");
|
|
55
|
+
const row = computeColumnValues(section, measured(range(100)));
|
|
56
|
+
expect(row.p70).toBeGreaterThanOrEqual(69);
|
|
57
|
+
expect(row.p70).toBeLessThanOrEqual(71);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("p999 uses divide-by-1000 convention", () => {
|
|
61
|
+
const section = buildTimeSection("p999");
|
|
62
|
+
const row = computeColumnValues(section, measured(range(1000)));
|
|
63
|
+
expect(row.p999).toBeGreaterThanOrEqual(999);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("p9999 uses divide-by-10000 convention", () => {
|
|
67
|
+
const section = buildTimeSection("p9999");
|
|
68
|
+
const row = computeColumnValues(section, measured(range(10000)));
|
|
69
|
+
expect(row.p9999).toBeGreaterThanOrEqual(9999);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("median and p50 produce the same value", () => {
|
|
73
|
+
const a = computeColumnValues(
|
|
74
|
+
buildTimeSection("median"),
|
|
75
|
+
measured(range(100)),
|
|
76
|
+
);
|
|
77
|
+
const b = computeColumnValues(buildTimeSection("p50"), measured(range(100)));
|
|
78
|
+
expect(a.p50).toBe(b.p50);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("mean and avg dedupe to a single column", () => {
|
|
82
|
+
const section = buildTimeSection("mean,avg");
|
|
83
|
+
expect(section.columns.length).toBe(1);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("min and max return exact values", () => {
|
|
87
|
+
const section = buildTimeSection("min,max");
|
|
88
|
+
const row = computeColumnValues(section, measured([5, 1, 9, 3, 7]));
|
|
89
|
+
expect(row.min).toBe(1);
|
|
90
|
+
expect(row.max).toBe(9);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("empty stats string throws", () => {
|
|
94
|
+
expect(() => buildTimeSection("")).toThrow(/at least one column/);
|
|
95
|
+
expect(() => buildTimeSection(" , ")).toThrow(/at least one column/);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("unknown token throws with vocabulary hint", () => {
|
|
99
|
+
expect(() => buildTimeSection("wat")).toThrow(
|
|
100
|
+
/expected mean, median, min, max, or p<N>/,
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("single-digit percentile token is rejected", () => {
|
|
105
|
+
expect(() => buildTimeSection("p5")).toThrow(/at least 2 digits/);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("3+ digit percentile tokens not starting with 9 are rejected", () => {
|
|
109
|
+
expect(() => buildTimeSection("p100")).toThrow(/must start with 9/);
|
|
110
|
+
expect(() => buildTimeSection("p500")).toThrow(/must start with 9/);
|
|
111
|
+
expect(() => buildTimeSection("p1000")).toThrow(/must start with 9/);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("reportResults renders user-chosen columns as table headers", () => {
|
|
115
|
+
const groups = [{ name: "g", reports: [report("bench", range(100))] }];
|
|
116
|
+
const table = reportResults(groups, [buildTimeSection("p70,p95")]);
|
|
117
|
+
expect(table).toContain("p70");
|
|
118
|
+
expect(table).toContain("p95");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("valuesForReports extracts user-chosen keys", () => {
|
|
122
|
+
const rows = valuesForReports(
|
|
123
|
+
[report("bench", range(100))],
|
|
124
|
+
[buildTimeSection("p70,p95")],
|
|
125
|
+
);
|
|
126
|
+
expect(rows[0].p70).toBeGreaterThanOrEqual(69);
|
|
127
|
+
expect(rows[0].p70).toBeLessThanOrEqual(71);
|
|
128
|
+
expect(rows[0].p95).toBeGreaterThanOrEqual(94);
|
|
129
|
+
expect(rows[0].p95).toBeLessThanOrEqual(96);
|
|
130
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import { sampleDifferenceCI } from "../stats/BootstrapDifference.ts";
|
|
3
|
+
import {
|
|
4
|
+
average,
|
|
5
|
+
maxBootstrapInput,
|
|
6
|
+
percentile,
|
|
7
|
+
sampleBootstrap,
|
|
8
|
+
} from "../stats/StatisticalUtils.ts";
|
|
9
|
+
|
|
10
|
+
test("sampleBootstrap uses full samples for point estimate", () => {
|
|
11
|
+
const samples = Array.from({ length: 5000 }, (_, i) => i);
|
|
12
|
+
const result = sampleBootstrap(samples, average, { resamples: 100 });
|
|
13
|
+
expect(result.estimate).toBe(average(samples));
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("sampleDifferenceCI preserves point estimate", () => {
|
|
17
|
+
const a = Array.from({ length: 5000 }, () => 50 + Math.random() * 10);
|
|
18
|
+
const b = a.map(v => v * 1.1);
|
|
19
|
+
const result = sampleDifferenceCI(a, b, average, { resamples: 100 });
|
|
20
|
+
const expected = ((average(b) - average(a)) / average(a)) * 100;
|
|
21
|
+
expect(result.percent).toBeCloseTo(expected, 10);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("sampleBootstrap point estimate uses full array when capped", () => {
|
|
25
|
+
const n = maxBootstrapInput + 5000;
|
|
26
|
+
const samples = Array.from({ length: n }, (_, i) => i);
|
|
27
|
+
const result = sampleBootstrap(samples, average, { resamples: 50 });
|
|
28
|
+
expect(result.estimate).toBe(average(samples));
|
|
29
|
+
expect(result.subsampled).toBe(n);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("sampleBootstrap does not set subsampled when under cap", () => {
|
|
33
|
+
const samples = Array.from({ length: 100 }, (_, i) => i);
|
|
34
|
+
const result = sampleBootstrap(samples, average, { resamples: 50 });
|
|
35
|
+
expect(result.subsampled).toBeUndefined();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("sampleDifferenceCI sets subsampled when inputs exceed cap", () => {
|
|
39
|
+
const n = maxBootstrapInput + 1000;
|
|
40
|
+
const a = Array.from({ length: n }, () => 50 + Math.random() * 10);
|
|
41
|
+
const b = a.map(v => v * 1.1);
|
|
42
|
+
const result = sampleDifferenceCI(a, b, average, { resamples: 50 });
|
|
43
|
+
expect(result.percent).toBeCloseTo(10, 0);
|
|
44
|
+
expect(result.subsampled).toBe(n);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("sampleDifferenceCI no subsampled flag when under cap", () => {
|
|
48
|
+
const a = Array.from({ length: 100 }, () => 50 + Math.random() * 10);
|
|
49
|
+
const b = a.map(v => v * 1.1);
|
|
50
|
+
const result = sampleDifferenceCI(a, b, average, { resamples: 50 });
|
|
51
|
+
expect(result.subsampled).toBeUndefined();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("quickselect-based percentile matches sorted percentile", () => {
|
|
55
|
+
const data = Array.from({ length: 1000 }, () => Math.random() * 100);
|
|
56
|
+
const sorted = [...data].sort((a, b) => a - b);
|
|
57
|
+
for (const p of [0.25, 0.5, 0.75, 0.99]) {
|
|
58
|
+
const k = Math.max(0, Math.ceil(sorted.length * p) - 1);
|
|
59
|
+
expect(percentile(data, p)).toBe(sorted[k]);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("quickselect handles small arrays", () => {
|
|
64
|
+
expect(percentile([42], 0.5)).toBe(42);
|
|
65
|
+
expect(percentile([1, 2], 0.5)).toBe(1);
|
|
66
|
+
expect(percentile([1, 2], 1.0)).toBe(2);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("quickselect handles duplicate values", () => {
|
|
70
|
+
const data = [5, 5, 5, 5, 5, 10, 10, 10, 10, 10];
|
|
71
|
+
expect(percentile(data, 0.5)).toBe(5);
|
|
72
|
+
expect(percentile(data, 0.99)).toBe(10);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("sampleBootstrap reuses buffer (no per-iteration allocation)", () => {
|
|
76
|
+
const samples = [10, 20, 30, 40, 50];
|
|
77
|
+
const result = sampleBootstrap(samples, average, { resamples: 50 });
|
|
78
|
+
expect(result.estimate).toBe(average(samples));
|
|
79
|
+
expect(result.samples).toHaveLength(50);
|
|
80
|
+
expect(result.ci[0]).toBeLessThanOrEqual(result.estimate);
|
|
81
|
+
expect(result.ci[1]).toBeGreaterThanOrEqual(result.estimate);
|
|
82
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
annotateFramesWithCounts,
|
|
4
|
+
buildCoverageMap,
|
|
5
|
+
} from "../export/CoverageExport.ts";
|
|
6
|
+
import type { CoverageData } from "../profiling/node/CoverageTypes.ts";
|
|
7
|
+
|
|
8
|
+
const source = `function foo() {
|
|
9
|
+
return 1;
|
|
10
|
+
}
|
|
11
|
+
function bar() {
|
|
12
|
+
return 2;
|
|
13
|
+
}
|
|
14
|
+
const baz = () => 3;
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
const coverage: CoverageData = {
|
|
18
|
+
scripts: [
|
|
19
|
+
{
|
|
20
|
+
url: "file:///test.js",
|
|
21
|
+
functions: [
|
|
22
|
+
{
|
|
23
|
+
functionName: "foo",
|
|
24
|
+
ranges: [{ startOffset: 0, endOffset: 30, count: 10 }],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
functionName: "bar",
|
|
28
|
+
ranges: [{ startOffset: 31, endOffset: 60, count: 5 }],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
functionName: "",
|
|
32
|
+
ranges: [{ startOffset: 61, endOffset: 80, count: 3 }],
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
test("buildCoverageMap resolves offsets to lines", () => {
|
|
40
|
+
const result = buildCoverageMap(coverage, { "file:///test.js": source });
|
|
41
|
+
|
|
42
|
+
expect(result.map.has("file:///test.js")).toBe(true);
|
|
43
|
+
const entries = result.map.get("file:///test.js")!;
|
|
44
|
+
expect(entries).toHaveLength(3);
|
|
45
|
+
|
|
46
|
+
const foo = entries.find(e => e.functionName === "foo");
|
|
47
|
+
expect(foo).toBeDefined();
|
|
48
|
+
expect(foo!.startLine).toBe(1);
|
|
49
|
+
expect(foo!.count).toBe(10);
|
|
50
|
+
|
|
51
|
+
const bar = entries.find(e => e.functionName === "bar");
|
|
52
|
+
expect(bar).toBeDefined();
|
|
53
|
+
expect(bar!.startLine).toBe(4);
|
|
54
|
+
expect(bar!.count).toBe(5);
|
|
55
|
+
|
|
56
|
+
// byName aggregates across all scripts
|
|
57
|
+
expect(result.byName.get("foo")).toBe(10);
|
|
58
|
+
expect(result.byName.get("bar")).toBe(5);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("annotateFramesWithCounts appends [N] to matched frames", () => {
|
|
62
|
+
const result = buildCoverageMap(coverage, { "file:///test.js": source });
|
|
63
|
+
|
|
64
|
+
const frames = [
|
|
65
|
+
{ name: "foo", file: "file:///test.js", line: 1 },
|
|
66
|
+
{ name: "bar", file: "file:///test.js", line: 4 },
|
|
67
|
+
{ name: "unmatched", file: "file:///other.js", line: 1 },
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
annotateFramesWithCounts(frames, result);
|
|
71
|
+
|
|
72
|
+
expect(frames[0].name).toBe("foo [10]");
|
|
73
|
+
expect(frames[1].name).toBe("bar [5]");
|
|
74
|
+
expect(frames[2].name).toBe("unmatched"); // no coverage data for this file
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("annotateFramesWithCounts falls back to name-only for frames without file", () => {
|
|
78
|
+
const result = buildCoverageMap(coverage, { "file:///test.js": source });
|
|
79
|
+
|
|
80
|
+
const frames = [
|
|
81
|
+
{ name: "foo" }, // no file — should match by name
|
|
82
|
+
{ name: "bar" },
|
|
83
|
+
{ name: "(anonymous)" }, // anonymous — should not match by name
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
annotateFramesWithCounts(frames, result);
|
|
87
|
+
|
|
88
|
+
expect(frames[0].name).toBe("foo [10]");
|
|
89
|
+
expect(frames[1].name).toBe("bar [5]");
|
|
90
|
+
expect(frames[2].name).toBe("(anonymous)");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("annotateFramesWithCounts formats large counts", () => {
|
|
94
|
+
const bigCoverage: CoverageData = {
|
|
95
|
+
scripts: [
|
|
96
|
+
{
|
|
97
|
+
url: "file:///big.js",
|
|
98
|
+
functions: [
|
|
99
|
+
{
|
|
100
|
+
functionName: "hot",
|
|
101
|
+
ranges: [{ startOffset: 0, endOffset: 10, count: 1_500_000 }],
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
};
|
|
107
|
+
const result = buildCoverageMap(bigCoverage, {
|
|
108
|
+
"file:///big.js": "function hot() {}",
|
|
109
|
+
});
|
|
110
|
+
const frames = [{ name: "hot", file: "file:///big.js", line: 1 }];
|
|
111
|
+
|
|
112
|
+
annotateFramesWithCounts(frames, result);
|
|
113
|
+
|
|
114
|
+
expect(frames[0].name).toBe("hot [1.5M]");
|
|
115
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import { withCoverageProfiling } from "../profiling/node/CoverageSampler.ts";
|
|
3
|
+
|
|
4
|
+
test("withCoverageProfiling returns function execution counts", async () => {
|
|
5
|
+
function hotFunction() {
|
|
6
|
+
let sum = 0;
|
|
7
|
+
for (let i = 0; i < 100; i++) sum += i;
|
|
8
|
+
return sum;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { result, coverage } = await withCoverageProfiling(_session => {
|
|
12
|
+
for (let i = 0; i < 10; i++) hotFunction();
|
|
13
|
+
return 42;
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
expect(result).toBe(42);
|
|
17
|
+
expect(coverage.scripts.length).toBeGreaterThan(0);
|
|
18
|
+
|
|
19
|
+
// Find our test file in the coverage data
|
|
20
|
+
const thisScript = coverage.scripts.find(s =>
|
|
21
|
+
s.url.includes("CoverageSampler.test"),
|
|
22
|
+
);
|
|
23
|
+
expect(thisScript).toBeDefined();
|
|
24
|
+
expect(thisScript!.functions.length).toBeGreaterThan(0);
|
|
25
|
+
|
|
26
|
+
// Find hotFunction and verify its count
|
|
27
|
+
const hotFn = thisScript!.functions.find(
|
|
28
|
+
f => f.functionName === "hotFunction",
|
|
29
|
+
);
|
|
30
|
+
expect(hotFn).toBeDefined();
|
|
31
|
+
const count = hotFn!.ranges[0].count;
|
|
32
|
+
expect(count).toBe(10);
|
|
33
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
aggregateSites,
|
|
4
|
+
type HeapSite,
|
|
5
|
+
} from "../profiling/node/HeapSampleReport.ts";
|
|
6
|
+
|
|
7
|
+
test("unknown column does not merge distinct functions on same line", () => {
|
|
8
|
+
const sites: HeapSite[] = [
|
|
9
|
+
{ name: "Foo", url: "test.ts", line: 10, col: undefined, bytes: 100 },
|
|
10
|
+
{ name: "Bar", url: "test.ts", line: 10, col: undefined, bytes: 200 },
|
|
11
|
+
];
|
|
12
|
+
const aggregated = aggregateSites(sites);
|
|
13
|
+
expect(aggregated).toHaveLength(2);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("same column merges regardless of function name", () => {
|
|
17
|
+
const sites: HeapSite[] = [
|
|
18
|
+
{ name: "Foo", url: "test.ts", line: 10, col: 5, bytes: 100 },
|
|
19
|
+
{ name: "Foo", url: "test.ts", line: 10, col: 5, bytes: 200 },
|
|
20
|
+
];
|
|
21
|
+
const aggregated = aggregateSites(sites);
|
|
22
|
+
expect(aggregated).toHaveLength(1);
|
|
23
|
+
expect(aggregated[0].bytes).toBe(300);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("aggregation preserves distinct caller stacks", () => {
|
|
27
|
+
const stackA = [
|
|
28
|
+
{ name: "root", url: "a.ts", line: 1, col: 0 },
|
|
29
|
+
{ name: "foo", url: "a.ts", line: 10, col: 0 },
|
|
30
|
+
{ name: "alloc", url: "a.ts", line: 20, col: 5 },
|
|
31
|
+
];
|
|
32
|
+
const stackB = [
|
|
33
|
+
{ name: "root", url: "a.ts", line: 1, col: 0 },
|
|
34
|
+
{ name: "bar", url: "a.ts", line: 15, col: 0 },
|
|
35
|
+
{ name: "alloc", url: "a.ts", line: 20, col: 5 },
|
|
36
|
+
];
|
|
37
|
+
const sites: HeapSite[] = [
|
|
38
|
+
{ name: "alloc", url: "a.ts", line: 20, col: 5, bytes: 800, stack: stackA },
|
|
39
|
+
{ name: "alloc", url: "a.ts", line: 20, col: 5, bytes: 200, stack: stackB },
|
|
40
|
+
];
|
|
41
|
+
const aggregated = aggregateSites(sites);
|
|
42
|
+
|
|
43
|
+
expect(aggregated).toHaveLength(1);
|
|
44
|
+
expect(aggregated[0].bytes).toBe(1000);
|
|
45
|
+
expect(aggregated[0].callers).toHaveLength(2);
|
|
46
|
+
// Primary stack should be the highest-bytes path (foo)
|
|
47
|
+
expect(aggregated[0].stack![1].name).toBe("foo");
|
|
48
|
+
// Callers sorted by bytes descending
|
|
49
|
+
expect(aggregated[0].callers![0].bytes).toBe(800);
|
|
50
|
+
expect(aggregated[0].callers![1].bytes).toBe(200);
|
|
51
|
+
});
|
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import { expect, test } from "vitest";
|
|
2
|
-
import type { BenchMatrix } from "../BenchMatrix.ts";
|
|
2
|
+
import type { BenchMatrix } from "../matrix/BenchMatrix.ts";
|
|
3
3
|
import { filterMatrix, parseMatrixFilter } from "../matrix/MatrixFilter.ts";
|
|
4
4
|
|
|
5
|
+
const inlineMatrix: BenchMatrix<string> = {
|
|
6
|
+
name: "Test",
|
|
7
|
+
variants: {
|
|
8
|
+
fast: (s: string) => s.toUpperCase(),
|
|
9
|
+
slow: (s: string) => s.toLowerCase(),
|
|
10
|
+
medium: (s: string) => s,
|
|
11
|
+
},
|
|
12
|
+
cases: ["small", "large", "bevy_env_map"],
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const noExplicitCases: BenchMatrix = {
|
|
16
|
+
name: "NoCase",
|
|
17
|
+
variants: { fast: () => {} },
|
|
18
|
+
};
|
|
19
|
+
|
|
5
20
|
test("parseMatrixFilter: case/variant", () => {
|
|
6
21
|
expect(parseMatrixFilter("bevy/link")).toEqual({
|
|
7
22
|
case: "bevy",
|
|
@@ -34,16 +49,6 @@ test("parseMatrixFilter: empty parts", () => {
|
|
|
34
49
|
});
|
|
35
50
|
});
|
|
36
51
|
|
|
37
|
-
const inlineMatrix: BenchMatrix<string> = {
|
|
38
|
-
name: "Test",
|
|
39
|
-
variants: {
|
|
40
|
-
fast: (s: string) => s.toUpperCase(),
|
|
41
|
-
slow: (s: string) => s.toLowerCase(),
|
|
42
|
-
medium: (s: string) => s,
|
|
43
|
-
},
|
|
44
|
-
cases: ["small", "large", "bevy_env_map"],
|
|
45
|
-
};
|
|
46
|
-
|
|
47
52
|
test("filterMatrix: no filter returns original", async () => {
|
|
48
53
|
const result = await filterMatrix(inlineMatrix, undefined);
|
|
49
54
|
expect(result).toBe(inlineMatrix);
|
|
@@ -106,11 +111,6 @@ test("filterMatrix: multiple matching variants", async () => {
|
|
|
106
111
|
expect(result.filteredVariants?.sort()).toEqual(["fast", "slow"]);
|
|
107
112
|
});
|
|
108
113
|
|
|
109
|
-
const noExplicitCases: BenchMatrix = {
|
|
110
|
-
name: "NoCase",
|
|
111
|
-
variants: { fast: () => {} },
|
|
112
|
-
};
|
|
113
|
-
|
|
114
114
|
test("filterMatrix: implicit default case returns default", async () => {
|
|
115
115
|
const result = await filterMatrix(noExplicitCases, { case: "default" });
|
|
116
116
|
expect(result.filteredCases).toEqual(["default"]);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expect, test } from "vitest";
|
|
2
|
-
import type { CaseResult, MatrixResults } from "../BenchMatrix.ts";
|
|
2
|
+
import type { CaseResult, MatrixResults } from "../matrix/BenchMatrix.ts";
|
|
3
3
|
import { reportMatrixResults } from "../matrix/MatrixReport.ts";
|
|
4
4
|
|
|
5
5
|
/** Create simple measured results for testing */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expect, test } from "vitest";
|
|
2
|
-
import { compareWithBaseline } from "../PermutationTest.ts";
|
|
2
|
+
import { compareWithBaseline } from "../stats/PermutationTest.ts";
|
|
3
3
|
import { assertValid, getSampleData } from "./TestUtils.ts";
|
|
4
4
|
|
|
5
5
|
test("detects 20% performance improvement", () => {
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
coefficientOfVariation,
|
|
5
5
|
medianAbsoluteDeviation,
|
|
6
6
|
percentile,
|
|
7
|
-
} from "../StatisticalUtils.ts";
|
|
7
|
+
} from "../stats/StatisticalUtils.ts";
|
|
8
8
|
import { bevy30SamplesMs, bevy30SamplesNs } from "./fixtures/bevy30-samples.ts";
|
|
9
9
|
|
|
10
|
-
test("bevy30 data characteristics", () => {
|
|
10
|
+
test.skip("bevy30 data characteristics", () => {
|
|
11
11
|
const sortedMs = [...bevy30SamplesMs].sort((a, b) => a - b);
|
|
12
12
|
|
|
13
13
|
const stats = {
|
|
@@ -48,7 +48,7 @@ test("bevy30 data characteristics", () => {
|
|
|
48
48
|
if (stats.cv > 0.5) console.warn("Very high variation - may be unstable");
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
test("convergence at different time points matches CLI behavior", () => {
|
|
51
|
+
test.skip("convergence at different time points matches CLI behavior", () => {
|
|
52
52
|
// Simulate 5-second run (approximately 100 samples at ~50ms each)
|
|
53
53
|
const samples5s = bevy30SamplesNs.slice(0, 100);
|
|
54
54
|
const result5s = checkConvergence(samples5s);
|
|
@@ -76,7 +76,7 @@ test("convergence at different time points matches CLI behavior", () => {
|
|
|
76
76
|
);
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
-
test("warm-up detection in real data", () => {
|
|
79
|
+
test.skip("warm-up detection in real data", () => {
|
|
80
80
|
const windowSize = 20;
|
|
81
81
|
const windows: Array<{ start: number; median: number }> = [];
|
|
82
82
|
|
|
@@ -104,7 +104,7 @@ test("warm-up detection in real data", () => {
|
|
|
104
104
|
}
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
-
test("convergence stability over sliding windows", () => {
|
|
107
|
+
test.skip("convergence stability over sliding windows", () => {
|
|
108
108
|
const windowSize = 100;
|
|
109
109
|
const step = 50;
|
|
110
110
|
const history: Array<{ start: number; confidence: number }> = [];
|
|
@@ -132,7 +132,7 @@ test("convergence stability over sliding windows", () => {
|
|
|
132
132
|
}
|
|
133
133
|
});
|
|
134
134
|
|
|
135
|
-
test("adaptive algorithm would stop at correct time", () => {
|
|
135
|
+
test.skip("adaptive algorithm would stop at correct time", () => {
|
|
136
136
|
const target = 95;
|
|
137
137
|
const fallback = 80;
|
|
138
138
|
const minSamples = 50;
|