Files
deerflow-code/frontend-web/scripts/benchmark-firefox52.mjs
T

123 lines
5.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { spawn } from "node:child_process";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const rounds = Math.max(1, Number(process.env.FIREFOX_BENCHMARK_ROUNDS || 5));
const cold = process.argv.includes("--cold");
const fast = process.argv.includes("--fast");
const command = fast ? "build:firefox52:fast" : "build:firefox52";
const reportDir = path.join(rootDir, ".runtime", "firefox52-benchmark");
function removeWorkspaceDirectory(relativePath) {
const target = path.resolve(rootDir, relativePath);
if (path.dirname(target) !== rootDir) throw new Error(`拒绝清理工作区之外的目录:${target}`);
fs.rmSync(target, { recursive: true, force: true });
}
function runBuild() {
return new Promise((resolve) => {
const startedAt = performance.now();
const executable = process.platform === "win32" ? (process.env.ComSpec || "cmd.exe") : "pnpm";
const args = process.platform === "win32" ? ["/d", "/s", "/c", `pnpm ${command}`] : [command];
const child = spawn(executable, args, {
cwd: rootDir,
env: process.env,
stdio: "inherit",
shell: false,
});
child.on("exit", (code, signal) => {
resolve({
durationMs: Math.round(performance.now() - startedAt),
exitCode: code,
signal,
});
});
child.on("error", (error) => resolve({ durationMs: Math.round(performance.now() - startedAt), exitCode: -1, error: String(error) }));
});
}
function percentile(values, ratio) {
if (values.length === 0) return null;
const sorted = [...values].sort((a, b) => a - b);
return sorted[Math.ceil(ratio * sorted.length) - 1];
}
function artifactStats() {
const assetsDir = path.join(rootDir, "dist-firefox52", "assets");
const stats = { jsFiles: 0, jsBytes: 0, cssFiles: 0, cssBytes: 0 };
if (!fs.existsSync(assetsDir)) return stats;
for (const entry of fs.readdirSync(assetsDir, { withFileTypes: true })) {
if (!entry.isFile()) continue;
const size = fs.statSync(path.join(assetsDir, entry.name)).size;
if (/\.js$/i.test(entry.name)) {
stats.jsFiles += 1;
stats.jsBytes += size;
} else if (/\.css$/i.test(entry.name)) {
stats.cssFiles += 1;
stats.cssBytes += size;
}
}
return stats;
}
if (cold) {
const productionCache = path.resolve(rootDir, ".rspack-cache", "firefox52-production");
if (!productionCache.startsWith(`${path.resolve(rootDir, ".rspack-cache")}${path.sep}`)) {
throw new Error(`拒绝清理预期缓存目录之外的路径:${productionCache}`);
}
fs.rmSync(productionCache, { recursive: true, force: true });
removeWorkspaceDirectory("dist-firefox52");
}
const samples = [];
for (let index = 0; index < rounds; index += 1) {
console.log(`\n[Firefox 52 benchmark] ${index + 1}/${rounds} ${command}`);
const sample = await runBuild();
samples.push(sample);
if (sample.exitCode !== 0) break;
}
const successfulDurations = samples.filter((item) => item.exitCode === 0).map((item) => item.durationMs);
const steadyDurations = successfulDurations.slice(1);
const now = new Date();
const stamp = now.toISOString().replace(/[:.]/g, "-");
const report = {
generatedAt: now.toISOString(),
command: `pnpm ${command}`,
coldCacheBeforeFirstRun: cold,
requestedRounds: rounds,
environment: {
platform: `${os.platform()} ${os.release()} ${os.arch()}`,
cpu: os.cpus()[0]?.model || "unknown",
logicalCpus: os.cpus().length,
totalMemoryBytes: os.totalmem(),
node: process.version,
pnpm: process.env.npm_config_user_agent || "run via pnpm.cmd",
},
samples,
summary: {
successfulRuns: successfulDurations.length,
medianMs: percentile(successfulDurations, 0.5),
p95Ms: percentile(successfulDurations, 0.95),
minMs: successfulDurations.length ? Math.min(...successfulDurations) : null,
maxMs: successfulDurations.length ? Math.max(...successfulDurations) : null,
steadyStateMedianMs: percentile(steadyDurations, 0.5),
steadyStateP95Ms: percentile(steadyDurations, 0.95),
},
artifacts: artifactStats(),
};
fs.mkdirSync(reportDir, { recursive: true });
const jsonPath = path.join(reportDir, `${stamp}.json`);
const markdownPath = path.join(reportDir, `${stamp}.md`);
fs.writeFileSync(jsonPath, `${JSON.stringify(report, null, 2)}\n`);
fs.writeFileSync(markdownPath, `# Firefox 52 Rspack 构建基准\n\n- 时间:${report.generatedAt}\n- 命令:\`${report.command}\`\n- 轮次:${samples.length}\n- 全部中位数:${report.summary.medianMs ?? "-"} ms\n- 全部 P95:${report.summary.p95Ms ?? "-"} ms\n- 首轮后的稳态中位数:${report.summary.steadyStateMedianMs ?? "-"} ms\n- 首轮后的稳态 P95:${report.summary.steadyStateP95Ms ?? "-"} ms\n- JS:${report.artifacts.jsFiles} 个 / ${report.artifacts.jsBytes} bytes\n- CSS:${report.artifacts.cssFiles} 个 / ${report.artifacts.cssBytes} bytes\n\n| 轮次 | 耗时(ms) | 退出码 |\n|---:|---:|---:|\n${samples.map((sample, index) => `| ${index + 1} | ${sample.durationMs} | ${sample.exitCode} |`).join("\n")}\n`);
console.log(`\nFirefox 52 benchmark median=${report.summary.medianMs}ms p95=${report.summary.p95Ms}ms steadyMedian=${report.summary.steadyStateMedianMs}ms`);
console.log(`JSON: ${jsonPath}`);
console.log(`Markdown: ${markdownPath}`);
if (successfulDurations.length !== rounds) process.exitCode = 1;