Skip to content

reduce_canonicalize: reserve the memo and hash-cons maps - #60

Merged
olydis merged 1 commit into
mainfrom
claude/tc-performance-optimizations-rqtyzl-reduce-reserve
Oct 3, 2026
Merged

olydis merged 1 commit into
mainfrom
claude/tc-performance-optimizations-rqtyzl-reduce-reserve

Conversation

@olydis

@olydis olydis commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Three reserve calls in reduce_canonicalize.cpp: apply_memo (1<<24), hash_cons and node_defs_idx (1<<22 each). Reducing forest's library fills the memo to 6.8M entries and the other two maps to 2.1M each. Without the reserves, rehashing as they grow, plus the fuller tables left behind, takes more than half the run.

Effect

Inputs and flags are the ones forest's build uses (build.sh phases 5 and 5b, --fuel=100000000, plus --stats-per-symbol for the library). Each figure is the median of 3 runs, with the old and new binaries run back to back on the same machine.

input before after peak RSS
forest .dag-bundle-library 16.0 s 7.05 s 1078 → 1145 MB
arboretum bundle (5b) 10.3 s 4.58 s 742 → 843 MB
tiny module (test.sh's *.dag) 0.003 s 0.14 s 10 → 207 MB

Forest runs the two reductions one after the other, saving about 14.7 s. Its build/test steps run three tiny reductions, which add about 0.45 s, so a full build saves about 14.3 s net.

Other variants measured (lib / arb, seconds; baseline 16.2 / 10.6):

  • apply_memo reserve alone: 9.8 / 7.0
  • apply_memo + hash_cons: 8.6 / 5.8
  • all three (this PR): 7.3 / 4.75
  • left out: hash_cons and node_defs_idx at 1<<23: 7.1 / 4.3, but +70 MB
  • left out: max_load_factor(0.5) with no reserve: 12.1 / 8.9
  • left out: reserving the vectors for 1<<23 ids: no change
  • left out: one shared stack of pending memo keys in place of a std::vector per task: −0.5 to −2 s alone, and nothing on top of the reserves

Validation

  • Output is byte-identical (cmp) on both bundles, and so is the --stats-per-symbol table (TOTAL 4,712,299). Forest's committed build/test reduce expect files (id, Bool.not, pipeline/03-reduced.dag) are also unchanged. None of the three maps is ever iterated, so the bucket count cannot affect output order.
  • implementation/cpp/dag-machine/test.sh passes under LC_ALL=C and under LC_ALL=C.UTF-8: jets 5, runner 21/21, reduce_canonicalize 4/4.
  • Reviewer re-measurement, 3 rounds back to back: library 16.75 → 6.99 s, arboretum bundle 10.64 → 4.39 s, tiny module 0.00 → 0.15 s / 206 MB. Output matched in every round.

Caveats

  • The sizes are fixed, chosen to fit forest's library today, with 2× headroom on the memo. A bigger module still works, and its maps grow as before. Sizing from the input would be wrong: an already-reduced 7.5 MB bundle needs almost no memo.
  • A tiny module costs 0.14 s and about 200 MB, because libstdc++ zeroes the reserved bucket arrays.
  • Wall times are noisy on this shared 4-core host. The ratio (−56% on both inputs) was stable across 5 rounds.

Merge order

None required. Forest picks this up when it bumps the tree-calculus pin through arboretum.

🤖 Generated with Claude Code

https://claude.ai/code/session_018ffv5AybPTpV56D5vuayQr

Growing an unordered_map rehashes every entry it holds. Reducing forest's
library fills the memo to 6.8M entries and hash_cons and node_defs_idx to
2.1M each, and that growth, with the fuller tables it leaves, was over
half the run. Reserved up front, with the build's flags (medians of 3,
back to back; output and --stats-per-symbol table byte-identical):

  forest library      16.0 -> 7.05 s  (peak RSS 1078 -> 1145 MB)
  arboretum bundle    10.3 -> 4.58 s  (742 -> 843 MB)

A tiny module pays for it: 0.003 -> 0.14 s, 10 -> 207 MB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ffv5AybPTpV56D5vuayQr
@olydis
olydis merged commit b873b56 into main Oct 3, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants