branch: main
run.js
4438 bytesRaw
#!/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);
});