Files
wehub-resource-sync e7738de6d2
CI / Deep Native Runtime Cases (1/6) (push) Has been skipped
CI / Native Preflight (push) Failing after 1s
CI / Native Runtime Cases (1/2) (push) Failing after 0s
CI / Native Runtime Cases (2/2) (push) Failing after 1s
CI / Native Metadata Reports (push) Failing after 0s
CI / Native Direct Backend Artifacts (push) Failing after 0s
CI / Native Sanitizer Smoke (push) Failing after 1s
CI / Command Contract Snapshots (push) Failing after 1s
CI / Deep Conformance Suite (push) Has been skipped
CI / Graph Build Perf (push) Failing after 1s
CI / Deep Native Preflight (push) Has been skipped
CI / Deep Native Runtime Cases (2/6) (push) Has been skipped
CI / Deep Native Runtime Cases (3/6) (push) Has been skipped
CI / Conformance Suite (push) Failing after 1s
CI / Workspace Checks (push) Failing after 0s
CI / Deep Native Runtime Cases (5/6) (push) Has been skipped
CI / Deep Native Runtime Cases (6/6) (push) Has been skipped
CI / Deep Native Runtime Cases (4/6) (push) Has been skipped
CI / Deep Graph Build Perf (push) Has been skipped
chore: import upstream snapshot with attribution
2026-07-13 12:29:30 +08:00

429 lines
14 KiB
TypeScript

