benchforge 0.1.8 → 0.1.11
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/README.md +69 -42
- package/dist/{BenchRunner-CSKN9zPy.d.mts → BenchRunner-BzyUfiyB.d.mts} +32 -8
- package/dist/{BrowserHeapSampler-DCeL42RE.mjs → BrowserHeapSampler-B6asLKWQ.mjs} +57 -57
- package/dist/BrowserHeapSampler-B6asLKWQ.mjs.map +1 -0
- package/dist/{GcStats-ByEovUi1.mjs → GcStats-wX7Xyblu.mjs} +15 -15
- package/dist/GcStats-wX7Xyblu.mjs.map +1 -0
- package/dist/HeapSampler-B8dtKHn1.mjs.map +1 -1
- package/dist/{TimingUtils-ClclVQ7E.mjs → TimingUtils-DwOwkc8G.mjs} +225 -225
- package/dist/TimingUtils-DwOwkc8G.mjs.map +1 -0
- package/dist/bin/benchforge.mjs +1 -1
- package/dist/browser/index.js +210 -210
- package/dist/index.d.mts +106 -48
- package/dist/index.mjs +3 -3
- package/dist/runners/WorkerScript.d.mts +1 -1
- package/dist/runners/WorkerScript.mjs +66 -66
- package/dist/runners/WorkerScript.mjs.map +1 -1
- package/dist/{src-HfimYuW_.mjs → src-B-DDaCa9.mjs} +1250 -991
- package/dist/src-B-DDaCa9.mjs.map +1 -0
- package/package.json +4 -3
- package/src/BenchMatrix.ts +125 -125
- package/src/BenchmarkReport.ts +50 -45
- package/src/HtmlDataPrep.ts +21 -21
- package/src/PermutationTest.ts +24 -24
- package/src/StandardSections.ts +45 -45
- package/src/StatisticalUtils.ts +60 -61
- package/src/browser/BrowserGcStats.ts +5 -5
- package/src/browser/BrowserHeapSampler.ts +63 -63
- package/src/cli/CliArgs.ts +20 -6
- package/src/cli/FilterBenchmarks.ts +5 -5
- package/src/cli/RunBenchCLI.ts +533 -476
- package/src/export/JsonExport.ts +10 -10
- package/src/export/PerfettoExport.ts +74 -74
- package/src/export/SpeedscopeExport.ts +202 -0
- package/src/heap-sample/HeapSampleReport.ts +143 -70
- package/src/heap-sample/HeapSampler.ts +55 -12
- package/src/heap-sample/ResolvedProfile.ts +89 -0
- package/src/html/HtmlReport.ts +33 -33
- package/src/html/HtmlTemplate.ts +67 -67
- package/src/html/browser/CIPlot.ts +50 -50
- package/src/html/browser/HistogramKde.ts +13 -13
- package/src/html/browser/LegendUtils.ts +48 -48
- package/src/html/browser/RenderPlots.ts +98 -98
- package/src/html/browser/SampleTimeSeries.ts +79 -79
- package/src/index.ts +6 -0
- package/src/matrix/MatrixFilter.ts +6 -6
- package/src/matrix/MatrixReport.ts +96 -96
- package/src/matrix/VariantLoader.ts +5 -5
- package/src/runners/AdaptiveWrapper.ts +151 -151
- package/src/runners/BasicRunner.ts +175 -175
- package/src/runners/BenchRunner.ts +8 -8
- package/src/runners/GcStats.ts +22 -22
- package/src/runners/RunnerOrchestrator.ts +168 -168
- package/src/runners/WorkerScript.ts +96 -96
- package/src/table-util/Formatters.ts +41 -36
- package/src/table-util/TableReport.ts +122 -122
- package/src/table-util/test/TableValueExtractor.ts +9 -9
- package/src/test/AdaptiveStatistics.integration.ts +7 -39
- package/src/test/HeapAttribution.test.ts +51 -0
- package/src/test/RunBenchCLI.test.ts +36 -11
- package/src/test/TestUtils.ts +24 -24
- package/src/test/fixtures/fn-export-bench.ts +3 -0
- package/src/test/fixtures/suite-export-bench.ts +16 -0
- package/src/tests/BenchMatrix.test.ts +12 -12
- package/src/tests/MatrixFilter.test.ts +15 -15
- package/dist/BrowserHeapSampler-DCeL42RE.mjs.map +0 -1
- package/dist/GcStats-ByEovUi1.mjs.map +0 -1
- package/dist/TimingUtils-ClclVQ7E.mjs.map +0 -1
- package/dist/src-HfimYuW_.mjs.map +0 -1
package/src/StatisticalUtils.ts
CHANGED
|
@@ -1,13 +1,3 @@
|
|
|
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
1
|
/** Bootstrap estimate with confidence interval and raw resample data */
|
|
12
2
|
export interface BootstrapResult {
|
|
13
3
|
estimate: number;
|
|
@@ -15,6 +5,32 @@ export interface BootstrapResult {
|
|
|
15
5
|
samples: number[];
|
|
16
6
|
}
|
|
17
7
|
|
|
8
|
+
export type CIDirection = "faster" | "slower" | "uncertain";
|
|
9
|
+
|
|
10
|
+
/** Binned histogram for efficient transfer to browser */
|
|
11
|
+
export interface HistogramBin {
|
|
12
|
+
x: number; // bin center
|
|
13
|
+
count: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Bootstrap confidence interval for percentage difference between two samples */
|
|
17
|
+
export interface DifferenceCI {
|
|
18
|
+
percent: number;
|
|
19
|
+
ci: [number, number];
|
|
20
|
+
direction: CIDirection;
|
|
21
|
+
/** Histogram of bootstrap distribution for visualization */
|
|
22
|
+
histogram?: HistogramBin[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Options for bootstrap resampling methods */
|
|
26
|
+
type BootstrapOptions = {
|
|
27
|
+
resamples?: number;
|
|
28
|
+
confidence?: number;
|
|
29
|
+
};
|
|
30
|
+
const confidence = 0.95;
|
|
31
|
+
const outlierMultiplier = 1.5; // Tukey's fence multiplier
|
|
32
|
+
const bootstrapSamples = 10000;
|
|
33
|
+
|
|
18
34
|
/** @return relative standard deviation (coefficient of variation) */
|
|
19
35
|
export function coefficientOfVariation(samples: number[]): number {
|
|
20
36
|
const mean = average(samples);
|
|
@@ -86,13 +102,6 @@ export function percentile(values: number[], p: number): number {
|
|
|
86
102
|
return sorted[Math.max(0, index)];
|
|
87
103
|
}
|
|
88
104
|
|
|
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
105
|
/** @return bootstrap resample with replacement */
|
|
97
106
|
export function createResample(samples: number[]): number[] {
|
|
98
107
|
const n = samples.length;
|
|
@@ -100,50 +109,6 @@ export function createResample(samples: number[]): number[] {
|
|
|
100
109
|
return Array.from({ length: n }, rand);
|
|
101
110
|
}
|
|
102
111
|
|
|
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
112
|
/** @return bootstrap CI for percentage difference between baseline and current medians */
|
|
148
113
|
export function bootstrapDifferenceCI(
|
|
149
114
|
baseline: number[],
|
|
@@ -174,3 +139,37 @@ export function bootstrapDifferenceCI(
|
|
|
174
139
|
const histogram = binValues(diffs);
|
|
175
140
|
return { percent: observedPercent, ci, direction, histogram };
|
|
176
141
|
}
|
|
142
|
+
|
|
143
|
+
/** @return medians from bootstrap resamples */
|
|
144
|
+
function generateMedians(samples: number[], resamples: number): number[] {
|
|
145
|
+
return Array.from({ length: resamples }, () =>
|
|
146
|
+
percentile(createResample(samples), 0.5),
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** @return confidence interval [lower, upper] */
|
|
151
|
+
function computeInterval(
|
|
152
|
+
medians: number[],
|
|
153
|
+
confidence: number,
|
|
154
|
+
): [number, number] {
|
|
155
|
+
const alpha = (1 - confidence) / 2;
|
|
156
|
+
const lower = percentile(medians, alpha);
|
|
157
|
+
const upper = percentile(medians, 1 - alpha);
|
|
158
|
+
return [lower, upper];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Bin values into histogram for compact visualization */
|
|
162
|
+
function binValues(values: number[], binCount = 30): HistogramBin[] {
|
|
163
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
164
|
+
const min = sorted[0];
|
|
165
|
+
const max = sorted[sorted.length - 1];
|
|
166
|
+
if (min === max) return [{ x: min, count: values.length }];
|
|
167
|
+
|
|
168
|
+
const step = (max - min) / binCount;
|
|
169
|
+
const counts = new Array(binCount).fill(0);
|
|
170
|
+
for (const v of values) {
|
|
171
|
+
const bin = Math.min(Math.floor((v - min) / step), binCount - 1);
|
|
172
|
+
counts[bin]++;
|
|
173
|
+
}
|
|
174
|
+
return counts.map((count, i) => ({ x: min + (i + 0.5) * step, count }));
|
|
175
|
+
}
|
|
@@ -32,13 +32,13 @@ export function parseGcTraceEvents(traceEvents: TraceEvent[]): GcEvent[] {
|
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/** Parse CDP trace events and aggregate into GcStats */
|
|
36
|
+
export function browserGcStats(traceEvents: TraceEvent[]): GcStats {
|
|
37
|
+
return aggregateGcStats(parseGcTraceEvents(traceEvents));
|
|
38
|
+
}
|
|
39
|
+
|
|
35
40
|
function gcType(name: string): GcEvent["type"] | undefined {
|
|
36
41
|
if (name === "MinorGC") return "scavenge";
|
|
37
42
|
if (name === "MajorGC") return "mark-compact";
|
|
38
43
|
return undefined;
|
|
39
44
|
}
|
|
40
|
-
|
|
41
|
-
/** Parse CDP trace events and aggregate into GcStats */
|
|
42
|
-
export function browserGcStats(traceEvents: TraceEvent[]): GcStats {
|
|
43
|
-
return aggregateGcStats(parseGcTraceEvents(traceEvents));
|
|
44
|
-
}
|
|
@@ -93,6 +93,32 @@ export async function profileBrowser(
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
/** Forward Chrome's stdout/stderr to the terminal so V8 flag output is visible. */
|
|
97
|
+
function pipeChromeOutput(server: BrowserServer): void {
|
|
98
|
+
const proc = server.process();
|
|
99
|
+
const pipe = (stream: NodeJS.ReadableStream | null) =>
|
|
100
|
+
stream?.on("data", (chunk: Buffer) => {
|
|
101
|
+
for (const line of chunk.toString().split("\n")) {
|
|
102
|
+
const text = line.trim();
|
|
103
|
+
if (text) process.stderr.write(`[chrome] ${text}\n`);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
pipe(proc.stdout);
|
|
107
|
+
pipe(proc.stderr);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Start CDP GC tracing, returns the event collector array. */
|
|
111
|
+
async function startGcTracing(cdp: CDPSession): Promise<TraceEvent[]> {
|
|
112
|
+
const events: TraceEvent[] = [];
|
|
113
|
+
cdp.on("Tracing.dataCollected", ({ value }) => {
|
|
114
|
+
for (const e of value) events.push(e as unknown as TraceEvent);
|
|
115
|
+
});
|
|
116
|
+
await cdp.send("Tracing.start", {
|
|
117
|
+
traceConfig: { includedCategories: ["v8", "v8.gc"] },
|
|
118
|
+
});
|
|
119
|
+
return events;
|
|
120
|
+
}
|
|
121
|
+
|
|
96
122
|
/** Inject __start/__lap as in-page functions, expose __done for results collection.
|
|
97
123
|
* __start/__lap are pure in-page (zero CDP overhead). First __start() triggers
|
|
98
124
|
* instrument start. __done() stops instruments and collects timing data. */
|
|
@@ -147,57 +173,6 @@ async function setupLapMode(
|
|
|
147
173
|
return { promise, cancel: () => clearTimeout(timer) };
|
|
148
174
|
}
|
|
149
175
|
|
|
150
|
-
/** In-page timing functions injected via addInitScript (zero CDP overhead).
|
|
151
|
-
* __start/__lap collect timestamps, __done delegates to exposed __benchCollect. */
|
|
152
|
-
function injectLapFunctions(): void {
|
|
153
|
-
const g = globalThis as any;
|
|
154
|
-
g.__benchSamples = [];
|
|
155
|
-
g.__benchLastTime = 0;
|
|
156
|
-
g.__benchFirstStart = 0;
|
|
157
|
-
|
|
158
|
-
g.__start = () => {
|
|
159
|
-
const now = performance.now();
|
|
160
|
-
g.__benchLastTime = now;
|
|
161
|
-
if (!g.__benchFirstStart) {
|
|
162
|
-
g.__benchFirstStart = now;
|
|
163
|
-
return g.__benchInstrumentStart();
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
g.__lap = () => {
|
|
168
|
-
const now = performance.now();
|
|
169
|
-
g.__benchSamples.push(now - g.__benchLastTime);
|
|
170
|
-
g.__benchLastTime = now;
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
g.__done = () => {
|
|
174
|
-
const wall = g.__benchFirstStart
|
|
175
|
-
? performance.now() - g.__benchFirstStart
|
|
176
|
-
: 0;
|
|
177
|
-
return g.__benchCollect(g.__benchSamples.slice(), wall);
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function heapSamplingParams(samplingInterval: number) {
|
|
182
|
-
return {
|
|
183
|
-
samplingInterval,
|
|
184
|
-
includeObjectsCollectedByMajorGC: true,
|
|
185
|
-
includeObjectsCollectedByMinorGC: true,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/** Start CDP GC tracing, returns the event collector array. */
|
|
190
|
-
async function startGcTracing(cdp: CDPSession): Promise<TraceEvent[]> {
|
|
191
|
-
const events: TraceEvent[] = [];
|
|
192
|
-
cdp.on("Tracing.dataCollected", ({ value }) => {
|
|
193
|
-
for (const e of value) events.push(e as unknown as TraceEvent);
|
|
194
|
-
});
|
|
195
|
-
await cdp.send("Tracing.start", {
|
|
196
|
-
traceConfig: { includedCategories: ["v8", "v8.gc"] },
|
|
197
|
-
});
|
|
198
|
-
return events;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
176
|
/** Bench function mode: run window.__bench in a timed iteration loop. */
|
|
202
177
|
async function runBenchLoop(
|
|
203
178
|
page: Page,
|
|
@@ -254,18 +229,43 @@ async function collectTracing(
|
|
|
254
229
|
return browserGcStats(traceEvents);
|
|
255
230
|
}
|
|
256
231
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
232
|
+
function heapSamplingParams(samplingInterval: number) {
|
|
233
|
+
return {
|
|
234
|
+
samplingInterval,
|
|
235
|
+
includeObjectsCollectedByMajorGC: true,
|
|
236
|
+
includeObjectsCollectedByMinorGC: true,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** In-page timing functions injected via addInitScript (zero CDP overhead).
|
|
241
|
+
* __start/__lap collect timestamps, __done delegates to exposed __benchCollect. */
|
|
242
|
+
function injectLapFunctions(): void {
|
|
243
|
+
const g = globalThis as any;
|
|
244
|
+
g.__benchSamples = [];
|
|
245
|
+
g.__benchLastTime = 0;
|
|
246
|
+
g.__benchFirstStart = 0;
|
|
247
|
+
|
|
248
|
+
g.__start = () => {
|
|
249
|
+
const now = performance.now();
|
|
250
|
+
g.__benchLastTime = now;
|
|
251
|
+
if (!g.__benchFirstStart) {
|
|
252
|
+
g.__benchFirstStart = now;
|
|
253
|
+
return g.__benchInstrumentStart();
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
g.__lap = () => {
|
|
258
|
+
const now = performance.now();
|
|
259
|
+
g.__benchSamples.push(now - g.__benchLastTime);
|
|
260
|
+
g.__benchLastTime = now;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
g.__done = () => {
|
|
264
|
+
const wall = g.__benchFirstStart
|
|
265
|
+
? performance.now() - g.__benchFirstStart
|
|
266
|
+
: 0;
|
|
267
|
+
return g.__benchCollect(g.__benchSamples.slice(), wall);
|
|
268
|
+
};
|
|
269
269
|
}
|
|
270
270
|
|
|
271
271
|
export { profileBrowser as profileBrowserHeap };
|
package/src/cli/CliArgs.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { Argv, InferredOptionTypes } from "yargs";
|
|
2
2
|
import yargs from "yargs";
|
|
3
3
|
|
|
4
|
-
export const defaultAdaptiveMaxTime = 20;
|
|
5
|
-
|
|
6
4
|
export type Configure<T> = (yargs: Argv) => Argv<T>;
|
|
7
5
|
|
|
8
|
-
/** CLI args type inferred from cliOptions */
|
|
9
|
-
export type DefaultCliArgs = InferredOptionTypes<typeof cliOptions
|
|
6
|
+
/** CLI args type inferred from cliOptions, plus optional file positional */
|
|
7
|
+
export type DefaultCliArgs = InferredOptionTypes<typeof cliOptions> & {
|
|
8
|
+
file?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const defaultAdaptiveMaxTime = 20;
|
|
10
12
|
|
|
11
13
|
// biome-ignore format: compact option definitions
|
|
12
14
|
const cliOptions = {
|
|
@@ -25,7 +27,9 @@ const cliOptions = {
|
|
|
25
27
|
html: { type: "boolean", default: false, describe: "generate HTML report and open in browser" },
|
|
26
28
|
"export-html": { type: "string", requiresArg: true, describe: "export HTML report to specified file" },
|
|
27
29
|
json: { type: "string", requiresArg: true, describe: "export benchmark data to JSON file" },
|
|
28
|
-
perfetto:
|
|
30
|
+
"export-perfetto": { type: "string", requiresArg: true, describe: "export Perfetto trace file (view at ui.perfetto.dev)" },
|
|
31
|
+
speedscope: { type: "boolean", default: false, describe: "open heap profile in speedscope (via npx)" },
|
|
32
|
+
"export-speedscope": { type: "string", requiresArg: true, describe: "export heap profile as speedscope JSON" },
|
|
29
33
|
"trace-opt": { type: "boolean", default: false, describe: "trace V8 optimization tiers (requires --allow-natives-syntax)" },
|
|
30
34
|
"skip-settle": { type: "boolean", default: false, describe: "skip post-warmup settle time (see V8 optimization cold start)" },
|
|
31
35
|
"pause-first": { type: "number", describe: "iterations before first pause (then pause-interval applies)" },
|
|
@@ -39,6 +43,7 @@ const cliOptions = {
|
|
|
39
43
|
"heap-rows": { type: "number", default: 20, describe: "top allocation sites to show" },
|
|
40
44
|
"heap-stack": { type: "number", default: 3, describe: "call stack depth to display" },
|
|
41
45
|
"heap-verbose": { type: "boolean", default: false, describe: "verbose output with file:// paths and line numbers" },
|
|
46
|
+
"heap-raw": { type: "boolean", default: false, describe: "dump every raw heap sample (ordinal, size, stack)" },
|
|
42
47
|
"heap-user-only": { type: "boolean", default: false, describe: "filter to user code only (hide node internals)" },
|
|
43
48
|
url: { type: "string", requiresArg: true, describe: "page URL for browser profiling (enables browser mode)" },
|
|
44
49
|
headless: { type: "boolean", default: true, describe: "run browser in headless mode" },
|
|
@@ -48,7 +53,16 @@ const cliOptions = {
|
|
|
48
53
|
|
|
49
54
|
/** @return yargs with standard benchmark options */
|
|
50
55
|
export function defaultCliArgs(yargsInstance: Argv): Argv<DefaultCliArgs> {
|
|
51
|
-
return yargsInstance
|
|
56
|
+
return yargsInstance
|
|
57
|
+
.command("$0 [file]", "run benchmarks", y => {
|
|
58
|
+
y.positional("file", {
|
|
59
|
+
type: "string",
|
|
60
|
+
describe: "benchmark file to run",
|
|
61
|
+
});
|
|
62
|
+
})
|
|
63
|
+
.options(cliOptions)
|
|
64
|
+
.help()
|
|
65
|
+
.strict() as Argv<DefaultCliArgs>;
|
|
52
66
|
}
|
|
53
67
|
|
|
54
68
|
/** @return parsed command line arguments */
|
|
@@ -55,14 +55,14 @@ function stripCaseSuffix(name: string): string {
|
|
|
55
55
|
return name.replace(/ \[.*?\]$/, "");
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
/** Escape regex special characters */
|
|
59
|
-
function escapeRegex(str: string): string {
|
|
60
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
58
|
/** Ensure at least one benchmark matches filter */
|
|
64
59
|
function validateFilteredSuite(groups: BenchGroup[], filter?: string): void {
|
|
65
60
|
if (groups.every(g => g.benchmarks.length === 0)) {
|
|
66
61
|
throw new Error(`No benchmarks match filter: "${filter}"`);
|
|
67
62
|
}
|
|
68
63
|
}
|
|
64
|
+
|
|
65
|
+
/** Escape regex special characters */
|
|
66
|
+
function escapeRegex(str: string): string {
|
|
67
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
68
|
+
}
|