Repository navigation
perf(fork_choice): build the parent-to-children map once in get_filtered_block_tree - #746
pablodeymo wants to merge 1 commit into
Conversation
…ead of scanning the whole block index at every node of filter_block_tree. The scan ran once per node of the justified subtree over the full live window since finality, so the head walk was quadratic in the number of unfinalized blocks, exactly what a finality stall or a range catch-up grows. walk_head and invalidate_subtree already build the map this way; this brings the filter in line with them. A new test keeps the spec-literal scan as a reference and checks the two agree on random trees, including ones where a branch is filtered out.
🤖 Claude Code ReviewReview of PR 746: build the parent-to-children map once in
|
🤖 Kimi Code ReviewI'll review this PR which optimizes Overall AssessmentThis is a solid performance optimization with good test coverage. The core idea—pre-building Issues Found1. Critical: Potential Stack Overflow in Deep Trees (fork_choice.rs, ~line 1908-1910)for &child in children {
if filter_block_tree(store, index, children_of, child, blocks, config)? {The recursive Recommendation: Consider adding 2. Bug:
|
| Severity | Issue | Line |
|---|---|---|
| Medium | Stack overflow risk in deep recursion | ~1908 |
| Medium | children_of may include stale/orphaned entries |
~1962 |
| Low | HashMap hasher choice for hot path | ~1908 |
| Low | Test "reference" claim slightly overstated | ~9933 |
| Trivial | Typo in variable name | ~9970 |
| Trivial | Doc function name consistency | ~1956 |
The PR is correct and should ship after addressing or documenting items 1 and 2.
Automated review by Kimi (Moonshot AI) · kimi-k2.6 · custom prompt
🤖 Codex Code ReviewLooks good overall. This is a targeted fork-choice performance fix, and the new regression test meaningfully checks semantic equivalence against the literal spec-style scan.
Minor note:
Validation note:
Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
…red_block_tree (#746) Build the parent-to-children map once in get_filtered_block_tree instead of scanning the whole block index at every node of filter_block_tree. The scan ran once per node of the justified subtree over the full live window since finality, so the head walk was quadratic in the number of unfinalized blocks, exactly what a finality stall or a range catch-up grows. walk_head and invalidate_subtree already build the map this way; this brings the filter in line with them. A new test keeps the spec-literal scan as a reference and checks the two agree on random trees, including ones where a branch is filtered out. Cherry-picked from fork-choice-children-map at f772f03.
🗒️ Description / Motivation
filter_block_treefound a block's children by scanning the whole block index(
Store::block_index, the fullLiveChainwindow since the finalized block) atevery node it visited. The recursion visits every node of the justified subtree,
so the head walk was quadratic in the number of unfinalized blocks. That runs on
every block import and every tick through
get_head, and the cost grows exactlywhen the node is under pressure: a finality stall or a range catch-up from a
checkpoint anchor.
walk_headandinvalidate_subtreealready build a parent-to-children map onceper call; this brings the filter in line with them. Found by Kimi during an
improvement review.
What Changed
crates/blockchain/state_transition/src/beacon/fork_choice.rsget_filtered_block_treebuildsHashMap<Root, Vec<Root>>from the index inone pass and hands it down.
filter_block_treetakes that map and does one lookup per node instead of ascan per node.
a
BTreeMap" section, thefilter_block_treedoc, one cross-reference incompute_head).compute_headandget_node_childrenkeep their per-level scans on purpose:they are the spec-literal references the tests compare the walk against.
Correctness / Behavior Guarantees
into a map, so it cannot change the result.
calls (
filter_block_treeispubbut has no callers outside this file).Tests Added / Run
the_filtered_tree_matches_the_spec_literal_children_scan_on_random_treeskeeps the old scan as a reference and asserts the map-based result equals it on
100 random trees, including slots where a branch is filtered out (asserts that
happened at least once, so it cannot pass vacuously).
already cover
get_filtered_block_tree.make fmt,make lint, fork_choice unit tests (94 passed), mainnetfork-choice fixture cases (206 passed),
make test-consensus(1324 passed).Related Issues / PRs
✅ Verification Checklist
make fmt— cleanmake lint(clippy with-D warnings) — cleanmake test—test-consensusrun locally;test-nodeleft to CI(unrelated to this change)