benchforge 0.1.0 → 0.1.2

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.
@@ -0,0 +1,225 @@
1
+ //#region src/heap-sample/HeapSampler.d.ts
2
+ interface ProfileNode {
3
+ callFrame: {
4
+ functionName: string;
5
+ url: string;
6
+ lineNumber: number;
7
+ columnNumber?: number;
8
+ };
9
+ selfSize: number;
10
+ children?: ProfileNode[];
11
+ }
12
+ interface HeapProfile {
13
+ head: ProfileNode;
14
+ samples?: number[];
15
+ }
16
+ //#endregion
17
+ //#region src/NodeGC.d.ts
18
+ /** Individual GC event for visualization */
19
+ interface GcEvent {
20
+ /** Offset from collection start (ms) - can be negative for warmup GCs */
21
+ offset: number;
22
+ /** Duration of GC pause (ms) */
23
+ duration: number;
24
+ }
25
+ /** GC time measured by Node's performance hooks */
26
+ interface NodeGCTime {
27
+ inRun: number;
28
+ before: number;
29
+ after: number;
30
+ total: number;
31
+ collects: number;
32
+ /** Individual GC events during sample collection (for visualization) */
33
+ events: GcEvent[];
34
+ }
35
+ //#endregion
36
+ //#region src/runners/GcStats.d.ts
37
+ /** GC statistics aggregated from V8 trace events.
38
+ * Node (--trace-gc-nvp) provides all fields.
39
+ * Browser (CDP Tracing) provides counts, collected, and pause only. */
40
+ interface GcStats {
41
+ scavenges: number;
42
+ markCompacts: number;
43
+ totalCollected: number;
44
+ gcPauseTime: number;
45
+ totalAllocated?: number;
46
+ totalPromoted?: number;
47
+ totalSurvived?: number;
48
+ }
49
+ //#endregion
50
+ //#region src/MeasuredResults.d.ts
51
+ /** CPU performance counter stats */
52
+ interface CpuCounts {
53
+ instructions?: number;
54
+ cycles?: number;
55
+ branchMisses?: number;
56
+ }
57
+ /** Benchmark results: times in milliseconds, sizes in kilobytes */
58
+ interface MeasuredResults {
59
+ name: string;
60
+ /** Raw execution time samples for custom statistics */
61
+ samples: number[];
62
+ /** Warmup iteration timings (ms) - captured before gc/settle */
63
+ warmupSamples?: number[];
64
+ /** Raw allocation samples per iteration (KB) */
65
+ allocationSamples?: number[];
66
+ /** Heap size per sample (bytes) - used for charts */
67
+ heapSamples?: number[];
68
+ /** Wall-clock timestamps per sample (μs since process start) - for Perfetto export */
69
+ timestamps?: number[];
70
+ /** Execution time in milliseconds (measurement overhead excluded by mitata) */
71
+ time: {
72
+ min: number;
73
+ max: number;
74
+ avg: number;
75
+ p25?: number;
76
+ p50: number;
77
+ p75: number;
78
+ p95?: number;
79
+ p99: number;
80
+ p999: number;
81
+ cv?: number;
82
+ mad?: number;
83
+ outlierRate?: number;
84
+ };
85
+ /** Heap size increase during test run (kilobytes) */
86
+ heapSize?: {
87
+ avg: number;
88
+ min: number;
89
+ max: number;
90
+ };
91
+ /**
92
+ * Time for explicit gc() call after test execution (milliseconds).
93
+ * Does not include GC time during test execution.
94
+ * Only reported by mitata runner.
95
+ */
96
+ gcTime?: {
97
+ avg: number;
98
+ min: number;
99
+ max: number;
100
+ };
101
+ /** CPU counter stats from @mitata/counters (requires root access) */
102
+ cpu?: CpuCounts;
103
+ /** L1 cache miss rate */
104
+ cpuCacheMiss?: number;
105
+ /** CPU stall rate (macOS only) */
106
+ cpuStall?: number;
107
+ /**
108
+ * Stop-the-world GC time blocking main thread (milliseconds).
109
+ * Measured via Node's performance hooks when nodeObserveGC is true.
110
+ * Excludes parallel thread collection time and indirect slowdowns.
111
+ */
112
+ nodeGcTime?: NodeGCTime;
113
+ /** Total time spent collecting samples (seconds) */
114
+ totalTime?: number;
115
+ /** Convergence information for adaptive mode */
116
+ convergence?: {
117
+ converged: boolean;
118
+ confidence: number;
119
+ reason: string;
120
+ };
121
+ /** V8 optimization tier tracking (requires --allow-natives-syntax) */
122
+ optStatus?: OptStatusInfo;
123
+ /** Per-sample V8 optimization status codes (for chart visualization) */
124
+ optSamples?: number[];
125
+ /** Points where pauses occurred for V8 optimization */
126
+ pausePoints?: PausePoint[];
127
+ /** GC stats from V8's --trace-gc-nvp (requires --gc-stats and worker mode) */
128
+ gcStats?: GcStats;
129
+ /** Heap sampling allocation profile (requires --heap-sample and worker mode) */
130
+ heapProfile?: HeapProfile;
131
+ }
132
+ /** A pause point during sample collection for V8 optimization */
133
+ interface PausePoint {
134
+ /** Sample index where pause occurred (after this iteration) */
135
+ sampleIndex: number;
136
+ /** Pause duration in milliseconds */
137
+ durationMs: number;
138
+ }
139
+ /** V8 optimization tier distribution */
140
+ interface OptTierInfo {
141
+ count: number;
142
+ medianMs: number;
143
+ }
144
+ /** V8 optimization status summary */
145
+ interface OptStatusInfo {
146
+ /** Samples by tier name (e.g., "turbofan", "sparkplug") */
147
+ byTier: Record<string, OptTierInfo>;
148
+ /** Number of samples with deopt flag set */
149
+ deoptCount: number;
150
+ }
151
+ //#endregion
152
+ //#region src/Benchmark.d.ts
153
+ /** Single benchmark function specification */
154
+ interface BenchmarkSpec<T = unknown> {
155
+ name: string;
156
+ fn: BenchmarkFunction<T>;
157
+ /** Path to module exporting the benchmark function (for worker mode) */
158
+ modulePath?: string;
159
+ /** Name of the exported function in the module (defaults to default export) */
160
+ exportName?: string;
161
+ /** Setup function export name - called once in worker, result passed to fn */
162
+ setupExportName?: string;
163
+ }
164
+ type BenchmarkFunction<T = unknown> = ((params: T) => void) | (() => void);
165
+ /** Group of benchmarks with shared setup */
166
+ interface BenchGroup<T = unknown> {
167
+ name: string;
168
+ /** Prepare parameters for all benchmarks in this group */
169
+ setup?: () => T | Promise<T>;
170
+ benchmarks: BenchmarkSpec<T>[];
171
+ /** Baseline benchmark for comparison */
172
+ baseline?: BenchmarkSpec<T>;
173
+ /** Metadata for reporting (e.g., lines of code) */
174
+ metadata?: Record<string, any>;
175
+ }
176
+ /** Collection of benchmark groups */
177
+ interface BenchSuite {
178
+ name: string;
179
+ groups: BenchGroup<any>[];
180
+ }
181
+ //#endregion
182
+ //#region src/runners/BenchRunner.d.ts
183
+ interface RunnerOptions {
184
+ /** Minimum time to run each benchmark (milliseconds) */
185
+ minTime?: number;
186
+ /** Maximum time to run each benchmark - ignored by mitata (milliseconds) */
187
+ maxTime?: number;
188
+ /** Maximum iterations per benchmark - ignored by TinyBench */
189
+ maxIterations?: number;
190
+ /** Warmup iterations before measurement (default: 0) */
191
+ warmup?: number;
192
+ /** Warmup time before measurement (milliseconds) */
193
+ warmupTime?: number;
194
+ /** Warmup samples - mitata only, for reducing test time */
195
+ warmupSamples?: number;
196
+ /** Warmup threshold - mitata only (nanoseconds) */
197
+ warmupThreshold?: number;
198
+ /** Minimum samples required - mitata only */
199
+ minSamples?: number;
200
+ /** Force GC after each iteration (requires --expose-gc) */
201
+ collect?: boolean;
202
+ /** Enable CPU performance counters (requires root access) */
203
+ cpuCounters?: boolean;
204
+ /** Trace V8 optimization tiers (requires --allow-natives-syntax) */
205
+ traceOpt?: boolean;
206
+ /** Skip post-warmup settle time (default: false) */
207
+ noSettle?: boolean;
208
+ /** Iterations before first pause (then pauseInterval applies) */
209
+ pauseFirst?: number;
210
+ /** Iterations between pauses for V8 optimization (0 to disable) */
211
+ pauseInterval?: number;
212
+ /** Pause duration in ms for V8 optimization */
213
+ pauseDuration?: number;
214
+ /** Collect GC stats via --trace-gc-nvp (requires worker mode) */
215
+ gcStats?: boolean;
216
+ /** Heap sampling allocation attribution */
217
+ heapSample?: boolean;
218
+ /** Heap sampling interval in bytes */
219
+ heapInterval?: number;
220
+ /** Heap sampling stack depth */
221
+ heapDepth?: number;
222
+ }
223
+ //#endregion
224
+ export { MeasuredResults as a, BenchmarkSpec as i, BenchGroup as n, HeapProfile as o, BenchSuite as r, RunnerOptions as t };
225
+ //# sourceMappingURL=BenchRunner-BLfGX2wQ.d.mts.map
@@ -0,0 +1,54 @@
1
+ import { Session } from "node:inspector/promises";
2
+
3
+ //#region src/heap-sample/HeapSampler.ts
4
+ const defaultOptions = {
5
+ samplingInterval: 32768,
6
+ stackDepth: 64,
7
+ includeMinorGC: true,
8
+ includeMajorGC: true
9
+ };
10
+ /** Run a function while sampling heap allocations, return profile */
11
+ async function withHeapSampling(options, fn) {
12
+ const opts = {
13
+ ...defaultOptions,
14
+ ...options
15
+ };
16
+ const session = new Session();
17
+ session.connect();
18
+ try {
19
+ await startSampling(session, opts);
20
+ return {
21
+ result: await fn(),
22
+ profile: await stopSampling(session)
23
+ };
24
+ } finally {
25
+ session.disconnect();
26
+ }
27
+ }
28
+ /** Start heap sampling, falling back if include-collected params aren't supported */
29
+ async function startSampling(session, opts) {
30
+ const { samplingInterval, stackDepth } = opts;
31
+ const base = {
32
+ samplingInterval,
33
+ stackDepth
34
+ };
35
+ const params = {
36
+ ...base,
37
+ includeObjectsCollectedByMinorGC: opts.includeMinorGC,
38
+ includeObjectsCollectedByMajorGC: opts.includeMajorGC
39
+ };
40
+ try {
41
+ await session.post("HeapProfiler.startSampling", params);
42
+ } catch {
43
+ console.warn("HeapProfiler: include-collected params not supported, falling back");
44
+ await session.post("HeapProfiler.startSampling", base);
45
+ }
46
+ }
47
+ async function stopSampling(session) {
48
+ const { profile } = await session.post("HeapProfiler.stopSampling");
49
+ return profile;
50
+ }
51
+
52
+ //#endregion
53
+ export { withHeapSampling };
54
+ //# sourceMappingURL=HeapSampler-BX3de22o.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HeapSampler-BX3de22o.mjs","names":[],"sources":["../src/heap-sample/HeapSampler.ts"],"sourcesContent":["import { Session } from \"node:inspector/promises\";\n\nexport interface HeapSampleOptions {\n samplingInterval?: number; // bytes between samples, default 32768\n stackDepth?: number; // max stack frames, default 64\n includeMinorGC?: boolean; // keep objects collected by minor GC, default true\n includeMajorGC?: boolean; // keep objects collected by major GC, default true\n}\n\nexport interface ProfileNode {\n callFrame: {\n functionName: string;\n url: string;\n lineNumber: number;\n columnNumber?: number;\n };\n selfSize: number;\n children?: ProfileNode[];\n}\n\nexport interface HeapProfile {\n head: ProfileNode;\n samples?: number[]; // sample IDs (length = number of samples taken)\n}\n\nconst defaultOptions: Required<HeapSampleOptions> = {\n samplingInterval: 32768,\n stackDepth: 64,\n includeMinorGC: true,\n includeMajorGC: true,\n};\n\n/** Run a function while sampling heap allocations, return profile */\nexport async function withHeapSampling<T>(\n options: HeapSampleOptions,\n fn: () => Promise<T> | T,\n): Promise<{ result: T; profile: HeapProfile }> {\n const opts = { ...defaultOptions, ...options };\n const session = new Session();\n session.connect();\n\n try {\n await startSampling(session, opts);\n const result = await fn();\n const profile = await stopSampling(session);\n return { result, profile };\n } finally {\n session.disconnect();\n }\n}\n\n/** Start heap sampling, falling back if include-collected params aren't supported */\nasync function startSampling(\n session: Session,\n opts: Required<HeapSampleOptions>,\n): Promise<void> {\n const { samplingInterval, stackDepth } = opts;\n const base = { samplingInterval, stackDepth };\n const params = {\n ...base,\n includeObjectsCollectedByMinorGC: opts.includeMinorGC,\n includeObjectsCollectedByMajorGC: opts.includeMajorGC,\n };\n\n try {\n await session.post(\"HeapProfiler.startSampling\", params);\n } catch {\n console.warn(\n \"HeapProfiler: include-collected params not supported, falling back\",\n );\n await session.post(\"HeapProfiler.startSampling\", base);\n }\n}\n\nasync function stopSampling(session: Session): Promise<HeapProfile> {\n const { profile } = await session.post(\"HeapProfiler.stopSampling\");\n return profile as HeapProfile;\n}\n"],"mappings":";;;AAyBA,MAAM,iBAA8C;CAClD,kBAAkB;CAClB,YAAY;CACZ,gBAAgB;CAChB,gBAAgB;CACjB;;AAGD,eAAsB,iBACpB,SACA,IAC8C;CAC9C,MAAM,OAAO;EAAE,GAAG;EAAgB,GAAG;EAAS;CAC9C,MAAM,UAAU,IAAI,SAAS;AAC7B,SAAQ,SAAS;AAEjB,KAAI;AACF,QAAM,cAAc,SAAS,KAAK;AAGlC,SAAO;GAAE,QAFM,MAAM,IAAI;GAER,SADD,MAAM,aAAa,QAAQ;GACjB;WAClB;AACR,UAAQ,YAAY;;;;AAKxB,eAAe,cACb,SACA,MACe;CACf,MAAM,EAAE,kBAAkB,eAAe;CACzC,MAAM,OAAO;EAAE;EAAkB;EAAY;CAC7C,MAAM,SAAS;EACb,GAAG;EACH,kCAAkC,KAAK;EACvC,kCAAkC,KAAK;EACxC;AAED,KAAI;AACF,QAAM,QAAQ,KAAK,8BAA8B,OAAO;SAClD;AACN,UAAQ,KACN,qEACD;AACD,QAAM,QAAQ,KAAK,8BAA8B,KAAK;;;AAI1D,eAAe,aAAa,SAAwC;CAClE,MAAM,EAAE,YAAY,MAAM,QAAQ,KAAK,4BAA4B;AACnE,QAAO"}