#!/usr/bin/env node /** * Benchmark runner for workers-rs * * This script runs performance benchmarks against the worker server. * It measures the time taken to complete a benchmark that makes 10 parallel * sub-requests, each streaming 1MB of data internally. */ import { Miniflare } from 'miniflare'; import { writeFileSync } from 'node:fs'; async function runBenchmark() { console.log('šŸš€ Starting workers-rs benchmark suite\n'); // Initialize Miniflare instance with the compiled worker console.log('šŸ“¦ Initializing Miniflare...'); const mf = new Miniflare({ workers: [ { name: 'benchmark', scriptPath: './build/index.js', compatibilityDate: '2025-01-06', modules: true, modulesRules: [ { type: 'CompiledWasm', include: ['**/*.wasm'], fallthrough: true } ], outboundService: 'benchmark', } ] }); const mfUrl = await mf.ready; console.log(`āœ… Miniflare ready at ${mfUrl}\n`); // Run warmup requests console.log('šŸ”„ Running warmup requests...'); for (let i = 0; i < 3; i++) { await mf.dispatchFetch(`${mfUrl}benchmark`); } console.log('āœ… Warmup complete\n'); // Run benchmark iterations const iterations = 20; const results = []; console.log(`šŸ“Š Running ${iterations} benchmark iterations...\n`); for (let i = 0; i < iterations; i++) { const iterStart = Date.now(); const response = await mf.dispatchFetch(`${mfUrl}benchmark`); const iterEnd = Date.now(); const nodeJsDuration = iterEnd - iterStart; const result = await response.json(); if (!result.success) { console.error(`āŒ Iteration ${i + 1} failed:`, result.errors); await mf.dispose(); process.exit(1); } results.push({ iteration: i + 1, nodeJsDuration, workerDuration: result.duration_ms, totalBytes: result.total_bytes, numRequests: result.num_requests, }); console.log(` Iteration ${i + 1}:`); console.log(` Node.js end-to-end time: ${nodeJsDuration}ms`); console.log(` Worker internal time: ${result.duration_ms}ms`); console.log(` Data transferred: ${(result.total_bytes / (1024 * 1024)).toFixed(2)}MB`); console.log(` Sub-requests: ${result.num_requests}`); console.log(); } // Calculate statistics const nodeJsDurations = results.map(r => r.nodeJsDuration); const workerDurations = results.map(r => r.workerDuration); const avgNodeJs = nodeJsDurations.reduce((a, b) => a + b, 0) / iterations; const avgWorker = workerDurations.reduce((a, b) => a + b, 0) / iterations; const minNodeJs = Math.min(...nodeJsDurations); const maxNodeJs = Math.max(...nodeJsDurations); const minWorker = Math.min(...workerDurations); const maxWorker = Math.max(...workerDurations); // Print summary console.log('━'.repeat(60)); console.log('šŸ“ˆ BENCHMARK SUMMARY'); console.log('━'.repeat(60)); console.log(); console.log('Node.js End-to-End Time:'); console.log(` Average: ${avgNodeJs.toFixed(2)}ms`); console.log(` Min: ${minNodeJs.toFixed(2)}ms`); console.log(` Max: ${maxNodeJs.toFixed(2)}ms`); console.log(); console.log('Worker Internal Time:'); console.log(` Average: ${avgWorker.toFixed(2)}ms`); console.log(` Min: ${minWorker.toFixed(2)}ms`); console.log(` Max: ${maxWorker.toFixed(2)}ms`); console.log(); console.log('Benchmark Configuration:'); console.log(` Parallel sub-requests: 10`); console.log(` Data per sub-request: 1MB`); console.log(` Total data per iteration: 10MB`); console.log(` Iterations: ${iterations}`); console.log(); console.log('━'.repeat(60)); // Calculate throughput const totalBytes = results[0].totalBytes; const throughputMbps = (totalBytes * 8 / (avgWorker / 1000)) / (1024 * 1024); console.log(`šŸš€ Average throughput: ${throughputMbps.toFixed(2)} Mbps`); console.log('━'.repeat(60)); // Write results JSON if BENCH_RESULT env is set const resultPath = process.env.BENCH_RESULT; if (resultPath) { writeFileSync(resultPath, JSON.stringify(results, null, 2)); console.log(`\nšŸ“ Results written to ${resultPath}`); } // Cleanup await mf.dispose(); console.log('\nāœ… Benchmark complete!'); } runBenchmark().catch((error) => { console.error('āŒ Benchmark failed:', error); process.exit(1); });