mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-03 02:21:44 +00:00
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
* Initial plan * fix(scope-resolution): gate C# and Kotlin free calls * fix(scope-resolution): keep Kotlin ownership gate safe * Apply remaining changes * perf(scope-resolution): benchmark and cache ownership gates * test(scope-resolution): simplify benchmark scaling loop * refactor(scope-resolution): encapsulate ownership cache * test(scope-resolution): enforce subquadratic ownership scaling * fix(scope-resolution): address ownership review findings * test(csharp): regenerate capture golden for #2563 fixtures The committed expected-captures.json was missing the new NamespaceOwnerCollision.cs entry and carried a stale SameFileCases.cs digest/count (56 → 67), so csharp-captures-golden.test.ts was the sole red check on the PR. Regenerate with UPDATE_GOLDEN=1 to match the fixtures the bench fingerprint already reflects. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
129 lines
3.8 KiB
TypeScript
129 lines
3.8 KiB
TypeScript
/**
|
|
* Instance-ownership free-call gate benchmark.
|
|
*
|
|
* Generates C# and Kotlin projects where every file contains a caller and an
|
|
* unrelated class method with the same receiver-less call name. Repeated calls
|
|
* stress the ownership gate that prevents the same-file fallback from linking
|
|
* those unrelated methods.
|
|
*
|
|
* Run:
|
|
* cd gitnexus && GITNEXUS_BENCH=1 npx vitest run test/integration/instance-ownership-pipeline-benchmark.test.ts
|
|
*/
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import { describe, expect, it } from 'vitest';
|
|
import { runPipelineFromRepo } from '../../src/core/ingestion/pipeline.js';
|
|
|
|
const BENCH_ENABLED = process.env.GITNEXUS_BENCH === '1';
|
|
const CALLS_PER_FILE = 24;
|
|
// time growth divided by file growth: quadratic work reaches 2 on a doubling.
|
|
const NORMALIZED_SCALING_LIMIT = 2;
|
|
|
|
interface BenchResult {
|
|
fileCount: number;
|
|
callCount: number;
|
|
elapsedMs: number;
|
|
peakHeapMB: number;
|
|
}
|
|
|
|
interface LanguageCase {
|
|
readonly label: string;
|
|
readonly extension: string;
|
|
source(fileIndex: number): string;
|
|
}
|
|
|
|
const LANGUAGES: readonly LanguageCase[] = [
|
|
{
|
|
label: 'C#',
|
|
extension: 'cs',
|
|
source: (fileIndex) => `namespace Bench${fileIndex};
|
|
|
|
public class Caller${fileIndex}
|
|
{
|
|
${Array.from(
|
|
{ length: CALLS_PER_FILE },
|
|
(_, callIndex) => ` public void Run${callIndex}() { Foreign(); }`,
|
|
).join('\n')}
|
|
}
|
|
|
|
public class Unrelated${fileIndex}
|
|
{
|
|
public void Foreign() {}
|
|
}
|
|
`,
|
|
},
|
|
{
|
|
label: 'Kotlin',
|
|
extension: 'kt',
|
|
source: (fileIndex) => `package bench${fileIndex}
|
|
|
|
class Caller${fileIndex} {
|
|
${Array.from(
|
|
{ length: CALLS_PER_FILE },
|
|
(_, callIndex) => ` fun run${callIndex}() { foreign() }`,
|
|
).join('\n')}
|
|
}
|
|
|
|
class Unrelated${fileIndex} {
|
|
fun foreign() {}
|
|
}
|
|
`,
|
|
},
|
|
];
|
|
|
|
function generateFixture(language: LanguageCase, fileCount: number): string {
|
|
const dir = fs.mkdtempSync(
|
|
path.join(os.tmpdir(), `instance-ownership-${language.extension}-${fileCount}-`),
|
|
);
|
|
for (let i = 0; i < fileCount; i++) {
|
|
fs.writeFileSync(path.join(dir, `Case${i}.${language.extension}`), language.source(i));
|
|
}
|
|
return dir;
|
|
}
|
|
|
|
async function runBenchmark(language: LanguageCase, fileCount: number): Promise<BenchResult> {
|
|
const dir = generateFixture(language, fileCount);
|
|
let peakHeapMB = 0;
|
|
const heapSampler = setInterval(() => {
|
|
peakHeapMB = Math.max(peakHeapMB, process.memoryUsage().heapUsed / 1024 / 1024);
|
|
}, 25);
|
|
|
|
try {
|
|
const start = performance.now();
|
|
await runPipelineFromRepo(dir, () => {}, { skipGraphPhases: true });
|
|
return {
|
|
fileCount,
|
|
callCount: fileCount * CALLS_PER_FILE,
|
|
elapsedMs: Math.round(performance.now() - start),
|
|
peakHeapMB: Math.round(peakHeapMB),
|
|
};
|
|
} finally {
|
|
clearInterval(heapSampler);
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
describe.skipIf(!BENCH_ENABLED)('instance-ownership free-call gate benchmark', () => {
|
|
for (const language of LANGUAGES) {
|
|
it(`${language.label} scales sub-quadratically with ownership-gated calls`, async () => {
|
|
// Wide enough steps to expose quadratic growth without making this
|
|
// opt-in benchmark impractical on contributor machines.
|
|
let previous: BenchResult | undefined;
|
|
for (const fileCount of [100, 250, 500]) {
|
|
const result = await runBenchmark(language, fileCount);
|
|
console.log(
|
|
`${language.label}: ${result.fileCount} files / ${result.callCount} calls: ` +
|
|
`${result.elapsedMs}ms, ${result.peakHeapMB}MB heap`,
|
|
);
|
|
|
|
if (previous !== undefined) {
|
|
const fileRatio = result.fileCount / previous.fileCount;
|
|
const timeRatio = result.elapsedMs / previous.elapsedMs;
|
|
expect(timeRatio / fileRatio).toBeLessThan(NORMALIZED_SCALING_LIMIT);
|
|
}
|
|
previous = result;
|
|
}
|
|
}, 600_000);
|
|
}
|
|
});
|