Skip to content

Conversation

@yongkangc
Copy link
Member

@yongkangc yongkangc commented Oct 28, 2025

Closes #19249

Eliminates sorting overhead per block by returning TrieInputSorted instead of unsorted TrieInput from compute_trie_input, and have ExecutedBlock utilise the TrieInputSorted

Previously, MultiProofConfig::from_input() would call drain_into_sorted() on both nodes and state every block, performing expensive sorting operations:

This eliminates 2-5ms of sorting overhead per block by returning
TrieInputSorted instead of unsorted TrieInput from compute_trie_input.

Previously, MultiProofConfig::from_input would call drain_into_sorted()
on both nodes and state, performing expensive sorting operations every
block. Now compute_trie_input sorts once at the end and returns sorted
data, making MultiProofConfig::from_input a simple Arc wrapper.

Changes:
- Add TrieInputSorted type with sorted TrieUpdates and HashedPostState
- Add clear() methods to TrieUpdatesSorted and HashedPostStateSorted
- Update compute_trie_input to return (TrieInputSorted, BlockNumber)
- Update MultiProofConfig::from_input to accept TrieInputSorted
- Update BasicEngineValidator to store Option<TrieInputSorted>

The implementation uses a "build unsorted, sort once" strategy:
unsorted HashMap-based structures are used during building for fast
extend operations, then sorted once before returning. This eliminates
redundant sorting while maintaining performance.

Resolves: #19249
@yongkangc yongkangc added C-perf A change motivated by improving speed, memory usage or disk footprint A-engine Related to the engine implementation labels Oct 28, 2025
@yongkangc yongkangc requested a review from Rjected as a code owner October 28, 2025 04:15
@github-project-automation github-project-automation bot moved this to Backlog in Reth Tracker Oct 28, 2025
@yongkangc yongkangc marked this pull request as draft October 28, 2025 04:21
@yongkangc yongkangc self-assigned this Oct 28, 2025
@yongkangc yongkangc moved this from Backlog to In Progress in Reth Tracker Oct 28, 2025
Updated the handling of trie input in the compute_trie_input function to improve performance and memory efficiency. The changes include:

- Replaced the use of Option<TrieInputSorted> with Option<TrieInput> to allow for better reuse of allocated capacity.
- Introduced a new method, drain_into_sorted, in TrieInput to convert it into TrieInputSorted while retaining HashMap capacity for subsequent operations.
- Adjusted the logic in compute_trie_input to utilize the new method, reducing unnecessary allocations and improving performance during block validations.

These modifications streamline the trie input processing, enhancing overall efficiency in the engine's validation workflow.
@yongkangc yongkangc added the A-trie Related to Merkle Patricia Trie implementation label Oct 29, 2025
Replaced the existing HashedPostState and TrieUpdates with their sorted counterparts, HashedPostStateSorted and TrieUpdatesSorted, in the ExecutedBlock struct. This change enhances the efficiency of state handling by ensuring that the trie updates and hashed state are maintained in a sorted order, improving performance during block execution and validation.
- The previous approach:
  - Converts every sorted block back into hash maps (cloning all
    keys/values once per block) because extend_with_blocks works
    on the unsorted representation.
  - After all that, drain_into_sorted() iterates those hash maps,
    builds sorted Vecs, and drains the allocations—more cloning
    and shuffling before we return to the same sorted layout we
    could have maintained from the start.

- So the new loop cuts out the conversion overhead and reduces
  allocations; the old code was strictly more work for the same
  end result.
Your changes improve performance by:

1.  **Avoiding Costly Allocations:** Instead of creating and merging many temporary `HashMap`s, the code now builds the final `HashMap` directly from sorted lists (`Vec`s). This drastically reduces memory allocation overhead.

2.  **Faster CPU Operations:** Iterating over a `Vec` is more cache-friendly and faster for the CPU than iterating over a `HashMap`. Additionally, you removed a redundant lookup (`.remove()`) from a critical loop, saving extra CPU cycles.
-   Since TrieInputSorted already has the Arc-wrapped nodes and
   state, we can use them directly without creating an
  intermediate wrapper struct. After eliminating both call
  sites, MultiProofConfig became dead code, so we removed it.
Fixed syntax error in test_hashed_storage_extend_from_sorted_wiped
that was preventing compilation and formatting.
Copy link
Collaborator

@mattsse mattsse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all of this lgtm

although I haven't reviewed all the order+extend changes line by line

input.state = Arc::clone(&first.hashed_state);
input.nodes = Arc::clone(&first.trie_updates);

// Only clone and mutate if there are multiple in-memory blocks.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah this makes sense

Use `.peekable()` instead of checking `blocks.len() > 1` for more
idiomatic iterator usage. This makes the code clearer by checking
if there are more items after consuming the first one, rather than
mixing iterator consumption with slice length checks.
@yongkangc
Copy link
Member Author

@mediocregopher @mattsse addressed your feedbacks 👍🏻

Copy link
Collaborator

@mediocregopher mediocregopher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One little question but overall LGTM, will need rebasing with #19430 merged

Add a dedicated `append_ref` method to `TrieInputSorted` that accepts
`&HashedPostStateSorted` directly, encapsulating the logic of extending
prefix sets and state. This makes the code cleaner and more maintainable
by moving the conversion outside and calling a dedicated method.

This addresses mediocregopher's review comment to have `append_ref`
accept sorted state directly, allowing for better encapsulation and
potential future optimizations.
Add construct_prefix_sets() to HashedPostStateSorted and construct_prefix_set()
to HashedStorageSorted to support the append_ref method on TrieInputSorted.

These methods efficiently iterate over already-sorted data to build prefix sets
needed for trie computation, without requiring any sorting operations.

Also fix BuiltPayloadExecutedBlock conversion to correctly use Either::Right for
sorted variants and convert unsorted to sorted when needed.
Modify the handling of hashed state and trie updates in BuiltPayloadExecutedBlock to keep them unsorted until conversion is necessary. This change ensures that the conversion to sorted form occurs only when required, improving efficiency and clarity in the codebase.
Eliminates redundant filter().count() pre-pass in both
TrieUpdates::extend_from_sorted and StorageTrieUpdates::extend_from_sorted.
Instead, use sorted.account_nodes.len() / sorted.storage_nodes.len() directly
for capacity reservation.

The previous implementation iterated twice: once to count filtered entries,
then again to insert them. The new approach reserves using the full vector
length, resulting in slight over-allocation but avoiding the O(n) pre-pass
entirely. The insertion loop still filters correctly, ensuring behavioral
correctness.
@mediocregopher mediocregopher added this pull request to the merge queue Nov 19, 2025
Merged via the queue into main with commit e58aa09 Nov 19, 2025
42 checks passed
@mediocregopher mediocregopher deleted the yk/compute_trie2 branch November 19, 2025 16:16
@github-project-automation github-project-automation bot moved this from In Progress to Done in Reth Tracker Nov 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-engine Related to the engine implementation A-trie Related to Merkle Patricia Trie implementation C-perf A change motivated by improving speed, memory usage or disk footprint

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Return sorted data from compute_trie_input

4 participants