Clone single-predecessor reaching states during lineage analysis #49
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#49
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
Semantic reaching-definition analysis rebuilt every predecessor output into a new nested
BTreeMapfor each CFG block evaluation, including the common single-predecessor case. At the current baseline,solve_reachingaccounted for 31.69% of whole-run profile children andincoming_stateaccounted for 11.06% self samples. This repeated ordered insertion and allocation materially increased index latency.A validated translation-unit-local change reduces median semantic-analysis time by 17.1% and parse-plus-analysis process CPU/end-to-end time by 7.8% without changing aggregate output counts or measured peak memory.
This is a new independently shippable optimization after the work completed in #37.
Environment and workload
f954a2b07fbb6a58127ff5a0a4a4ea21aa0e6611(main/origin/main)fn incoming_state(predecessors: &[BasicBlockId],outputs: &BTreeMap<BasicBlockId, ReachingState>,) -> ReachingState {let mut state = ReachingState::new();for predecessor in predecessors {if let Some(output) = outputs.get(predecessor) {for (symbol, definitions) in output {let reaching = state.entry(*symbol).or_default();for (definition, certainty) in definitions {reaching.entry(*definition).and_modify(|existing| {*existing = merge_certainties(*existing, *certainty);}).or_insert(*certainty);}}}}state}melee-event-matchat6b6f8a8a987c2e871b8b96baa863aaa99e406f330c025066b2f5ed7940a668d4fc97b912d8eb5d467fb7bb33ba0e72ab7d50ea16MemoryMax=8G,MemorySwapMax=0, andRuntimeMaxSec=24073b93fde6016c4ff25fa780f68d52492b1ae0a41f14878e139e0e50f79b938aabd25e352c532e38f71d90547773dfddc4c79ab7a293949796c8b05b459aa3382Reproduction uses the ignored release-mode
profile_lineagetest withCLARITREE_PROFILE_PROJECTset to the corpus andCLARITREE_PROFILE_LIMIT=200.Baseline and root cause
The existing
incoming_statestarts with an emptyBTreeMap, then walks every predecessor, symbol, and reaching definition and inserts each item into new ordered maps. With one predecessor, this pays repeatedO(R log R)insertion and allocation to recreate an already ordered state instead of using the standard linear-timeBTreeMap::clonepath.The workload has 53,015 basic blocks and 49,028 CFG edges, so single- or zero-predecessor blocks are common.
incoming_stateruns repeatedly during both fixed-point solves and again while constructing merges and connecting reference-backed uses.Baseline profile
target/perf-member-scope-index-candidate-200.data(SHA-2561520db0691e48f101a68ed1da65b74944e95fec3e8f24662c6e55959627074ef) attributed 11.06% self samples toincoming_state; 9.45 percentage points came throughsolve_reaching. Allocation and B-tree insertion were its dominant children.Proposed implementation
merge_certaintiesoperation.BTreeMap<SymbolId, BTreeMap<ValueDefinitionId, AnalysisCertainty>>for deterministic order and typed IDs.CertainversusPossiblecertainty merging.This preserves the previous fold semantics because
merge_certaintiesis commutative, associative for the closed certainty precedence, and idempotent. Duplicate predecessors therefore remain harmless, missing outputs remain ignored, and cloning does not change deterministic key order.Alternatives considered:
Before and after measurements
Fresh execution order was baseline/candidate/candidate/baseline/baseline/candidate.
Every run preserved 377,067 definitions, 412,330 uses, and 801,507 lineage relationships, along with the complete parser aggregate counts. Every run peaked at 3.9 GiB and used zero cgroup swap.
Paired first-40-TU
perf statruns in both execution orders produced medians of:The exact baseline instruction runs were 59,477,366,713 and 59,484,052,639; candidate runs were 57,661,480,966 and 57,661,476,533.
Post-change profile
target/perf-incoming-clone-candidate-200.data(SHA-256831096a7dbdc4c377a581ee9921dbf56d9e959bd3811fe28ed0d8b27f3138b36) reducedincoming_stateself share to 2.48%. The profiled run retained exact counts and zero cgroup swap; profiling overhead increased peak memory to 6.8 GiB and is excluded from ordinary-run memory comparison.All
target/and/tmp/opencode/benchmark artifacts are local, ignored, and non-durable; their hashes identify retained copies only.Expected benefit, confidence, and risks
Expected benefit is approximately 15-20% lower semantic-analysis time and 5-10% lower parse-plus-analysis time for CFG workloads dominated by single-predecessor reaching states. Confidence is high for the pinned workload and semantic equivalence, and medium for the magnitude on other corpora.
Risks are limited to incorrectly changing certainty union or predecessor edge cases. The implementation retains the same merge function and ordered types, and direct tests cover missing, duplicate, reversed, and mixed-certainty inputs. Native Windows runtime behavior and performance remain untested; Windows GNU cross-compilation passes.
Validation and acceptance criteria
The current candidate passes:
nix develop --command cargo fmt --all -- --checknix develop --command cargo lintnix develop --command cargo test --all-targets --all-features(282 unit tests plus all integration suites)nix develop --command cargo doc --no-deps --all-featuresnix develop --command cargo check --target x86_64-pc-windows-gnu --all-targets --all-featuresAcceptance criteria:
Rollback is a direct revert to rebuilding the nested reaching state from an empty map; no schema or data rollback is required.
Delivered to
main:35bdc6d87c1a6eb8ed85b0600ec14849e3c863a3(Optimize reaching state union)ed8b489e919d8e11d7e77d618cf1cb2a99b868a5On three interleaved first-200-TU runs of pinned melee-event-match, median semantic analysis fell from 47.367 s to 39.273 s (-17.09%), process CPU from 99.167 s to 91.438 s (-7.79%), and end-to-end time from 99.51 s to 91.76 s (-7.79%). Exact aggregate counts were preserved; all ordinary runs stayed at a 3.9 GiB peak with zero cgroup swap.
Paired first-40-TU
perf statruns reduced task-clock 1.35%, cycles 1.34%, and retired instructions 3.06%. The post-change profile reduced directincoming_stateself share from 11.06% to 2.48%.The complete required Nix validation passed after direct missing/single/duplicate/reversed-predecessor and certainty-merge coverage: format, lint, 282 unit tests plus all integration suites, rustdoc, and Windows GNU all-target/all-feature cross-check. Native Windows runtime remains untested, and parity evidence is aggregate-count rather than identity-level. Full workload, artifact hashes, observer-effect qualification, rollback, and alternatives are recorded in
annals/2026-09-11-clone-predecessor-reaching-state.md.