import assert from 'node:assert';
import childProcess from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { localRepoPath } from '../utils.ts';
import type { Runtime } from './args.ts';
import {
memoryCommandArgsByRuntime,
timingCommandArgsByRuntime,
} from './config.ts';
export function getBenchmarkName(modulePath: string, runtime: Runtime): string {
return runWorkerFile(
localRepoPath('resources/benchmark/worker-name.js'),
modulePath,
timingCommandArgsByRuntime[runtime],
runtime,
) as string;
}
export function sampleTimingModule(
modulePath: string,
runtime: Runtime = inferRuntimeFromExecPath(process.execPath),
): number {
return runWorkerFile(
localRepoPath('resources/benchmark/worker-timing.js'),
modulePath,
timingCommandArgsByRuntime[runtime],
runtime,
) as number;
}
export function sampleMemoryModule(
modulePath: string,
runtime: Runtime = inferRuntimeFromExecPath(process.execPath),
): number {
return runWorkerFile(
localRepoPath('resources/benchmark/worker-memory.js'),
modulePath,
memoryCommandArgsByRuntime[runtime],
runtime,
) as number;
}
function runWorkerFile(
workerPath: string,
modulePath: string,
commandArgs: ReadonlyArray<string>,
runtime: Runtime,
): unknown {
const resultPath = fs.mkdtempSync(
path.join(os.tmpdir(), 'graphql-benchmark-worker-'),
);
const resultFilePath = path.join(resultPath, 'result.json');
const currentRuntime = inferRuntimeFromExecPath(process.execPath);
const execPath =
runtime === currentRuntime ? process.execPath : String(runtime);
const execArgs = [...commandArgs, workerPath, modulePath];
try {
const result = childProcess.spawnSync(execPath, execArgs, {
stdio: 'inherit',
env: {
...process.env,
BENCHMARK_RESULT_FILE: resultFilePath,
NODE_ENV: 'production',
},
});
if (result.error != null) {
throw result.error;
}
if (result.signal != null) {
throw new Error(
`Benchmark worker terminated by signal "${result.signal}".`,
);
}
if (result.status !== 0) {
throw new Error(
`Benchmark worker failed with "${result.status}" status.`,
);
}
const resultStr = fs.readFileSync(resultFilePath, 'utf8');
assert(resultStr !== '');
return JSON.parse(resultStr);
} finally {
fs.rmSync(resultPath, { recursive: true, force: true });
}
}
function inferRuntimeFromExecPath(execPath: string): Runtime {
const executableName = path.basename(execPath).toLowerCase();
if (executableName === 'deno' || executableName === 'deno.exe') {
return 'deno';
}
if (executableName === 'bun' || executableName === 'bun.exe') {
return 'bun';
}
return 'node';
}