@@ -21,6 +21,32 @@ function updateProgress(current, total, label = '', width) {
21
21
process . stdout . write ( `${ label } [${ bar } ] ${ current } /${ total } ` ) ;
22
22
}
23
23
24
+ /**
25
+ * Create a name filter function from CLI argument
26
+ * - Supports substring match (default)
27
+ * - Supports /regex/flags syntax
28
+ * @param {string|undefined } arg
29
+ * @returns {(name: string) => boolean }
30
+ */
31
+ function createNameFilter ( arg ) {
32
+ if ( ! arg ) {
33
+ return ( ) => true ;
34
+ }
35
+ if ( arg . startsWith ( '/' ) && arg . lastIndexOf ( '/' ) > 0 ) {
36
+ const lastSlash = arg . lastIndexOf ( '/' ) ;
37
+ const pattern = arg . slice ( 1 , lastSlash ) ;
38
+ const flags = arg . slice ( lastSlash + 1 ) ;
39
+ try {
40
+ const re = new RegExp ( pattern , flags ) ;
41
+ return ( name ) => re . test ( name ) ;
42
+ } catch ( e ) {
43
+ console . error ( 'Invalid regular expression for --filter:' , e . message ) ;
44
+ process . exit ( 1 ) ;
45
+ }
46
+ }
47
+ return ( name ) => name . includes ( arg ) ;
48
+ }
49
+
24
50
/**
25
51
* Calculate coefficient of variation (relative standard deviation)
26
52
* @param {Array<number> } values - Array of measurement values
@@ -238,24 +264,28 @@ function saveJsonResults(filePath, data) {
238
264
* @param {Object } results - Results object to store benchmark data
239
265
* @returns {Promise<void> }
240
266
*/
241
- async function singleRun ( results ) {
267
+ async function singleRun ( results , nameFilter ) {
242
268
const options = await defaultNodeSetup ( { } )
269
+ const benchmarkRunner = ( name , body ) => {
270
+ if ( nameFilter && ! nameFilter ( name ) ) {
271
+ return ;
272
+ }
273
+ const startTime = performance . now ( ) ;
274
+ body ( ) ;
275
+ const endTime = performance . now ( ) ;
276
+ const duration = endTime - startTime ;
277
+ if ( ! results [ name ] ) {
278
+ results [ name ] = [ ]
279
+ }
280
+ results [ name ] . push ( duration )
281
+ }
243
282
const { exports } = await instantiate ( {
244
283
...options ,
245
- imports : {
284
+ getImports : ( ) => ( {
246
285
benchmarkHelperNoop : ( ) => { } ,
247
286
benchmarkHelperNoopWithNumber : ( n ) => { } ,
248
- benchmarkRunner : ( name , body ) => {
249
- const startTime = performance . now ( ) ;
250
- body ( ) ;
251
- const endTime = performance . now ( ) ;
252
- const duration = endTime - startTime ;
253
- if ( ! results [ name ] ) {
254
- results [ name ] = [ ]
255
- }
256
- results [ name ] . push ( duration )
257
- }
258
- }
287
+ benchmarkRunner : benchmarkRunner
288
+ } )
259
289
} ) ;
260
290
exports . run ( ) ;
261
291
}
@@ -266,7 +296,7 @@ async function singleRun(results) {
266
296
* @param {Object } options - Adaptive sampling options
267
297
* @returns {Promise<void> }
268
298
*/
269
- async function runUntilStable ( results , options , width ) {
299
+ async function runUntilStable ( results , options , width , nameFilter , filterArg ) {
270
300
const {
271
301
minRuns = 5 ,
272
302
maxRuns = 50 ,
@@ -285,9 +315,14 @@ async function runUntilStable(results, options, width) {
285
315
// Update progress with estimated completion
286
316
updateProgress ( runs , maxRuns , "Benchmark Progress:" , width ) ;
287
317
288
- await singleRun ( results ) ;
318
+ await singleRun ( results , nameFilter ) ;
289
319
runs ++ ;
290
320
321
+ if ( runs === 1 && Object . keys ( results ) . length === 0 ) {
322
+ console . error ( `\nNo benchmarks matched filter: ${ filterArg } ` ) ;
323
+ process . exit ( 1 ) ;
324
+ }
325
+
291
326
// Check if we've reached minimum runs
292
327
if ( runs < minRuns ) continue ;
293
328
@@ -349,6 +384,7 @@ Options:
349
384
--min-runs=NUMBER Minimum runs for adaptive sampling (default: 5)
350
385
--max-runs=NUMBER Maximum runs for adaptive sampling (default: 50)
351
386
--target-cv=NUMBER Target coefficient of variation % (default: 5)
387
+ --filter=PATTERN Filter benchmarks by name (substring or /regex/flags)
352
388
--help Show this help message
353
389
` ) ;
354
390
}
@@ -363,7 +399,8 @@ async function main() {
363
399
adaptive : { type : 'boolean' , default : false } ,
364
400
'min-runs' : { type : 'string' , default : '5' } ,
365
401
'max-runs' : { type : 'string' , default : '50' } ,
366
- 'target-cv' : { type : 'string' , default : '5' }
402
+ 'target-cv' : { type : 'string' , default : '5' } ,
403
+ filter : { type : 'string' }
367
404
}
368
405
} ) ;
369
406
@@ -374,6 +411,8 @@ async function main() {
374
411
375
412
const results = { } ;
376
413
const width = 30 ;
414
+ const filterArg = args . values . filter ;
415
+ const nameFilter = createNameFilter ( filterArg ) ;
377
416
378
417
if ( args . values . adaptive ) {
379
418
// Adaptive sampling mode
@@ -388,7 +427,7 @@ async function main() {
388
427
console . log ( `Results will be saved to: ${ args . values . output } ` ) ;
389
428
}
390
429
391
- await runUntilStable ( results , options , width ) ;
430
+ await runUntilStable ( results , options , width , nameFilter , filterArg ) ;
392
431
} else {
393
432
// Fixed number of runs mode
394
433
const runs = parseInt ( args . values . runs , 10 ) ;
@@ -410,7 +449,12 @@ async function main() {
410
449
console . log ( "\nOverall Progress:" ) ;
411
450
for ( let i = 0 ; i < runs ; i ++ ) {
412
451
updateProgress ( i , runs , "Benchmark Runs:" , width ) ;
413
- await singleRun ( results ) ;
452
+ await singleRun ( results , nameFilter ) ;
453
+ if ( i === 0 && Object . keys ( results ) . length === 0 ) {
454
+ process . stdout . write ( "\n" ) ;
455
+ console . error ( `No benchmarks matched filter: ${ filterArg } ` ) ;
456
+ process . exit ( 1 ) ;
457
+ }
414
458
}
415
459
updateProgress ( runs , runs , "Benchmark Runs:" , width ) ;
416
460
console . log ( "\n" ) ;
0 commit comments