branch: main
codspeed-convert.js
3035 bytesRaw
#!/usr/bin/env node

/**
 * Converts workers-rs benchmark results into CodSpeed walltime format.
 * Based on the codspeed-rust 4.1.0 result schema.
 *
 * Usage: node codspeed-convert.js <input.json>
 *
 * Writes results to $CODSPEED_PROFILE_FOLDER/results/<pid>.json
 * or ./codspeed-results/<pid>.json as fallback.
 */

import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
import { join } from 'node:path';

const IQR_OUTLIER_FACTOR = 1.5;
const STDEV_OUTLIER_FACTOR = 3.0;

function quantile(sorted, q) {
  const pos = (sorted.length - 1) * q;
  const base = Math.floor(pos);
  const rest = pos - base;
  if (sorted[base + 1] !== undefined) {
    return sorted[base] + rest * (sorted[base + 1] - sorted[base]);
  }
  return sorted[base];
}

function computeStats(name, uri, timesMs) {
  // Convert ms to ns
  const timesNs = timesMs.map(t => t * 1_000_000);
  const sorted = [...timesNs].sort((a, b) => a - b);
  const rounds = timesNs.length;

  const mean = timesNs.reduce((a, b) => a + b, 0) / rounds;
  const stdev = rounds < 2 ? 0 : Math.sqrt(
    timesNs.reduce((sum, t) => sum + (t - mean) ** 2, 0) / (rounds - 1)
  );

  const q1 = quantile(sorted, 0.25);
  const median = quantile(sorted, 0.5);
  const q3 = quantile(sorted, 0.75);
  const iqr = q3 - q1;

  const iqrOutliers = timesNs.filter(
    t => t < q1 - IQR_OUTLIER_FACTOR * iqr || t > q3 + IQR_OUTLIER_FACTOR * iqr
  ).length;
  const stdevOutliers = timesNs.filter(
    t => t < mean - STDEV_OUTLIER_FACTOR * stdev || t > mean + STDEV_OUTLIER_FACTOR * stdev
  ).length;

  return {
    name,
    uri,
    config: {},
    stats: {
      min_ns: sorted[0],
      max_ns: sorted[sorted.length - 1],
      mean_ns: mean,
      stdev_ns: stdev,
      q1_ns: q1,
      median_ns: median,
      q3_ns: q3,
      rounds,
      total_time: timesNs.reduce((a, b) => a + b, 0) / 1_000_000_000,
      iqr_outlier_rounds: iqrOutliers,
      stdev_outlier_rounds: stdevOutliers,
      iter_per_round: 1,
      warmup_iters: 3,
    },
  };
}

const inputPath = process.argv[2];
if (!inputPath) {
  console.error('Usage: node codspeed-convert.js <input.json>');
  process.exit(1);
}

const results = JSON.parse(readFileSync(inputPath, 'utf-8'));

const benchmarks = [
  computeStats(
    'worker_internal_time',
    'benchmark/src/lib.rs::handle_benchmark::worker_internal_time',
    results.map(r => r.workerDuration)
  ),
  computeStats(
    'e2e_time',
    'benchmark/run.js::runBenchmark::e2e_time',
    results.map(r => r.nodeJsDuration)
  ),
];

const output = {
  creator: {
    name: 'codspeed-node',
    version: '4.1.0',
    pid: process.pid,
  },
  instrument: { type: 'walltime' },
  benchmarks,
};

const profileFolder = process.env.CODSPEED_PROFILE_FOLDER || 'codspeed-results';
const resultsDir = join(profileFolder, 'results');
mkdirSync(resultsDir, { recursive: true });

const outputPath = join(resultsDir, `${process.pid}.json`);
writeFileSync(outputPath, JSON.stringify(output, null, 2));
console.log(`CodSpeed results written to ${outputPath}`);