Repository navigation
perf(py-lsp): O(1) type membership in the shared Python cross-registry build - #2420
Fieldnote-Echo wants to merge 7 commits into
Conversation
…y build The shared tier-2 registry registered Python defs one at a time without a mid-build finalize, so every Method's receiver probe scanned all registered types linearly: O(methods x types), single-threaded in lsp_cross_prepare. Keep the per-def registration order and back only the existence probe with a cbm_ht set of type QNs, as the Go and Rust builders do; the registry is identical by construction. On the Python 3.14 stdlib + site-packages the builder drops from 67-75 s to 0.20-0.25 s, with a byte-identical graph. Signed-off-by: Nelson Spence <nelson@projectnavi.ai>
|
Thanks for opening this — it has been seen, and it is queued. This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence. Current review status: working through a backlog. What that means for this PR, concretely:
Things that will genuinely speed it up whenever review does happen:
If this fixes a bug, a reproduction we can run is worth more than a description of the symptom. Thanks for contributing, and sorry in advance for the wait. |
Signed-off-by: Nelson Spence <nelson@projectnavi.ai>
Signed-off-by: Nelson Spence <nelson@projectnavi.ai>
|
Thanks for the Python registry measurements and the registry-order control. I have read the fallback and byte-equivalence claims; they remain to be checked in review, including the perf suite that PR CI skips. No routine rebase is requested here. |
Brings DeusData#2420 (O(1) type membership in the shared Python cross-registry build, DeusData#2413) up to date with main a222715. Conflicts in internal/cbm/lsp/py_lsp.c, resolved as follows: - Includes: main already includes src/foundation/hash_table.h (module re-exports) and added lsp_node_iter.h. Main's lines are kept; the branch's duplicate include is dropped. - py_register_lsp_defs: main changed it to return bool (false when the package scope cannot be normalized). The merged signature keeps main's bool return and adds this branch's PyKnownTypes *known parameter. - cbm_run_py_lsp_cross_status: main's failure handling is kept and passes known = NULL, as the branch did for the per-file path. - cbm_py_build_cross_registry: main added an early `return NULL` when a def fails to register. The merged loop passes &known and frees the type-QN set before that early return, so the set does not leak on the failure path. - The test seam cbm_py_lsp_test_register_defs (added on main) passes known = NULL. tests/test_py_lsp.c and tests/test_py_lsp_scale.c merged cleanly. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Replace the wall-clock ratio test pylsp_scale_cross_registry_build_linear (py_lsp_scale suite) with pylsp_cross_registry_build_work_is_linear in the py_lsp suite. The new test builds the same generated input (n classes, each followed by two methods, in extraction order) and reads the seam-build CBM_LSP_WORK counter instead of a clock, so its verdict does not depend on host load, and it runs in PR CI, which skips the perf suites. 4x the classes must cost at most ~4x the counted work. With the type-QN set the build examines no registry candidates (0 at 1000 and 4000 classes); with the scanning receiver probe the work grows from 2,809,000 to 23,236,000 and the test fails. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
|
Thank you, @Fieldnote-Echo, and sorry this waited three weeks for us. The change is exactly the right size: it backs only the receiver-existence probe with a set, keeps the per-def registration order so the registry is identical by construction (and you explained why a types-first build would not be), falls back to the registry probe if the set cannot be allocated, and What we changed on your branch, so you know what landed:
Before pushing we ran the merged tree on real input (macOS arm64):
Nothing is needed from you. CI runs again on the refreshed branch, and once it is green the merge decision is the last step on our side. Thanks again for isolating this builder from the per-file path and for measuring it so carefully. |
|
Thank you again for the Python shared-registry improvement. The October updates record the deterministic counted-work regression and branch integration as completed on our side. No further contributor work is requested; sorry for the delay in the remaining follow-up, and I’ll follow up soon. |
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
|
Thank you again, @Fieldnote-Echo, and for your patience while this waited on us. The CI run after our October 20 update stopped early on every test leg at a repository-wide check of the published language counts, which was broken on main at the time and has since been repaired, so none of the suites (including py_lsp) got to run. We merged current main into your branch once more (one signed-off merge commit, 2be8ee3; your commits are untouched) so CI runs fresh. Because this change speeds up the shared Python registry build, we will also compare the graph itself on Django and the Python standard library before merging, and post the numbers here. Nothing is needed from you. If you push to this branch again, please pull first so the merge commit is kept. |
Fixes #2413
What does this PR do?
The shared Python cross-registry build registers defs one at a time, so each Method's receiver probe (
internal/cbm/lsp/py_lsp.c:5049) is a linear scan over the types registered so far (type_registry.c:830-834). I back only that existence probe with acbm_htset of type QNs, updated after everycbm_registry_add_type, as the Go (959f4de) and Rust (10b3b79) fixes did. If the set cannot be allocated or an insert fails, probes fall back to the registry lookup. Keeping the per-def order makes the registry identical by construction (a types-first build would be simpler but can reorder auto-registered receiver types). Sorted node and edge dumps match main byte for byte, and peak RSS is unchanged.pylsp_scale_cross_registry_build_linearfails on main: going from 1k to 16k classes costs over 100× more time there, and under 8× here. It is in thepy_lsp_scaleperf suite, which PR CI skips viaCBM_SKIP_PERF, so it needs a local run.pylsp_cross_registry_interleaved_orderpins the registry order.Checklist
git commit -s) — required, CI rejectsunsigned commits (DCO, see CONTRIBUTING.md)
make -f Makefile.cbm test)(run as
make -f Makefile.cbm test-par)make -f Makefile.cbm lint-ci)(for this PR's changes: CI's lint job passes on this head, and local cppcheck 2.22 / clang-format 23 add no findings over main)
Prepared with AI assistance (exploration, reproduction, verification, implementation); the spec and design decisions are mine, and I reviewed and ran everything above.