Reduce libclang cursor identity overhead with a typed local entity topology #44
Labels
No labels
area/cli
area/core
area/database
area/indexing
area/query
type/epic
type/feature
type/foundation
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
JayKobe6k/ClariTree#44
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem and impact
ClariTree repeatedly hashes and compares native libclang cursors while indexing each translation unit. The
clangcrate implementsHashandPartialEqforclang::Entitythroughclang_hashCursorandclang_equalCursors, so cursor-keyed parent, ordinal, and scope maps turn local ancestor traversal into repeated native calls. This consumes a material share of parser CPU on every full index and changed update.Environment and workload
b1bca86cdd3570cf85b38697a1743d80aa5ddfc3clangcrate: 2.1.0melee-event-matchat6b6f8a8a987c2e871b8b96baa863aaa99e406f33MemoryMax=4G,MemorySwapMax=0,RuntimeMaxSec=240, pinned to CPU 1tests/profile_lineage.rs, symbolized release builds with frame pointersReproduction:
Run baseline and candidate binaries as interleaved, separately built release artifacts under the same cgroup.
Baseline and profiling evidence
Three interleaved 40-TU baseline parse runs took 3,269 ms, 3,278 ms, and 3,256 ms (3,269 ms median). Three 200-TU baseline parse runs took 26,560 ms, 26,781 ms, and 27,379 ms (26,781 ms median). Every 200-TU run produced exactly:
A fresh 200-TU
perf record -e cpu_core/cycles/u -F 99 -gprofile attributed 16.83% of samples directly toclang_equalCursors; the RustEntity::eqwrapper and dynamicclang_sys::clang_equalCursorsdispatch were separately visible at 2.00% and 1.52%.A baseline
perf statrun measured 32,579.96 ms task-clock, 67,442,795,622 cycles, and 212,290,785,341 retired instructions.Root cause
LibclangIndexer::indexbuilds a cursor-keyed parent map after recursively collecting every entity.IndexingStateindependently builds a second cursor-keyed ordinal map and retains a third cursor-keyed scope map.Parent-sensitive helpers such as
qualified_nameandhas_ancestor_kindhash and compare a native cursor again for every ancestor step. The pinned crate implementation confirms thatEntity::eqandEntity::hashcall libclang.These relations are translation-unit-local topology, not durable symbol identity. Paying native cursor identity costs after the collection boundary is unnecessary.
Proposed implementation
EntityIdlocal to one parsed translation unit.EntityTopology.Vec<Option<ScopeId>>.Alternatives considered:
get_kindcalls cost more than avoided inserts.clangcrate process-global runtime constraints.Candidate results
Three interleaved candidate runs produced:
A paired
perf statrun measured 29,603.96 ms task-clock, 59,126,041,698 cycles, and 179,912,192,547 instructions: 9.1%, 12.3%, and 15.3% below baseline respectively. A fresh final-candidate profile reduced directclang_equalCursorssample share from 16.83% to 9.78%.Expected benefit, confidence, and risk
Acceptance criteria
perf statrun.cargo fmt --all -- --check,cargo lint,cargo test --all-targets --all-features,cargo doc --no-deps --all-features, and the Windows-target check throughnix develop --command.Delivered on
mainand pushed toorigin/main.EntityTopologyimplementation:0e69e4cc74f0b15c5e4c3849a1f1f783e6384b8021967f0f7507190449cadc6b1320e03bd2ce42150e92e14150754b60c7206866fa002f2cdf875fbfThe original accepted candidate reduced first-200-TU median parse time from 26.781 s to 23.895 s (10.8%), process CPU by 10.7%, task-clock by 9.1%, cycles by 12.3%, and retired instructions by 15.3%. Direct
clang_equalCursorssample share fell from 16.83% to 9.78%.The separately measured dense parent-chain follow-up reduced the then-current first-200-TU median from 23.860 s to 23.438 s (1.77%). Both paired counter orders reduced retired instructions by about 1.02%, and matched profiles reduced direct
clang_equalCursorssample share from 10.35% to 3.42%.All measured runs preserved exactly 169,045 definitions, 180,182 uses, and 400,939 relationships with approximately 1 GiB peak memory and zero cgroup swap. Duplicate-child and duplicate-parent cursor parity is covered by the typed topology oracle. Full Nix formatting, lint, all-target/all-feature tests, rustdoc, and Windows cross-compilation passed, and independent rereview found no remaining actionable defect.
The additional reference-parent-ID threading experiment was measured and rejected because retired instructions regressed consistently; its rollback and evidence are recorded in the repository annals.
JayKobe6k referenced this issue2026-09-12 18:18:27 +00:00