perf(kernel): syncing a changed Python file resolves each class's bases once

Merged
BO
13d ago
+9-0

Syncing one changed file in CPython takes about 3% less on Node (5.33 s to 5.15 s on the second sync) and its retry phase 9–10% less (2.29 s to 2.08 s, 2.19 s to 1.96 s); Bun's sync time shows no clear change.

Metric, CPython (3,710 files), median of 4 runs per buildBeforeAfterChange
Retry phase, first sync, Node2.29 s2.08 s9% lower (ranges do not overlap)
Retry phase, second sync, Node2.19 s1.96 s10% lower (ranges do not overlap)
One-file sync, second sync, Node5.33 s5.15 s3% lower (ranges do not overlap)
One-file sync, first sync, Node5.45 s5.25 sno clear change (ranges overlap)
One-file sync, Bun (both syncs)5.59 s, 5.41 s5.74 s, 5.33 sno clear change (first sync 2.8% slower, inside the 3% band)
Retry phase, Bun2.29 s, 2.20 s2.04 s, 2.02 s11% and 8% lower; ranges overlap on the first sync
Full index, Node / Bun36.16 s / 39.59 s34.92 s / 39.49 sno clear change
Full index, Django (guard, Node, 2 runs per build)12.27 s12.44 sno clear change

The small size is expected: this removes part of the two slow scans from the earlier fix (#493), not the scans themselves. Figures are from codegraph sync after appending one line to Lib/os.py on a fresh index, builds alternated B F F B B F F B; the preset rule (median gain above 3% and non-overlapping ranges) was declared before the runs.

What changes

python_bases resolves a class's base classes from its header and file. The scan that looks for a descendant of a class (see #493) calls it for every class that has a method of the name being linked, again for each such reference. A class's bases depend only on that class, so they are now kept by class id for the rest of the run (py_bases_memo, beside the existing py_descendants_memo). 8 lines of Rust.

Checks

  • The graph is unchanged: on CPython (167,929 nodes, 600,467 edges, 364,595 unresolved references) and Django (62,500, 185,361, 144,585), no edge row, node row or unresolved reference differs between the builds in either direction.
  • Full vitest suite on this head: 702 files, 8,952 passed, 39 skipped, 0 failed; no Native stack trace or Worker exited unexpectedly in the raw output. Kernel golden dumps unchanged.
  • README: checked "About this fork". The rows that compare the CPython one-file sync with upstream are still out of date in the fork's disfavor (see #493); this change moves the fork's figure by about 3%, so they are unchanged and wait for the fork-against-upstream re-run. CHANGELOG entry added under Fixes.

Not covered: the other slow parts of the retry phase (the first use of the attribute-writer index, about 0.34 s, and the descendant scan itself); other Python corpora; Windows and macOS.


Summary by cubic

Caches each Python class's resolved base classes so the descendant scan during a sync resolves them once per class instead of once per reference. Syncing one changed file in CPython drops the retry phase by 9–10% on Node (2.29 s/2.19 s to 2.08 s/1.96 s) and the one-file sync by about 3% (5.33 s to 5.15 s); Bun shows no clear change.

Benchmarks (CPython, 3,710 files, median of 4 alternating runs per build)

Metric, NodeBeforeAfter
Retry phase, first sync2.29 s2.08 s
Retry phase, second sync2.19 s1.96 s
One-file sync, second sync5.33 s5.15 s

A full index and Bun's one-file sync stay within run-to-run variation. This removes part of the slow scans from #493, not the scans themselves.

Verification

  • The graph is unchanged on CPython and Django: no edge, node, or unresolved reference differs.
  • Full vitest suite passes (8,952 passed, 0 failed) and kernel golden dumps are unchanged; a CHANGELOG entry is added under Fixes.

Summary by CodeRabbit

  • Performance
    • Improved Python analysis performance, reducing CPython retry-phase time from 2.29 seconds to 2.08 seconds and one-file sync time from 5.33 seconds to 5.15 seconds in reported benchmarks.
    • Full-index timing remained within normal run-to-run variation, and no graph output differences were observed in CPython or Django benchmarks. No clear sync-time change was measured with Bun.