perf(kernel): syncing a changed Python file resolves each class's bases once
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 build | Before | After | Change |
|---|---|---|---|
| Retry phase, first sync, Node | 2.29 s | 2.08 s | 9% lower (ranges do not overlap) |
| Retry phase, second sync, Node | 2.19 s | 1.96 s | 10% lower (ranges do not overlap) |
| One-file sync, second sync, Node | 5.33 s | 5.15 s | 3% lower (ranges do not overlap) |
| One-file sync, first sync, Node | 5.45 s | 5.25 s | no clear change (ranges overlap) |
| One-file sync, Bun (both syncs) | 5.59 s, 5.41 s | 5.74 s, 5.33 s | no clear change (first sync 2.8% slower, inside the 3% band) |
| Retry phase, Bun | 2.29 s, 2.20 s | 2.04 s, 2.02 s | 11% and 8% lower; ranges overlap on the first sync |
| Full index, Node / Bun | 36.16 s / 39.59 s | 34.92 s / 39.49 s | no clear change |
| Full index, Django (guard, Node, 2 runs per build) | 12.27 s | 12.44 s | no 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 traceorWorker exited unexpectedlyin 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, Node | Before | After |
|---|---|---|
| Retry phase, first sync | 2.29 s | 2.08 s |
| Retry phase, second sync | 2.19 s | 1.96 s |
| One-file sync, second sync | 5.33 s | 5.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.