mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-08 08:53:43 +08:00
Audit for O(n^2) hot paths after Round 10. The dedup loop in py_emit_resolved_call scanned ALL prior entries linearly, making emission O(N) and total file work O(N^2). On a synthetic 2000-class fixture with 8000 calls, that dominated time at 5083 ms. Cap the scan window at the last 256 entries. Most duplicate emissions come from the same expression being walked twice (resolver + emitter passes), so they're temporally adjacent — a 256-entry window catches every common case while keeping the dedup O(1) per emission. Scale measurements (test_py_lsp_scale.c added): | Classes | Calls | Resolved | Before | After | |--------:|------:|---------:|-------:|------:| | 100 | 400 | 300 | 55ms | 57ms | | 500 | 2000 | 1500 | 333ms | 323ms | | 2000 | 8000 | 6000 | 5083ms | 1804ms| Ratio for 20x input: 91.8x -> 31.4x (linear would be 20x; quadratic would be 400x). No remaining quadratic hot path in code added by this branch. Other hot paths surveyed: - py_register_def -> cbm_registry_lookup_type: O(types) per def at registration, O(D x types) for D defs. Pre-existing linear scan in type_registry.c; hash-indexing it would be a cross-language optimization (Go and C++ LSPs benefit too). - py_lookup_attribute / py_lookup_field: O(types) per call site. Same pre-existing linear scan. With ~900 stdlib types + per-file defs, accounts for ~75µs/call at 2000-class scale. - MODULE submodule prefix scan: O(funcs) per module-attribute access, early-exits on first match. Bounded by registry size. - lambda / dict-literal registries: linear, but bounded by per-file N which is small. Bench unchanged at 100% / 12ms (52/52 calls, 178 LOC).