#!/usr/bin/env -S node --experimental-strip-types --disable-warning=ExperimentalWarning
import { execFile } from "node:child_process";
import { mkdir, rm } from "node:fs/promises";
import { join } from "node:path";
import { performance } from "node:perf_hooks";
import { promisify } from "node:util";
const execFileAsync = promisify(execFile);
const zero = process.env.ZERO_BIN || "bin/zero";
const target = process.env.ZERO_GRAPH_BUILD_PERF_TARGET || "linux-musl-x64";
const outRoot = process.env.ZERO_GRAPH_BUILD_PERF_DIR || `/tmp/zero-graph-build-perf-${process.pid}`;
const maxBuffer = 128 * 1024 * 1024;
type Fixture = {
name: string;
input: string;
target?: string;
checkTarget?: string;
};
type CommandRun = {
ok: boolean;
command: string[];
wallMs: number;
body: any;
stderr: string;
};
type PerformanceBudget = {
name: string;
fixture: string;
pass: "cold" | "warm";
command: "build" | "check";
metric: string;
max: number;
unit: "ms" | "bytes";
};
const fixtures: Fixture[] = [
{ name: "hello-artifact", input: "examples/hello.graph" },
{ name: "stdlib-heavy-artifact", input: "conformance/native/pass/stdlib-target-neutral.graph" },
{ name: "crm-api-package", input: "examples/crm-api" },
];
const budgetMode = process.env.ZERO_GRAPH_BUILD_PERF_BUDGET || "warn";
function envNumber(name: string, fallback: number) {
const raw = process.env[name];
if (!raw) return fallback;
const parsed = Number(raw);
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
}
const performanceBudgets: PerformanceBudget[] = [
{
name: "simple graph cold build",
fixture: "hello-artifact",
pass: "cold",
command: "build",
metric: "compilerElapsedMs",
max: envNumber("ZERO_GRAPH_BUILD_PERF_SIMPLE_COLD_MAX_MS", 5000),
unit: "ms",
},
{
name: "simple graph warm build",
fixture: "hello-artifact",
pass: "warm",
command: "build",
metric: "compilerElapsedMs",
max: envNumber("ZERO_GRAPH_BUILD_PERF_SIMPLE_WARM_MAX_MS", 3000),
unit: "ms",
},
{
name: "simple graph artifact size",
fixture: "hello-artifact",
pass: "warm",
command: "build",
metric: "artifactBytes",
max: envNumber("ZERO_GRAPH_BUILD_PERF_SIMPLE_ARTIFACT_MAX_BYTES", 8192),
unit: "bytes",
},
{
name: "simple graph lowered IR size",
fixture: "hello-artifact",
pass: "warm",
command: "build",
metric: "loweredIrBytes",
max: envNumber("ZERO_GRAPH_BUILD_PERF_SIMPLE_LOWERED_IR_MAX_BYTES", 16384),
unit: "bytes",
},
{
name: "stdlib-heavy cold build",
fixture: "stdlib-heavy-artifact",
pass: "cold",
command: "build",
metric: "compilerElapsedMs",
max: envNumber("ZERO_GRAPH_BUILD_PERF_STDLIB_HEAVY_COLD_MAX_MS", 120000),
unit: "ms",
},
{
name: "stdlib-heavy warm build",
fixture: "stdlib-heavy-artifact",
pass: "warm",
command: "build",
metric: "compilerElapsedMs",
max: envNumber("ZERO_GRAPH_BUILD_PERF_STDLIB_HEAVY_WARM_MAX_MS", 120000),
unit: "ms",
},
{
name: "stdlib-heavy warm graph load",
fixture: "stdlib-heavy-artifact",
pass: "warm",
command: "build",
metric: "timings.graphLoadMs",
max: envNumber("ZERO_GRAPH_BUILD_PERF_STDLIB_HEAVY_GRAPH_LOAD_MAX_MS", 30000),
unit: "ms",
},
{
name: "stdlib-heavy warm stdlib merge",
fixture: "stdlib-heavy-artifact",
pass: "warm",
command: "build",
metric: "timings.stdlibMergeMs",
max: envNumber("ZERO_GRAPH_BUILD_PERF_STDLIB_HEAVY_STDLIB_MERGE_MAX_MS", 70000),
unit: "ms",
},
{
name: "stdlib-heavy artifact size",
fixture: "stdlib-heavy-artifact",
pass: "warm",
command: "build",
metric: "artifactBytes",
max: envNumber("ZERO_GRAPH_BUILD_PERF_STDLIB_HEAVY_ARTIFACT_MAX_BYTES", 262144),
unit: "bytes",
},
{
name: "stdlib-heavy lowered IR size",
fixture: "stdlib-heavy-artifact",
pass: "warm",
command: "build",
metric: "loweredIrBytes",
max: envNumber("ZERO_GRAPH_BUILD_PERF_STDLIB_HEAVY_LOWERED_IR_MAX_BYTES", 4194304),
unit: "bytes",
},
];
function selectedFixtures() {
const raw = process.env.ZERO_GRAPH_BUILD_PERF_CASES;
if (!raw) return fixtures;
const wanted = new Set(raw.split(",").map((part) => part.trim()).filter(Boolean));
return fixtures.filter((fixture) => wanted.has(fixture.name) || wanted.has(fixture.input));
}
function phaseMs(body: any, name: string) {
const phase = Array.isArray(body?.compilerPhases)
? body.compilerPhases.find((item: any) => item?.name === name)
: null;
return typeof phase?.elapsedMs === "number" ? phase.elapsedMs : null;
}
function graphBuildTimings(body: any) {
const timings = body?.graphBuild?.timings || {};
return {
graphLoadMs: timings.graphLoadMs ?? null,
stdlibMergeMs: timings.stdlibMergeMs ?? null,
stdlibReferenceScanMs: timings.stdlibReferenceScanMs ?? null,
stdlibCleanupMs: timings.stdlibCleanupMs ?? null,
stdlibModuleLoadMs: timings.stdlibModuleLoadMs ?? null,
stdlibNodeMergeMs: timings.stdlibNodeMergeMs ?? null,
stdlibEdgeMergeMs: timings.stdlibEdgeMergeMs ?? null,
stdlibFinalizeMs: timings.stdlibFinalizeMs ?? null,
readinessCheckMs: timings.readinessCheckMs ?? null,
mirCacheLoadMs: timings.mirCacheLoadMs ?? null,
mirLowerMs: timings.mirLowerMs ?? null,
mirCacheWriteMs: timings.mirCacheWriteMs ?? null,
mirCacheReloadMs: timings.mirCacheReloadMs ?? null,
mirLoadOrLowerMs: timings.mirLoadOrLowerMs ?? null,
lowerPhaseMs: timings.lowerPhaseMs ?? phaseMs(body, "lower"),
codegenMs: timings.codegenMs ?? phaseMs(body, "codegen"),
objectMs: timings.objectMs ?? phaseMs(body, "object"),
linkMs: timings.linkMs ?? phaseMs(body, "link"),
};
}
function graphBuildStdlibMergeFacts(body: any) {
const merge = body?.graphBuild?.stdlibMerge || {};
return {
modulesMerged: merge.modulesMerged ?? null,
nodesMerged: merge.nodesMerged ?? null,
edgesMerged: merge.edgesMerged ?? null,
cacheHit: merge.cacheHit ?? null,
cacheStored: merge.cacheStored ?? null,
};
}
function graphCheckTimings(body: any) {
const timings = body?.graphCompiler?.timings || {};
const resolveMs = timings.resolveMs ?? phaseMs(body, "resolve");
const checkMs = timings.checkMs ?? phaseMs(body, "check");
const lowerMs = timings.lowerMs ?? phaseMs(body, "lower");
return {
graphLoadMs: timings.loadMs ?? null,
resolveMs: resolveMs ?? null,
checkMs: checkMs ?? null,
readinessCheckMs: typeof resolveMs === "number" && typeof checkMs === "number" ? resolveMs + checkMs : null,
readinessLowerMs: lowerMs ?? null,
cacheMs: timings.cacheMs ?? null,
};
}
function cacheFacts(body: any) {
const caches = Array.isArray(body?.compilerCaches) ? body.compilerCaches : [];
const mapped = caches.find((cache: any) => cache?.name === "mappedFinalMir") || body?.graphBuild?.mappedFinalMir || null;
return {
hits: caches.filter((cache: any) => cache?.hit === true).length,
misses: caches.filter((cache: any) => cache?.hit === false).length,
mappedFinalMir: mapped ? {
hit: mapped.hit ?? null,
written: mapped.written ?? null,
byteLength: mapped.byteLength ?? null,
memoryMapped: mapped.memoryMapped ?? null,
codegenImmediate: mapped.codegenImmediate ?? null,
programReconstructed: mapped.programReconstructed ?? null,
} : null,
};
}
function valueAtPath(value: any, path: string) {
return path.split(".").reduce((current, part) => current?.[part], value);
}
function evaluateBudgets(results: any[]) {
const mode = budgetMode === "fail" || budgetMode === "warn" || budgetMode === "off" ? budgetMode : "warn";
if (mode === "off") {
return { schemaVersion: 1, mode, status: "off", ok: true, overCount: 0, skippedCount: 0, items: [] };
}
const items = performanceBudgets.map((budget) => {
const fixture = results.find((item) => item.name === budget.fixture);
const run = fixture?.[budget.pass]?.[budget.command];
const actual = valueAtPath(run, budget.metric);
if (!fixture) {
return { ...budget, status: "skipped", actual: null, reason: "fixture not selected" };
}
if (typeof actual !== "number") {
return { ...budget, status: "skipped", actual: null, reason: "metric missing" };
}
const overBy = Number(Math.max(0, actual - budget.max).toFixed(3));
const unitSuffix = budget.unit === "ms" ? "Ms" : "Bytes";
return {
...budget,
status: actual <= budget.max ? "ok" : "over",
actual,
max: budget.max,
[`actual${unitSuffix}`]: actual,
[`max${unitSuffix}`]: budget.max,
[`overBy${unitSuffix}`]: overBy,
ratio: Number((actual / budget.max).toFixed(3)),
};
});
const overCount = items.filter((item) => item.status === "over").length;
const skippedCount = items.filter((item) => item.status === "skipped").length;
return {
schemaVersion: 1,
mode,
status: overCount === 0 ? "ok" : mode === "fail" ? "fail" : "warn",
ok: overCount === 0,
overCount,
skippedCount,
items,
};
}
function writeBudgetMessages(budget: any) {
if (!budget || budget.mode === "off") return;
for (const item of budget.items || []) {
if (item.status === "over") {
process.stderr.write(
`graph perf budget ${budget.status}: ${item.name} ${item.actual} ${item.unit} > ${item.max} ${item.unit} (${item.metric})\n`,
);
}
}
}
async function runJson(args: string[], env: Record<string, string | undefined>): Promise<CommandRun> {
const started = performance.now();
try {
const result = await execFileAsync(zero, args, {
env: { ...process.env, ...env },
maxBuffer,
});
const wallMs = Number((performance.now() - started).toFixed(3));
return { ok: true, command: [zero, ...args], wallMs, body: JSON.parse(result.stdout), stderr: result.stderr };
} catch (error: any) {
const wallMs = Number((performance.now() - started).toFixed(3));
let body: any = null;
try {
body = JSON.parse(error.stdout?.toString() ?? "");
} catch {
body = null;
}
return {
ok: false,
command: [zero, ...args],
wallMs,
body,
stderr: error.stderr?.toString() ?? error.message ?? "",
};
}
}
async function buildCompiler() {
if (process.env.ZERO_GRAPH_BUILD_PERF_SKIP_BUILD === "1") return null;
const started = performance.now();
await execFileAsync("make", ["-C", "native/zero-c"], { maxBuffer });
return Number((performance.now() - started).toFixed(3));
}
async function zeroVersion() {
const result = await runJson(["--version", "--json"], {});
if (!result.ok) throw new Error(`zero version failed: ${result.stderr}`);
return result.body;
}
function requireOk(run: CommandRun) {
if (run.ok && run.body?.ok !== false) return;
const diagnostic = run.body?.diagnostics?.[0];
const message = diagnostic ? `${diagnostic.code}: ${diagnostic.message}` : run.stderr || "command failed";
throw new Error(`${run.command.join(" ")} failed: ${message}`);
}
function normalizeBuild(run: CommandRun) {
requireOk(run);
return {
ok: true,
wallMs: run.wallMs,
compilerElapsedMs: run.body?.elapsedMs ?? null,
graphHash: run.body?.graph?.graphHash ?? null,
lowering: run.body?.graph?.lowering ?? null,
artifactBytes: run.body?.artifactBytes ?? null,
loweredIrBytes: run.body?.loweredIrBytes ?? null,
timings: graphBuildTimings(run.body),
stdlibMerge: graphBuildStdlibMergeFacts(run.body),
caches: cacheFacts(run.body),
};
}
function normalizeCheck(run: CommandRun) {
requireOk(run);
return {
ok: true,
wallMs: run.wallMs,
graphHash: run.body?.graph?.graphHash ?? run.body?.graphHash ?? null,
targetReady: run.body?.targetReadiness?.ok ?? null,
timings: graphCheckTimings(run.body),
caches: cacheFacts(run.body),
};
}
async function runFixture(fixture: Fixture) {
const fixtureTarget = fixture.target || target;
const checkTarget = fixture.checkTarget || process.env.ZERO_GRAPH_BUILD_PERF_CHECK_TARGET || "";
const fixtureRoot = join(outRoot, fixture.name);
const buildCache = join(fixtureRoot, "build-cache");
const checkCache = join(fixtureRoot, "check-cache");
await mkdir(fixtureRoot, { recursive: true });
async function runPass(pass: "cold" | "warm") {
const artifactPath = join(fixtureRoot, `${fixture.name}-${pass}.o`);
process.stderr.write(`graph perf ${fixture.name} ${pass} build\n`);
const build = await runJson(["build", "--json", "--emit", "obj", "--target", fixtureTarget, fixture.input, "--out", artifactPath], {
ZERO_CACHE_DIR: buildCache,
});
process.stderr.write(`graph perf ${fixture.name} ${pass} check\n`);
const checkArgs = ["check", "--json"];
if (checkTarget) checkArgs.push("--target", checkTarget);
checkArgs.push(fixture.input);
const check = await runJson(checkArgs, {
ZERO_CACHE_DIR: checkCache,
});
return {
build: normalizeBuild(build),
check: normalizeCheck(check),
};
}
return {
name: fixture.name,
input: fixture.input,
target: fixtureTarget,
checkTarget: checkTarget || "host-default",
cold: await runPass("cold"),
warm: await runPass("warm"),
};
}
async function main() {
await rm(outRoot, { recursive: true, force: true });
await mkdir(outRoot, { recursive: true });
const nativeBuildMs = await buildCompiler();
const version = await zeroVersion();
const cases = selectedFixtures();
if (cases.length === 0) throw new Error("ZERO_GRAPH_BUILD_PERF_CASES selected no fixtures");
const started = performance.now();
const results = [];
for (const fixture of cases) results.push(await runFixture(fixture));
const elapsedMs = Number((performance.now() - started).toFixed(3));
const budgets = evaluateBudgets(results);
const report = {
schemaVersion: 1,
kind: "zero-graph-build-perf",
generatedAt: new Date().toISOString(),
zero: version,
target,
outDir: outRoot,
nativeBuildMs,
elapsedMs,
budgets,
cases: results,
};
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
writeBudgetMessages(budgets);
if (budgets.status === "fail") process.exit(1);
}
main().catch((error) => {
process.stderr.write(`${error instanceof Error ? error.stack || error.message : String(error)}\n`);
process.exit(1);
});