perf(push): find the Python-side scale walls all at once, and remove them

The loop the maintainer called out: four scale defects in the PHP transfer
replay, found one per two-hour Joern run, all Python bookkeeping between
engine calls. benchmarks/push/python_overhead.py ends that: the real
replay() on the real PMPro pins, with a JoernBackend whose graphs are stubs
and whose queries answer instantly with rows recorded from the real engine.
Engine time is zero, so everything measured is ours, in about a minute,
under the sampling profiler. Its first pass named two more walls:

- Region discovery re-parsed the ruleset for every region: 89,127 TOML
  parses, 275 s of a 339 s discovery, twice per push. The fact, obligation
  and taxonomy loaders now keep the parsed (immutable) result keyed by each
  file's name, size and mtime -- an edit in place or an added file reloads.
  Discovery 339 s -> 7 s; output digest identical on PMPro.
- affected_context decoded every span's and every line anchor's path for
  every context row -- a changed file has an anchor per unchanged line --
  78% of a stubbed push. Decoded once per call, anchors indexed by line and
  bisected, original order kept. A randomised control compares it against
  the row-by-row matching over 200 contexts (>100 attachments).

Result: the whole stubbed PMPro push, 27,542 answered requests and 2,688
outcomes, runs in 58.5 s of Python. What is left is the engine.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
Marius Ciepluch
2026-09-23 19:36:32 +02:00
co-authored by Claude Opus 5.5
parent c629fea89a
commit 6b85a60ff1
7 changed files with 338 additions and 15 deletions
+33
View File
@@ -0,0 +1,33 @@
"""The parsed ruleset is shared across calls, and never outlives a change to the files it came from.
Region discovery asked for the facts once per region and re-parsed the TOML every time: 89,127 parses and
275 s of a 339 s discovery on a WordPress plugin, twice per push.
"""
import shutil
from openultrasast.semantic.facts import DEFAULT_FACTS_DIR, load_facts
from openultrasast.semantic.obligations.facts import DEFAULT_OBLIGATION_FACTS_DIR, load_obligation_facts
def test_facts_are_parsed_once_and_reread_when_a_file_changes(tmp_path):
root = tmp_path / "semantic"
shutil.copytree(DEFAULT_FACTS_DIR, root)
first = load_facts(root)
assert load_facts(root) is first, "an unchanged ruleset must not be parsed again"
python = root / "python.toml"
python.write_text(python.read_text() + '\n[[sink]]\nid = "memo-probe"\ncwe = "CWE-78"\ncalls = ["memo_probe_call"]\n')
edited = load_facts(root)
assert edited is not first and any("memo_probe_call" in sink.calls for sink in edited.sinks), "an edit in place was not seen"
(root / "zz_added.toml").write_text('language = "python"\n[[sink]]\nid = "added"\ncwe = "CWE-78"\ncalls = ["added_call"]\n')
added = load_facts(root)
assert any("added_call" in sink.calls for sink in added.sinks), "a new fact file was not seen"
def test_obligation_facts_are_parsed_once_and_reread_when_a_file_changes(tmp_path):
root = tmp_path / "obligations"
shutil.copytree(DEFAULT_OBLIGATION_FACTS_DIR, root)
first = load_obligation_facts(root)
assert load_obligation_facts(root) is first
(root / "python.toml").write_text((root / "python.toml").read_text().replace('version = "', 'version = "memo-', 1))
assert load_obligation_facts(root) is not first
+65
View File
@@ -472,3 +472,68 @@ def test_attaching_relationships_is_linear_in_what_it_attaches(tmp_path, monkeyp
enriched, _ = affected_context(context(), questions, rows, tmp_path)
assert len(enriched.relationships) == 4000
assert len(compared) < 4000, f"{len(compared)} comparisons for 4,000 relationships"
def _reference_evidence(context_value, path_text, start, end):
"""The pre-index matching, verbatim in effect: every span and anchor decoded and tested per row."""
renamed = {context_value.decode_path(r.base_path): context_value.decode_path(r.head_path) for r in context_value.renames}
evidence = []
for span in context_value.spans:
old_path = context_value.decode_path(span.path)
match_path = renamed.get(old_path, old_path) if span.side == "base" else old_path
if match_path != path_text:
continue
if span.side == "base" or (span.start_line <= end and span.end_line >= start):
evidence.append(f"{span.side}_span:{old_path}:{span.start_line}-{span.end_line}")
for anchor in context_value.line_correspondences:
if context_value.decode_path(anchor.head_path) == path_text and start <= anchor.head_start_line <= end:
evidence.append(f"unchanged_line:{anchor.head_start_line};lexical_only")
return evidence
def test_indexed_matching_attaches_exactly_what_the_row_by_row_matching_did(tmp_path):
"""Randomised: renames, base and head spans, anchors out of order and in several files."""
import random
from openultrasast.model.contracts import PathRename, QuestionIdentity
from openultrasast.model.regions import affected_context
rng = random.Random(20260923)
names = ["a.php", "b.php", "c.php", "old.php"]
for name in ["a.php", "b.php", "c.php"]:
(tmp_path / name).write_text("x\n" * 200)
attached = 0
for _ in range(200):
spans = tuple(
ChangedSpan(rng.choice(names).encode().hex(), s, s + rng.randint(0, 20), rng.choice(["base", "head"]))
for s in (rng.randint(1, 180) for _ in range(rng.randint(0, 6)))
)
anchors = tuple(
LineCorrespondence(
rng.choice(names).encode().hex(), rng.choice(names).encode().hex(), base, base + rng.randint(0, 3), rng.randint(1, 200)
)
for base in (rng.randint(1, 190) for _ in range(rng.randint(0, 40)))
)
renames = (PathRename(b"old.php".hex(), b"c.php".hex()),) if rng.random() < 0.5 else ()
changed = tuple(n.encode().hex() for n in rng.sample(["a.php", "b.php", "c.php"], rng.randint(1, 3)))
ctx = ChangeContext("base", "head", changed, (), spans, renames, (), (), (), "filesystem-bytes-hex", anchors)
question = QuestionIdentity("unit", "php", "q.php", "run", "injection")
rows = [{"kind": "context_summary"}]
for _ in range(rng.randint(1, 12)):
s = rng.randint(1, 190)
rows.append(
{**row(rng.choice(["a.php", "b.php", "c.php"]), function=f"f{s}"), "startLine": s, "endLine": s + rng.randint(0, 30)}
)
enriched, _ = affected_context(ctx, [question], {question: rows}, tmp_path)
attachable = {ctx.decode_path(p) for p in ctx.changed_paths} | {ctx.decode_path(r.head_path) for r in ctx.renames}
expected = []
for r in rows[1:]:
evidence = _reference_evidence(ctx, r["path"], r["startLine"], r["endLine"])
if evidence and r["path"] in attachable:
key = (r["path"], r["function"], tuple(evidence))
if key not in expected:
expected.append(key)
got = [(rel.source.path, rel.source.function, rel.evidence) for rel in enriched.relationships]
assert got == expected
attached += len(got)
assert attached > 100, f"only {attached} relationships across 200 cases: the comparison would be vacuous"