Skip to content

Commit 9f465b4

Browse files
committed
feat(benchmark): add benchmark runner
1 parent b21f1ef commit 9f465b4

File tree

1 file changed

+145
-0
lines changed

1 file changed

+145
-0
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/**
2+
* Run complete benchmark suite
3+
*
4+
* Usage: npx tsx runners/run-all.ts [--skip-compile] [--skip-prove]
5+
*/
6+
7+
import * as fs from 'fs';
8+
import * as path from 'path';
9+
import { fileURLToPath } from 'url';
10+
import { ALL_CONFIGS, SCALING_CONFIGS, RSA_CONFIGS, FEATURE_CONFIGS, PRECOMPUTE_CONFIGS, PRECOMPUTE_REDUCED_CONFIGS } from '../config/circuits.config.js';
11+
import { BENCHMARK_CONFIG } from '../config/benchmark.config.js';
12+
import { generateAllCircuits } from '../lib/circuit-generator.js';
13+
import { compileAllCircuits, ConstraintInfo } from '../lib/constraint-counter.js';
14+
import { runBenchmarks, ProvingResult } from '../lib/prover.js';
15+
import { generateReport, saveReports } from '../lib/reporter.js';
16+
17+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
18+
19+
async function main() {
20+
const args = process.argv.slice(2);
21+
const skipCompile = args.includes('--skip-compile');
22+
const skipProve = args.includes('--skip-prove');
23+
const category = args.find(a => ['--scaling', '--rsa', '--features', '--precompute'].includes(a));
24+
25+
// Select configs based on category
26+
let configs = ALL_CONFIGS;
27+
let categoryName = 'All';
28+
29+
if (category === '--scaling') {
30+
configs = SCALING_CONFIGS;
31+
categoryName = 'Scaling';
32+
} else if (category === '--rsa') {
33+
configs = RSA_CONFIGS;
34+
categoryName = 'RSA';
35+
} else if (category === '--features') {
36+
configs = FEATURE_CONFIGS;
37+
categoryName = 'Features';
38+
} else if (category === '--precompute') {
39+
configs = [...PRECOMPUTE_CONFIGS, ...PRECOMPUTE_REDUCED_CONFIGS];
40+
categoryName = 'Precompute';
41+
}
42+
43+
console.log('═'.repeat(60));
44+
console.log(` ZK-Email-Verify Benchmark Suite (${categoryName})`);
45+
console.log('═'.repeat(60));
46+
console.log();
47+
48+
// Check prerequisites
49+
if (!skipProve && !fs.existsSync(BENCHMARK_CONFIG.ptauFile)) {
50+
console.error(`Error: Powers of tau file not found: ${BENCHMARK_CONFIG.ptauFile}`);
51+
console.error('Download with:');
52+
console.error(` wget -O "${BENCHMARK_CONFIG.ptauFile}" https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_22.ptau`);
53+
process.exit(1);
54+
}
55+
56+
// Check for input files
57+
const inputsExist = configs.some(c =>
58+
fs.existsSync(path.join(BENCHMARK_CONFIG.inputsDir, `${c.id}.json`))
59+
);
60+
61+
if (!skipProve && !inputsExist) {
62+
console.error('Error: No input files found. Run these steps first:');
63+
console.error(' npm run generate-keys');
64+
console.error(' npm run generate-emails');
65+
console.error(' npm run generate-circuit-inputs');
66+
process.exit(1);
67+
}
68+
69+
let constraintResults: Map<string, ConstraintInfo> = new Map();
70+
let provingResults: ProvingResult[] = [];
71+
72+
// Phase 1: Generate circuits
73+
console.log('Phase 1: Generating circuit files...');
74+
console.log('─'.repeat(40));
75+
const circuitPaths = generateAllCircuits(configs);
76+
console.log(`Generated ${circuitPaths.size} circuits\n`);
77+
78+
// Phase 2: Compile and count constraints
79+
if (!skipCompile) {
80+
console.log('Phase 2: Compiling circuits and counting constraints...');
81+
console.log('─'.repeat(40));
82+
console.log('This may take a while for large circuits...\n');
83+
84+
console.log('ID\t\t\tConstraints\tTime');
85+
console.log('─'.repeat(60));
86+
87+
constraintResults = await compileAllCircuits(configs, false);
88+
89+
console.log('─'.repeat(60));
90+
console.log(`Compiled ${constraintResults.size} circuits\n`);
91+
} else {
92+
console.log('Phase 2: Skipped (--skip-compile)\n');
93+
}
94+
95+
// Phase 3: Run proving benchmarks
96+
if (!skipProve) {
97+
console.log(`Phase 3: Running proving benchmarks (${BENCHMARK_CONFIG.numRuns} runs each)...`);
98+
console.log('─'.repeat(40));
99+
100+
provingResults = await runBenchmarks(configs, BENCHMARK_CONFIG.numRuns);
101+
102+
const successful = provingResults.filter(r => r.success).length;
103+
console.log(`\nCompleted ${provingResults.length} runs (${successful} successful)\n`);
104+
} else {
105+
console.log('Phase 3: Skipped (--skip-prove)\n');
106+
}
107+
108+
// Phase 4: Generate reports
109+
if (constraintResults.size > 0 || provingResults.length > 0) {
110+
console.log('Phase 4: Generating reports...');
111+
console.log('─'.repeat(40));
112+
113+
const report = generateReport(
114+
Array.from(constraintResults.values()),
115+
provingResults
116+
);
117+
118+
const paths = saveReports(report);
119+
120+
console.log(` JSON: ${paths.jsonPath}`);
121+
console.log(` CSV: ${paths.csvPath}`);
122+
console.log(` MD: ${paths.mdPath}`);
123+
console.log();
124+
}
125+
126+
// Summary
127+
console.log('═'.repeat(60));
128+
console.log(' Benchmark Complete');
129+
console.log('═'.repeat(60));
130+
131+
if (constraintResults.size > 0) {
132+
const constraints = Array.from(constraintResults.values()).map(c => c.constraints);
133+
console.log(`\nConstraint Range: ${Math.min(...constraints).toLocaleString()} - ${Math.max(...constraints).toLocaleString()}`);
134+
}
135+
136+
if (provingResults.length > 0) {
137+
const successful = provingResults.filter(r => r.success);
138+
if (successful.length > 0) {
139+
const avgProve = successful.reduce((a, b) => a + b.provingTimeMs, 0) / successful.length;
140+
console.log(`Avg Proving Time: ${(avgProve / 1000).toFixed(2)}s`);
141+
}
142+
}
143+
}
144+
145+
main().catch(console.error);

0 commit comments

Comments
 (0)