Skip to content

Commit d9b5e3a

Browse files
Merge pull request #439 from PassiveLogic/fix/fix-benchmark-setup
fix: Benchmark setup fixes and filtering option
2 parents 2d23296 + 3b21f4a commit d9b5e3a

File tree

3 files changed

+72
-21
lines changed

3 files changed

+72
-21
lines changed

Benchmarks/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,11 @@ node run.js --adaptive --output=stable-results.json
2727

2828
# Run benchmarks and compare with previous results
2929
node run.js --baseline=previous-results.json
30+
31+
# Run only a subset of benchmarks
32+
# Substring match
33+
node run.js --filter=Call
34+
# Regex (with flags)
35+
node run.js --filter=/^Property access\//
36+
node run.js --filter=/string/i
3037
```

Benchmarks/Sources/Benchmarks.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class Benchmark {
1414
body()
1515
return .undefined
1616
}
17-
benchmarkRunner("\(title)/\(name)", jsBody)
17+
try! benchmarkRunner("\(title)/\(name)", jsBody)
1818
}
1919
}
2020

@@ -24,13 +24,13 @@ class Benchmark {
2424

2525
call.testSuite("JavaScript function call through Wasm import") {
2626
for _ in 0..<20_000_000 {
27-
benchmarkHelperNoop()
27+
try! benchmarkHelperNoop()
2828
}
2929
}
3030

3131
call.testSuite("JavaScript function call through Wasm import with int") {
3232
for _ in 0..<10_000_000 {
33-
benchmarkHelperNoopWithNumber(42)
33+
try! benchmarkHelperNoopWithNumber(42)
3434
}
3535
}
3636

Benchmarks/run.js

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,32 @@ function updateProgress(current, total, label = '', width) {
2121
process.stdout.write(`${label} [${bar}] ${current}/${total}`);
2222
}
2323

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+
2450
/**
2551
* Calculate coefficient of variation (relative standard deviation)
2652
* @param {Array<number>} values - Array of measurement values
@@ -238,24 +264,28 @@ function saveJsonResults(filePath, data) {
238264
* @param {Object} results - Results object to store benchmark data
239265
* @returns {Promise<void>}
240266
*/
241-
async function singleRun(results) {
267+
async function singleRun(results, nameFilter) {
242268
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+
}
243282
const { exports } = await instantiate({
244283
...options,
245-
imports: {
284+
getImports: () => ({
246285
benchmarkHelperNoop: () => { },
247286
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+
})
259289
});
260290
exports.run();
261291
}
@@ -266,7 +296,7 @@ async function singleRun(results) {
266296
* @param {Object} options - Adaptive sampling options
267297
* @returns {Promise<void>}
268298
*/
269-
async function runUntilStable(results, options, width) {
299+
async function runUntilStable(results, options, width, nameFilter, filterArg) {
270300
const {
271301
minRuns = 5,
272302
maxRuns = 50,
@@ -285,9 +315,14 @@ async function runUntilStable(results, options, width) {
285315
// Update progress with estimated completion
286316
updateProgress(runs, maxRuns, "Benchmark Progress:", width);
287317

288-
await singleRun(results);
318+
await singleRun(results, nameFilter);
289319
runs++;
290320

321+
if (runs === 1 && Object.keys(results).length === 0) {
322+
console.error(`\nNo benchmarks matched filter: ${filterArg}`);
323+
process.exit(1);
324+
}
325+
291326
// Check if we've reached minimum runs
292327
if (runs < minRuns) continue;
293328

@@ -349,6 +384,7 @@ Options:
349384
--min-runs=NUMBER Minimum runs for adaptive sampling (default: 5)
350385
--max-runs=NUMBER Maximum runs for adaptive sampling (default: 50)
351386
--target-cv=NUMBER Target coefficient of variation % (default: 5)
387+
--filter=PATTERN Filter benchmarks by name (substring or /regex/flags)
352388
--help Show this help message
353389
`);
354390
}
@@ -363,7 +399,8 @@ async function main() {
363399
adaptive: { type: 'boolean', default: false },
364400
'min-runs': { type: 'string', default: '5' },
365401
'max-runs': { type: 'string', default: '50' },
366-
'target-cv': { type: 'string', default: '5' }
402+
'target-cv': { type: 'string', default: '5' },
403+
filter: { type: 'string' }
367404
}
368405
});
369406

@@ -374,6 +411,8 @@ async function main() {
374411

375412
const results = {};
376413
const width = 30;
414+
const filterArg = args.values.filter;
415+
const nameFilter = createNameFilter(filterArg);
377416

378417
if (args.values.adaptive) {
379418
// Adaptive sampling mode
@@ -388,7 +427,7 @@ async function main() {
388427
console.log(`Results will be saved to: ${args.values.output}`);
389428
}
390429

391-
await runUntilStable(results, options, width);
430+
await runUntilStable(results, options, width, nameFilter, filterArg);
392431
} else {
393432
// Fixed number of runs mode
394433
const runs = parseInt(args.values.runs, 10);
@@ -410,7 +449,12 @@ async function main() {
410449
console.log("\nOverall Progress:");
411450
for (let i = 0; i < runs; i++) {
412451
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+
}
414458
}
415459
updateProgress(runs, runs, "Benchmark Runs:", width);
416460
console.log("\n");

0 commit comments

Comments
 (0)