Files
oh-my-pi/packages/natives/bench/grep.ts
T
can1357 37c74bb19d perf: migrated timing measurements to Bun.nanoseconds() for higher precision
- Migrated timing measurements from `performance.now()` to `Bun.nanoseconds()` for higher precision benchmarking across all benchmark and timing-sensitive code.
- Updated elapsed time calculations to convert nanoseconds to milliseconds using division by 1e6 to maintain consistent time units.
- Increased benchmark precision from 4 to 6 decimal places for per-operation timing measurements.
- Refactored grep benchmark to use averaged timing values instead of storing individual iteration times, reducing memory overhead.
2026-02-05 06:19:49 +01:00

101 lines
3.4 KiB
TypeScript

import { spawn } from "node:child_process";
import * as path from "node:path";
import { grep } from "../src/index.js";
const ITERATIONS = 50;
const CONCURRENCY = 8;
const packages = path.resolve(import.meta.dir, "../..");
interface BenchCase {
name: string;
path: string;
pattern: string;
glob?: string;
}
const cases: BenchCase[] = [
{ name: "Medium (50 files)", path: path.resolve(packages, "tui/src"), pattern: "export", glob: "*.ts" },
{ name: "Large (200+ files)", path: path.resolve(packages, "coding-agent/src"), pattern: "import", glob: "*.ts" },
];
// Warmup
await grep({ pattern: "test", path: path.resolve(packages, "tui/src") });
console.log(`Benchmark: ${ITERATIONS} iterations per case\n`);
for (const c of cases) {
const grepArgs = { pattern: c.pattern, path: c.path, glob: c.glob };
const rgDefaultArgs = ["--hidden", "--no-ignore", "--no-ignore-vcs"];
const globArg = c.glob ? ["-g", c.glob] : [];
const runRg = (): Promise<string> =>
new Promise((resolve, reject) => {
const proc = spawn("rg", ["--json", ...rgDefaultArgs, ...globArg, c.pattern, c.path], {
stdio: ["ignore", "pipe", "ignore"],
});
let stdout = "";
proc.stdout.on("data", (data: Buffer) => {
stdout += data.toString();
});
proc.on("close", () => resolve(stdout));
proc.on("error", reject);
});
const countMatches = (result: string): number => {
const lines = result.split("\n").filter((l) => l.trim());
let matches = 0;
for (const line of lines) {
try {
if (JSON.parse(line).type === "match") matches++;
} catch {
/* ignore */
}
}
return matches;
};
// Capture match counts from a single run
const mainMatches = (await grep(grepArgs)).totalMatches;
const rgMatches = countMatches(await runRg());
// Main thread sequential
let start = Bun.nanoseconds();
for (let i = 0; i < ITERATIONS; i++) await grep(grepArgs);
const mainMs = (Bun.nanoseconds() - start) / 1e6 / ITERATIONS;
// Main thread concurrent (8x parallel)
start = Bun.nanoseconds();
for (let i = 0; i < ITERATIONS; i++) {
await Promise.all(Array.from({ length: CONCURRENCY }, () => grep(grepArgs)));
}
const mainConcurrentMs = (Bun.nanoseconds() - start) / 1e6 / ITERATIONS;
// Subprocess rg sequential
start = Bun.nanoseconds();
for (let i = 0; i < ITERATIONS; i++) await runRg();
const rgMs = (Bun.nanoseconds() - start) / 1e6 / ITERATIONS;
// Subprocess rg concurrent (8x parallel)
start = Bun.nanoseconds();
for (let i = 0; i < ITERATIONS; i++) {
await Promise.all(Array.from({ length: CONCURRENCY }, () => runRg()));
}
const rgConcurrentMs = (Bun.nanoseconds() - start) / 1e6 / ITERATIONS;
console.log(`${c.name}:`);
console.log(` Main thread: ${mainMs.toFixed(2)}ms (${mainMatches} matches)`);
console.log(` Main thread 8x: ${mainConcurrentMs.toFixed(2)}ms`);
console.log(` Subprocess rg: ${rgMs.toFixed(2)}ms (${rgMatches} matches)`);
console.log(` Subprocess rg 8x: ${rgConcurrentMs.toFixed(2)}ms`);
const mainVsRg = rgMs / mainMs;
const mainVsRgConcurrent = rgConcurrentMs / mainConcurrentMs;
console.log(
` => Main thread is ${mainVsRg > 1 ? `${mainVsRg.toFixed(1)}x faster` : `${(1 / mainVsRg).toFixed(1)}x slower`} than rg (sequential)`,
);
console.log(
` => Main thread is ${mainVsRgConcurrent > 1 ? `${mainVsRgConcurrent.toFixed(1)}x faster` : `${(1 / mainVsRgConcurrent).toFixed(1)}x slower`} than rg (8x concurrent)\n`,
);
}