F4.06 · part 2 of 3
Cache-friendly iteration
Walking a CHAMP is a pre-order over the tree: at each node, yield the packed entry array first, then descend into each child. Because the entries are contiguous and held apart from child pointers, that first step is a linear read of adjacent memory — a much friendlier access pattern than a HAMT, whose single array interleaves entries with pointers and gaps. The order is also canonical: the same map always yields its entries in the same sequence.
The same node, walked as a CHAMP and as a HAMT. Select the step, or compare the layouts.
Entries first, then descend
A node’s own entries come out before any child’s, in slot order; then each child is walked the same way. Select what to read — the entry sweep, the descent, or the HAMT layout it improves on.
What to read · select one
role: entries are contiguous, read first
result: walk entries[] linearly
Contiguous entries turn a node’s contribution into a straight-line read; the canonical order makes that read reproducible. A HAMT gets the same answer but pays in cache misses, hopping over gaps and past child pointers.
Advanced: canonical order & diffs
The traversal is the eager Enum/lazy Stream story from
F3.04, now over a tree: reduce this node’s entries, then reduce its children. Two properties make it
useful beyond speed. First, the order is canonical, so iterating the same map twice — or two equal
maps — yields identical sequences, which is what makes a deterministic snapshot possible. Second,
because an update reuses every untouched child by sharing, walking a changed map re-reads only the parts
that differ. For the course’s page registry that is the cheap path: take a snapshot, edit one page,
and the diff against the previous snapshot is the single changed sub-tree — the same idea a LiveView
uses to send only what changed.
# pre-order walk: this node's packed entries, then each child def reduce(%Champ.Node{entries: es, nodes: ns}, acc, fun) do acc = Enum.reduce(es, acc, fun) # contiguous, canonical order Enum.reduce(ns, acc, fn child, a -> reduce(child, a, fun) end) end # same sequence every time -> snapshot, then diff only the changed sub-tree
F3.04 · Enum walks a collection
Reduce folds elements in order into an accumulator.
F4.06.2 · a cache-friendly pre-order
Reduce the packed entries, then each child — contiguous, canonical, diff-friendly.
Next: canonical equality — what that one fixed shape per map buys when you compare two of them.
References
Primary sources for the CHAMP traversal, and where it connects in the course.
Sources
- Steindorfer & Vinju, “Optimizing Hash-Array Mapped Tries for Fast and Lean Immutable JVM Collections” (OOPSLA 2015) — the CHAMP paper, which separates entries from sub-nodes for a contiguous, canonical iteration order (DOI 10.1145/2814270.2814312).
- Bagwell, “Ideal Hash Trees” (2001) — the HAMT layout that interleaves entries and pointers, which CHAMP reorganises.