GitNexus/gitnexus/bench/zig-cross-file-resolution/measure.mjs
Parafee41 1c1cbf111e
Some checks failed
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Gitleaks / gitleaks (push) Has been cancelled
Publish / Classify release event (push) Has been cancelled
Scorecard / Scorecard analysis (push) Has been cancelled
Trivy Image Scan / Trivy (gitnexus-cli) (push) Has been cancelled
Trivy Image Scan / Trivy (gitnexus-web) (push) Has been cancelled
Publish / Build & Push RC Docker images (push) Has been cancelled
Publish / RC guard (marker + release-PR skip) (push) Has been cancelled
Publish / ci (push) Has been cancelled
Publish / Publish to npm (push) Has been cancelled
fix(zig): resolve cross-file static gates (#3185)
* fix(zig): resolve cross-file static gates

* Fix Zig workspace import alias enrichment

* Handle extensionless Zig workspace imports

* Reuse Zig import resolution for static gates

* Document Zig workspace static gating

* Harden Zig workspace reference enrichment

* Benchmark Zig cross-file static gating

* Enforce linear Zig benchmark scaling

---------

Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
2026-09-07 07:19:30 +01:00

138 lines
4.7 KiB
JavaScript

#!/usr/bin/env node
/**
* Build-free scaling and correctness guard for Zig cross-file static gates.
*
* The workspace pass parses every indexed Zig file, resolves direct @import
* aliases, then applies sibling boolean constants to call sites. This bench
* makes both relevant axes explicit: file count and calls per importer. It
* also fingerprints the number of calls classified dead, so a fast no-op
* implementation cannot pass the timing gate.
*
* Usage:
* node --import tsx bench/zig-cross-file-resolution/measure.mjs
* node --import tsx bench/zig-cross-file-resolution/measure.mjs --check
*/
import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { performance } from 'node:perf_hooks';
import { populateZigWorkspaceStaticGating } from '../../src/core/ingestion/languages/zig/workspace-static-gating.ts';
const HERE = dirname(fileURLToPath(import.meta.url));
const SMALL_MODULES = 40;
const LARGE_MODULES = 160;
const CALLS_PER_MODULE = 12;
const REPS = 7;
const range = (line, col) => ({ startLine: line, startCol: col, endLine: line, endCol: col + 4 });
function corpus(modules) {
const parsedFiles = [];
const fileContents = new Map();
for (let i = 0; i < modules; i++) {
const cfgPath = `bench/cfg${i}.zig`;
const appPath = `bench/app${i}.zig`;
fileContents.set(cfgPath, 'pub const ENABLED = false;\n');
fileContents.set(
appPath,
`const cfg = @import(\"./cfg${i}.zig\");\n` +
Array.from(
{ length: CALLS_PER_MODULE },
(_, j) => `pub fn run${j}() void { if (cfg.ENABLED) dead${j}(); }`,
).join('\n'),
);
parsedFiles.push(Object.freeze({ filePath: cfgPath, referenceSites: Object.freeze([]) }));
parsedFiles.push(
Object.freeze({
filePath: appPath,
referenceSites: Object.freeze(
Array.from({ length: CALLS_PER_MODULE }, (_, j) => ({
kind: 'call',
name: `dead${j}`,
atRange: range(j + 2, 44),
})),
),
}),
);
}
return { parsedFiles, fileContents };
}
function run(modules) {
const { parsedFiles, fileContents } = corpus(modules);
populateZigWorkspaceStaticGating(parsedFiles, { fileContents });
let gatedCalls = 0;
for (const file of parsedFiles) {
for (const site of file.referenceSites) if (site.staticGated === true) gatedCalls++;
}
return gatedCalls;
}
function measure(modules) {
run(modules);
let bestMs = Infinity;
let gatedCalls = 0;
for (let i = 0; i < REPS; i++) {
const start = performance.now();
gatedCalls = run(modules);
bestMs = Math.min(bestMs, performance.now() - start);
}
return {
modules,
calls_per_module: CALLS_PER_MODULE,
gated_calls: gatedCalls,
min_ms: Number(bestMs.toFixed(2)),
};
}
const report = { small: measure(SMALL_MODULES), large: measure(LARGE_MODULES) };
report.workload_ratio = LARGE_MODULES / SMALL_MODULES;
report.scaling_ratio = Number(
(report.large.min_ms / Math.max(report.small.min_ms, 0.01)).toFixed(3),
);
report.linear_factor = Number((report.scaling_ratio / report.workload_ratio).toFixed(3));
if (!process.argv.includes('--check')) {
console.log(JSON.stringify(report, null, 2));
process.exit(0);
}
const baseline = JSON.parse(readFileSync(join(HERE, 'baseline.json'), 'utf8'));
const failures = [];
const requirePositiveNumber = (path, value) => {
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {
failures.push(`${path}: expected a finite positive number, got ${JSON.stringify(value)}`);
return false;
}
return true;
};
for (const arm of ['small', 'large']) {
for (const key of ['modules', 'calls_per_module', 'gated_calls']) {
if (report[arm][key] !== baseline[arm][key]) {
failures.push(`${arm}.${key}: expected ${baseline[arm][key]}, got ${report[arm][key]}`);
}
}
if (
requirePositiveNumber(`${arm}.ms_budget`, baseline[arm].ms_budget) &&
report[arm].min_ms > baseline[arm].ms_budget
) {
failures.push(`${arm}.min_ms ${report[arm].min_ms} exceeds budget ${baseline[arm].ms_budget}`);
}
}
if (
requirePositiveNumber('linear_scaling_slack', baseline.linear_scaling_slack) &&
report.linear_factor > baseline.linear_scaling_slack
) {
failures.push(
`linear_factor ${report.linear_factor} exceeds slack ${baseline.linear_scaling_slack} ` +
`(runtime ${report.scaling_ratio}x for ${report.workload_ratio}x work)`,
);
}
console.log(JSON.stringify(report, null, 2));
if (failures.length > 0) {
console.error('[zig-cross-file-resolution --check] FAIL');
for (const failure of failures) console.error(` - ${failure}`);
process.exit(1);
}
console.log('[zig-cross-file-resolution --check] PASS');