mirror of https://github.com/icsharpcode/ILSpy.git
Browse Source
Removing a collection element invalidated the parent's whole flattened index set, so the next sibling navigation rebuilt it in O(children) -- and that renumber, not the array shift, was the dominant cost: a reverse (tail-first) removal, which shifts nothing, was still quadratic, isolating EnsureChildIndices as the culprit. When a node's only collection is also its last slot it owns the contiguous range [base, base + Count) with nothing after it, so an element's flattened index is just base + its local position (base is the slot's declaration position, since the preceding slots are all single children). On that fast-path -- which the generator now flags, passing the base index -- Add indexes only the appended element, Insert/Remove renumber only the shifted suffix, and IndexOf is base-relative O(1); none of them invalidate. Other shapes (several collections, or a slot after the collection) keep the invalidate-and-rebuild fallback. Tail and scattered removal, and removal during traversal, no longer pay the per-operation renumber. Microbenchmark, removing every element of an N-element block: tail-first at N=32000 went 1493 ms -> 0.5 ms (now O(N)); front-first is ~1.6x faster and no longer renumbers (its residual cost is the inherent array shift). Output is byte-identical and the Pretty suite stays green with CheckInvariant validating the maintained indices after every transform. Assisted-by: Claude:claude-opus-4-8:Claude Codepull/3807/head
3 changed files with 65 additions and 5 deletions
Loading…
Reference in new issue