mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-05 02:43:32 +00:00
bench(cfg): deep-nest scenario + tighten dense-bindings rd budget 10->2 (#2201 U7)
dense-bindings rd_scaling drops 5.2->0.86 (SSA linear); budget tightened to 2.0. New deep-nest scenario (N nested loops, one carried var) measures rd under the production blocks×64 ceiling and asserts the SSA solver still COMPUTES full facts (facts_large_min) where the dense worklist would truncate — the ceiling-stops-firing acceptance. CFG fingerprints unchanged.
This commit is contained in:
parent
0a8dbd43d5
commit
101738113b
2 changed files with 74 additions and 10 deletions
|
|
@ -29,8 +29,16 @@
|
|||
"scaling_budget": 1.8,
|
||||
"disk_bytes_budget": 1.2,
|
||||
"heap_budget": 1.3,
|
||||
"rd_scaling_budget": 10.0,
|
||||
"_note": "#2082 M2: N bindings live across ~N blocks in one loop -- bindings x blocks scale JOINTLY (the solver-lattice stressor). The overlay design measures rd ~5.2 normalized: the OUT spine copy on genning blocks is O(V) per block, which is quadratic when V scales with B (bounded in prod by maxFunctionLines; real functions have V~10-40). Budget 10 deliberately tolerates that known shape and exists to catch the repo's recurring per-item-rescan class (a per-use scan over all defs is O(n^3) here, ratio >=16). If rd drops well below 5, tighten."
|
||||
"rd_scaling_budget": 2.0,
|
||||
"_note": "#2082 M2 / #2201 SSA: N bindings live across ~N blocks in one loop -- bindings x blocks scale JOINTLY (the solver-lattice stressor). The dense GEN/KILL worklist measured rd ~5.2 normalized here (the OUT spine copy is O(V) per block, quadratic when V scales with B). The #2201 SSA-sparse solver answers each use's reaching set from the def-use graph WITHOUT a per-block dense lattice, dropping rd to ~0.86 (linear; measured 5-23x faster absolute). Budget tightened 10->2: still absorbs noise + catches a regression to the per-item-rescan class (a per-use scan over all defs is O(n^3) here, ratio >=16), but now also catches a fall-back to the dense quadratic. Fingerprint unchanged -- CFG construction is untouched."
|
||||
},
|
||||
"deep-nest": {
|
||||
"fingerprint": "c0ca870487abc6ff379304c3162003e9e4f9b44aeb2fc29adfcf8d2179c7613a",
|
||||
"scaling_budget": 1.8,
|
||||
"disk_bytes_budget": 1.2,
|
||||
"rd_scaling_budget": 2.0,
|
||||
"facts_large_min": 100,
|
||||
"_note": "#2201: N nested loops carrying ONE variable end-to-end (depth 40->160) -- the pathology the dense worklist is superlinear on and whose block-visit total drives it past the blocks×64 ceiling (it would TRUNCATE to empty). rd is measured under the PRODUCTION blocks×64 budget (rdProductionBudget) to prove the ceiling stops firing: the depth-INDEPENDENT SSA solver (phi-nodes capture loop merges statically; no fixpoint iteration) computes the full facts (164 at large; floor 100 catches a regression to truncation/zero) with rd_scaling ~0.90 (linear in depth; measured ~0.42ms at depth 160). rd_scaling_budget 2.0 catches a regression back to superlinear. No heap_budget -- the deep-nest CFG payload is tiny and the retained-heap delta is GC-noise-dominated. Re-baseline the fingerprint only on an intentional CFG/visitor change."
|
||||
},
|
||||
"fact-fanout": {
|
||||
"fingerprint": "83a8243a8aff117f69aeecb39d02a483e6cca70439d75f63e433f4e4ac85578f",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,10 @@ import { fileURLToPath } from 'node:url';
|
|||
import Parser from 'tree-sitter';
|
||||
import { collectFunctionCfgs } from '../../src/core/ingestion/cfg/collect.ts';
|
||||
import { computeReachingDefs } from '../../src/core/ingestion/cfg/reaching-defs.ts';
|
||||
import { DEFAULT_PDG_MAX_REACHING_DEF_FACTS_PER_FUNCTION } from '../../src/core/ingestion/cfg/emit.ts';
|
||||
import {
|
||||
DEFAULT_PDG_MAX_REACHING_DEF_FACTS_PER_FUNCTION,
|
||||
DEFAULT_PDG_MAX_REACHING_DEF_BLOCK_REVISITS,
|
||||
} from '../../src/core/ingestion/cfg/emit.ts';
|
||||
import { getTreeSitterBufferSize } from '../../src/core/ingestion/constants.ts';
|
||||
import { getLanguageGrammar } from '../../src/core/tree-sitter/parser-loader.ts';
|
||||
import { getProvider } from '../../src/core/ingestion/languages/index.ts';
|
||||
|
|
@ -172,6 +175,29 @@ const SCENARIOS = [
|
|||
return s + ' c = c - 1;\n }\n return v0;\n}\n';
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'deep-nest',
|
||||
// #2201: N nested loops carrying one variable end-to-end — the pathology the
|
||||
// dense GEN/KILL worklist is superlinear on and that drives its block-visit
|
||||
// total past the blocks×64 ceiling (it would truncate to an empty result).
|
||||
// The production SSA solver is depth-INDEPENDENT (φ-nodes capture the loop
|
||||
// merges statically; no fixpoint iteration), so rd time scales ~linearly
|
||||
// with depth and the ceiling never fires. Two gates: rd_scaling_budget
|
||||
// catches a regression back to superlinear, and facts_large_min asserts the
|
||||
// solver still COMPUTES full facts under the PRODUCTION blocks×64 budget
|
||||
// (rdProductionBudget) — a dense worklist would report zero facts here.
|
||||
small: 40,
|
||||
large: 160, // 4×, well under the visitor's recursive-nesting depth guard
|
||||
rdMaxFacts: 0, // measure the algorithm, not the cap
|
||||
rdProductionBudget: true, // pass blocks×64 — the SSA solver must still compute
|
||||
gen: (n) => {
|
||||
let s = 'function f(c: number) {\n let x = 0;\n';
|
||||
for (let i = 0; i < n; i++) s += ' '.repeat(i + 1) + `while (c > ${i}) {\n`;
|
||||
s += ' '.repeat(n + 1) + 'x = x + 1;\n';
|
||||
for (let i = n - 1; i >= 0; i--) s += ' '.repeat(i + 1) + '}\n';
|
||||
return s + ' return x;\n}\n';
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'fact-fanout',
|
||||
// #2082 M2: N parallel case-arm defs of one variable + N later uses —
|
||||
|
|
@ -296,17 +322,32 @@ function measureCollect(tk, src, file, reps) {
|
|||
// the scope-resolution emit loop adds per file on a --pdg run). `maxFacts`
|
||||
// mirrors the per-scenario production posture: 0 (unlimited) measures the
|
||||
// algorithm; the production default exercises the boundedness contract.
|
||||
function measureReachingDefs(cfgs, reps, maxFacts) {
|
||||
for (const c of cfgs) computeReachingDefs(c, { maxFacts }); // warm JIT
|
||||
// When `blockVisitsMul` > 0 each call also passes the PRODUCTION per-function
|
||||
// maxBlockVisits budget (blocks × mul). On the deep-nest scenario this is how
|
||||
// "the ceiling stops firing" (#2201) is measured: the dense worklist would
|
||||
// truncate to an empty result under this budget, whereas the production SSA
|
||||
// solver computes the full facts — so a nonzero `facts` under the budget is the
|
||||
// gate (see facts_large_min in baselines.json).
|
||||
function measureReachingDefs(cfgs, reps, maxFacts, blockVisitsMul = 0) {
|
||||
const limitsFor = (c) =>
|
||||
blockVisitsMul > 0
|
||||
? { maxFacts, maxBlockVisits: c.blocks.length * blockVisitsMul }
|
||||
: { maxFacts };
|
||||
for (const c of cfgs) computeReachingDefs(c, limitsFor(c)); // warm JIT
|
||||
const samples = [];
|
||||
let facts = 0;
|
||||
let allComputed = true;
|
||||
for (let i = 0; i < reps; i++) {
|
||||
const start = process.hrtime.bigint();
|
||||
facts = 0;
|
||||
for (const c of cfgs) facts += computeReachingDefs(c, { maxFacts }).facts.length;
|
||||
for (const c of cfgs) {
|
||||
const r = computeReachingDefs(c, limitsFor(c));
|
||||
facts += r.facts.length;
|
||||
if (r.status !== 'computed') allComputed = false;
|
||||
}
|
||||
samples.push(Number(process.hrtime.bigint() - start) / 1e6);
|
||||
}
|
||||
return { ms: median(samples), facts };
|
||||
return { ms: median(samples), facts, allComputed };
|
||||
}
|
||||
|
||||
// ---- taint pass cost (#2083 M3 U7) ----
|
||||
|
|
@ -441,10 +482,14 @@ function measureScenario(scenario) {
|
|||
? heapLarge / heapSmall / sizeRatio
|
||||
: null;
|
||||
|
||||
// #2082 M2: reaching-defs solve cost over the same CFGs.
|
||||
// #2082 M2: reaching-defs solve cost over the same CFGs. #2201: scenarios
|
||||
// marked `rdProductionBudget` also pass the per-function blocks×64 ceiling, to
|
||||
// prove the production SSA solver still COMPUTES where the dense worklist would
|
||||
// truncate (the deep-nest ceiling-stops-firing acceptance).
|
||||
const rdMaxFacts = scenario.rdMaxFacts ?? 0;
|
||||
const rdSmall = measureReachingDefs(small.cfgs, REPS, rdMaxFacts);
|
||||
const rdLarge = measureReachingDefs(large.cfgs, REPS, rdMaxFacts);
|
||||
const rdBudgetMul = scenario.rdProductionBudget ? DEFAULT_PDG_MAX_REACHING_DEF_BLOCK_REVISITS : 0;
|
||||
const rdSmall = measureReachingDefs(small.cfgs, REPS, rdMaxFacts, rdBudgetMul);
|
||||
const rdLarge = measureReachingDefs(large.cfgs, REPS, rdMaxFacts, rdBudgetMul);
|
||||
// Clamp the denominator: a 0.000ms small-N median would otherwise yield
|
||||
// ratio 0 and the gate would self-disable exactly when the solver is fast.
|
||||
const rdRatio = rdLarge.ms / Math.max(rdSmall.ms, 0.001) / sizeRatio;
|
||||
|
|
@ -506,6 +551,7 @@ function measureScenario(scenario) {
|
|||
rd_scaling_ratio: Number(rdRatio.toFixed(3)),
|
||||
facts_small: rdSmall.facts,
|
||||
facts_large: rdLarge.facts,
|
||||
rd_all_computed: rdLarge.allComputed,
|
||||
...fingerprint(tk, scenario),
|
||||
};
|
||||
}
|
||||
|
|
@ -570,6 +616,16 @@ if (!CHECK) {
|
|||
`(the maxFacts early-stop is the boundedness contract)`,
|
||||
);
|
||||
}
|
||||
// #2201 deep-nest: under the PRODUCTION blocks×64 budget the SSA solver must
|
||||
// still COMPUTE full facts (a nonzero floor) where the dense worklist would
|
||||
// truncate to empty — "the ceiling stops firing".
|
||||
if (base.facts_large_min !== undefined && r.facts_large < base.facts_large_min) {
|
||||
failures.push(
|
||||
`${r.scenario}: only ${r.facts_large} facts < floor ${base.facts_large_min} under the ` +
|
||||
`production block-visit budget — the ceiling fired (SSA should not truncate here)` +
|
||||
(r.rd_all_computed ? '' : ` [status != computed]`),
|
||||
);
|
||||
}
|
||||
if (base.disk_bytes_large_max !== undefined && r.disk_bytes_large > base.disk_bytes_large_max) {
|
||||
failures.push(
|
||||
`${r.scenario}: cfgSideChannel absolute size ${r.disk_bytes_large} > ceiling ` +
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue