-
Notifications
You must be signed in to change notification settings - Fork 1k
feat: Add parallel state root computation support for Bonsai trie #9576
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
b6b2b46
add state root computation optimization
matkt 7fc21ec
refactor stateroot opti
matkt d72a362
remove threshold
matkt 602b4b3
try use parallel stream
matkt f3ca34a
full parallel stream
matkt f9203c2
use cached thread pool
matkt 96d9a24
10 fixed thread pool
matkt 6bc14bb
change parallel trie logic and condition
matkt 51d7680
clean code
matkt 8f197b7
Merge branch 'main' into merkle-trie-optimisation
matkt 3ff878d
add parallel update for leaf and null node
matkt 0b1acc4
fix logic issue for leaf and null
matkt 917c12e
clean build
matkt d9381a0
clean build
matkt 032d645
fix logic
matkt d0a7026
change fork join pool and condition
matkt 97faea3
remove NCPU condition
matkt 33b0b7a
set correct condition
matkt 474b810
add parallel extension
matkt 3e4e8b3
change logic for root node
matkt 3b3b6f8
disable extension
matkt 459eb45
fix extension
matkt a12cd91
clean extension code
matkt 8e67269
clean extension code
matkt d6cfcb8
merge
matkt 6c1c09a
move to ncpu 2
matkt b6c091f
load node in a different location
matkt 03e1d9b
add //stream for fcu
matkt 2db94a6
clean PR
matkt 3867b15
Merge branch 'main' into merkle-trie-optimisation
matkt f02c367
add flag for parallel state root computation
matkt 1421b81
remove parallel stream
matkt e3c735d
Merge branch 'main' into merkle-trie-optimisation
matkt 58ecf61
revert change for parallel stream
matkt 64b74dd
use ForkJoinTask for parallel state root computation
matkt e6c150f
clean PR
matkt 2455b66
fix tests
matkt 86cb7d6
clean comment
matkt 9c39824
Merge branch 'main' into merkle-trie-optimisation
ahamlat 5781c17
Address code review comments
matkt b06f0f0
Merge branch 'main' into merkle-trie-optimisation
matkt c5fb1d5
merge main
matkt 00886fd
Merge branch 'main' into merkle-trie-optimisation
matkt 05ada8d
Merge branch 'main' into merkle-trie-optimisation
matkt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,181 @@ | ||
| # ParallelStoredMerklePatriciaTrie - Logic Explanation | ||
|
|
||
| ## Core Problem Being Solved | ||
|
|
||
| When you need to update many keys in a Merkle Patricia Trie (like Ethereum state updates), doing them one-by-one is slow. This implementation batches updates and processes independent parts of the tree simultaneously using multiple CPU cores. | ||
|
|
||
| ## High-Level Strategy | ||
|
|
||
| **The Big Idea:** A branch node has 16 independent children. If you have updates going to different children, you can process those children in parallel. | ||
|
|
||
| ### Three Phases | ||
|
|
||
| 1. **Accumulation Phase**: Collect all updates without applying them | ||
| 2. **Parallel Processing Phase**: Apply updates recursively, splitting work when beneficial | ||
| 3. **Persistence Phase**: Write all modified nodes to storage at once | ||
|
|
||
| ## The Trie Structure Recap | ||
|
|
||
| Think of the trie like a filing system: | ||
| - **Keys** are converted to hex nibbles (0-F) | ||
| - **Path** through the tree follows these nibbles | ||
| - Example: Key `0xABCD` → path `[A, B, C, D]` | ||
|
|
||
| **Node types:** | ||
| - **Branch**: 16 slots (one per hex digit) + optional value | ||
| - **Extension**: Shortcut for long single paths (e.g., `[A,B,C,D]` → child) | ||
| - **Leaf**: Terminal node with actual value | ||
| - **Null**: Empty slot | ||
|
|
||
| ## How Parallelization Works | ||
|
|
||
| ### Starting Point: Branch Nodes | ||
|
|
||
| When processing a branch node with multiple updates: | ||
|
|
||
| 1. **Group updates by their next nibble** | ||
| - Updates to `[A, ...]` go in group A | ||
| - Updates to `[B, ...]` go in group B | ||
| - And so on for all 16 possible hex digits | ||
|
|
||
| 2. **Decide which groups to parallelize** | ||
| - Large groups (multiple updates) → process in thread pool | ||
| - Small groups (single update) → process in current task | ||
| - Reason: Avoid task creation overhead for tiny tasks | ||
|
|
||
| 3. **Process each group recursively** | ||
| - Each child node gets processed with only its relevant updates | ||
| - That child might be a branch → parallelizes again | ||
| - Creates a tree of parallel tasks | ||
|
|
||
| 4. **Wait for all parallel tasks to complete** | ||
| - Use futures to track parallel work | ||
| - Block until all children are updated at each level | ||
|
|
||
| 5. **Reconstruct the branch with new children** | ||
| - Create new branch node (immutable pattern) | ||
| - Compute its hash | ||
| - Return to parent | ||
|
|
||
| ### Example Visualization | ||
|
|
||
| ``` | ||
| Updates: [0xAB01, 0xAB02, 0xAE03, 0xF123] | ||
|
|
||
| Root Branch: | ||
| ├─ Child[A]: 3 updates [AB01, AB02, AE03] → PARALLEL TASK 1 | ||
| │ └─ Branch at [A]: | ||
| │ ├─ Child[B]: 2 updates [AB01, AB02] → PARALLEL TASK 1.1 | ||
| │ └─ Child[E]: 1 update [AE03] → SEQUENTIAL TASK 1 | ||
| └─ Child[F]: 1 update [F123] → SEQUENTIAL | ||
|
|
||
| Result: multiple threads working simultaneously | ||
| ``` | ||
|
|
||
| ### Why This Works | ||
|
|
||
| **Independence**: Updates to `child[A]` don't affect `child[F]`. They share no data, so they can safely run in parallel. | ||
|
|
||
| **Recursive Nature**: The child at `[A]` is itself a branch, so it can further parallelize its own children `[B]` and `[E]`. | ||
|
|
||
| **Natural Load Balancing**: Hot paths in the trie (many updates) automatically get more parallel workers. | ||
|
|
||
| ## Handling Non-Branch Nodes | ||
|
|
||
| ### Extension Nodes: The Complexity | ||
|
|
||
| Extensions compress long single paths. Example: | ||
| - Instead of: `Branch[A] → Branch[B] → Branch[C] → Child` | ||
| - Store: `Extension([A,B,C]) → Child` | ||
|
|
||
| **Problem**: Extensions are sequential by definition. How to parallelize? | ||
|
|
||
| **Solution**: Temporarily expand them into branches! | ||
|
|
||
| #### Expansion Logic | ||
|
|
||
| When updates diverge within an extension's path: | ||
|
|
||
| 1. **Find the divergence point** | ||
| - Extension path: `[A, B, C, D]` | ||
| - Addition at: `[A, B, C, E]` and `[A, B, C, F]` | ||
| - Divergence at index 3 (position D) | ||
|
|
||
| 2. **Build branch structure up to divergence** | ||
| - Create branches for `[A]`, then `[B]`, then `[C]` | ||
| - At `[C]`, create branch with children at `[D]`, `[E]`, `[F]` | ||
|
|
||
| 3. **Process the expanded structure in parallel** | ||
| - Now it's a branch tree → normal parallel processing | ||
|
|
||
| 4. **Let the visitor pattern re-optimize** | ||
| - After updates, the structure may collapse back to extensions | ||
| - Standard trie optimization (not shown in parallel code) | ||
|
|
||
| **When to expand:** | ||
| - Only if there are multiple updates (worth the overhead) | ||
| - Single update → use sequential visitor (faster, handles restructuring) | ||
|
|
||
| ### Leaf Nodes: Conversion Strategy | ||
|
|
||
| Leaf represents a single value at some path. | ||
|
|
||
| **If multiple addition affect this area:** | ||
| 1. Convert leaf to a branch | ||
| 2. Place the existing leaf value in appropriate child slot | ||
| 3. Process addition in parallel on the new branch | ||
|
|
||
| **If single update:** | ||
| - Let sequential visitor handle it (might just update the value, or split the leaf) | ||
|
|
||
| ### Null Nodes: Building from Scratch | ||
|
|
||
| Empty position getting multiple addition: | ||
| 1. Create empty branch (16 null children) | ||
| 2. Process addition in parallel | ||
| 3. Children get created as addition are applied | ||
|
|
||
| ## Thread Safety Mechanisms | ||
|
|
||
| ### BranchWrapper: Coordinating Parallel Updates | ||
|
|
||
| Problem: Multiple threads want to update different children of the same branch. | ||
|
|
||
| Solution: | ||
| - Wrap the branch node's children list in a synchronized list | ||
| - Each parallel task updates its assigned child slot | ||
| - No conflicts because each task has exclusive child index | ||
| - After all futures complete, reconstruct the branch atomically | ||
|
|
||
| ### CommitCache: Deferred Storage Writes | ||
|
|
||
| Problem: Many threads computing node hashes, but storage writes should be batched. | ||
|
|
||
| Solution: | ||
| - Thread-safe map collects all nodes to persist | ||
| - Each parallel task adds its nodes to that map | ||
| - Single flush at the end writes everything to storage | ||
| - Reduces I/O contention and transaction overhead | ||
|
|
||
| ### Immutable Nodes | ||
|
|
||
| Key principle: Nodes are never modified in place. | ||
|
|
||
| Every update creates a new node instance. This means: | ||
| - No locks needed for reading nodes | ||
| - Parallel tasks can safely read shared nodes | ||
| - Parent gets updated with reference to new child | ||
| - Old nodes eventually garbage collected | ||
|
|
||
| ## Decision Logic: When to Parallelize? | ||
|
|
||
| ### The Partitioning Rule | ||
|
|
||
| A group of updates gets parallel processing if: | ||
| 1. The group has more than 1 update, AND | ||
matkt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 2. The branch has more than 1 active group | ||
|
|
||
| **Why both conditions?** | ||
| - Single update → sequential is faster (no parallelization benefit) | ||
| - Single active child → no parallelization opportunity at this level | ||
| - Both conditions met → multiple independent tasks worth parallelizing | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.