[all-commits] [llvm/llvm-project] f3cb7b: [ADT] Rewrite ImmutableSet/Map in-order iterator w...

Gábor Horváth via All-commits all-commits at lists.llvm.org
Thu Jun 25 10:06:50 PDT 2026


  Branch: refs/heads/main
  Home:   https://github.com/llvm/llvm-project
  Commit: f3cb7bbc0110938c78542add1f79364fa526fb8d
      https://github.com/llvm/llvm-project/commit/f3cb7bbc0110938c78542add1f79364fa526fb8d
  Author: Gábor Horváth <xazax.hun at gmail.com>
  Date:   2026-06-25 (Thu, 25 Jun 2026)

  Changed paths:
    M llvm/benchmarks/CMakeLists.txt
    A llvm/benchmarks/ImmutableSetIteratorBM.cpp
    M llvm/include/llvm/ADT/ImmutableSet.h
    M llvm/unittests/ADT/ImmutableSetTest.cpp

  Log Message:
  -----------
  [ADT] Rewrite ImmutableSet/Map in-order iterator without per-node state (#205552)

ImutAVLTreeInOrderIterator was layered on a "generic" iterator whose
stack stored a 2-bit visit-state (None/Left/Right) packed into the low
bits of each node pointer, and which was spun through several
intermediate states per in-order step.

Replace it with a single iterator that keeps a stack of plain node
pointers (the root-to-current ancestor chain) and recovers the traversal
direction by inspecting whether it is ascending from a node's left or
right child. A node's parent cannot be cached in the node itself because
these trees are persistent and structurally shared (one node may be a
child of different parents across tree versions), so the ancestor stack
is the per-traversal parent chain.

The observable contract is unchanged: same in-order sequence, same
skipSubTree() semantics (so tree canonicalization via isEqual is
unaffected), and the iterator stays bidirectional. operator== now
compares the current node rather than the whole path, which is O(1) and
correct because within a single tree a node has a unique path.

Microbenchmarks (llvm/benchmarks/ImmutableSetIteratorBM.cpp, -O2 with
assertions) show ~2x faster plain traversal and skipSubTree, and the
node-identity operator== is ~1.5x faster on comparison-heavy loops (no
change on the common compare-against-end() path).

| Test (input size) | Before | After | Speedup |
|:--|--:|--:|--:|
| Cycle `N=100` | 64.3 ms | 52.3 ms | 1.23× |
| Cycle `N=200` | 464.5 ms | 375.5 ms | 1.24× |
| Cycle `N=300` | 1540 ms | 1260 ms | 1.22× |
| Merge `N=1000` | 51.3 ms | 44.7 ms | 1.15× |
| Merge `N=2000` | 237.0 ms | 198.0 ms | 1.20× |
| Merge `N=5000` | 1710 ms | 1480 ms | 1.16× |
| Switch fan-out `N=1000` | 73.8 ms | 62.7 ms | 1.18× |
| Switch fan-out `N=2000` | 328.0 ms | 294.2 ms | 1.11× |
| Switch fan-out `N=4000` | 1420 ms | 1260 ms | 1.13× |

Assisted by: Claude Opus 4.8

Co-authored-by: Gabor Horvath <gaborh at apple.com>



To unsubscribe from these emails, change your notification settings at https://github.com/llvm/llvm-project/settings/notifications


More information about the All-commits mailing list