Skip to content

Conversation

@yongkangc
Copy link
Member

@yongkangc yongkangc commented Oct 22, 2025

Closes: #19183

This PR refactors the multiproof pipeline to improve performance and simplify its design by eliminating a spawn_blocking hop for handling proof results.

Previously, the MultiproofManager would spawn a blocking task to await the result of each proof calculation, which introduced unnecessary overhead and complexity.

The new implementation streamlines this process:

  1. Proof workers now send their results (ProofResultMessage) directly to the MultiProofTask event loop via a dedicated crossbeam channel.
  2. The MultiProofTask uses crossbeam::select! to concurrently handle both incoming control messages (like state updates) and the proof results from workers.

This change removes the intermediate blocking task, resulting in a more direct, efficient, and easier-to-understand data flow.

The updated message flow is as follows:

MultiProofTask -> MultiproofManager -> ProofWorkerHandle -> Storage/Account Worker
       ^                                          |
       |                                          v
ProofResultMessage <-------- ProofResultSender ---

@github-project-automation github-project-automation bot moved this to Backlog in Reth Tracker Oct 22, 2025
@yongkangc yongkangc self-assigned this Oct 22, 2025
@yongkangc yongkangc added C-perf A change motivated by improving speed, memory usage or disk footprint S-needs-benchmark This set of changes needs performance benchmarking to double-check that they help labels Oct 22, 2025
Comment on lines -123 to -133
/// Message about completion of proof calculation for a specific state update
#[derive(Debug)]
pub(super) struct ProofCalculated {
/// The index of this proof in the sequence of state updates
sequence_number: u64,
/// Sparse trie update
update: SparseTrieUpdate,
/// The time taken to calculate the proof.
elapsed: Duration,
}

Copy link
Member Author

Choose a reason for hiding this comment

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

We can remove ProofCalculated entirely because the task loop no longer
forwards worker results throughProofCalculated.

Results now arrive as ProofResultMessage over proof_result_rx, and
the loop converts them straight into SparseTrieUpdates inline—no
intermediate wrapper needed.

@yongkangc yongkangc requested a review from Copilot October 22, 2025 08:59
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR eliminates spawn_blocking calls in the multiproof manager by restructuring proof result delivery. Instead of spawning blocking tasks that wait for proof completion, workers now send results directly through a dedicated crossbeam channel to the MultiProofTask event loop.

Key changes:

  • Workers deliver proof results directly via ProofResultMessage through a crossbeam channel
  • MultiProofTask uses crossbeam_channel::select! to handle both state updates and proof results concurrently
  • Removed intermediate blocking tasks and message types (ProofCalculated, ProofCalculationError)

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
crates/trie/parallel/src/proof_task.rs Adds ProofResultMessage type and ProofResultContext for direct result delivery; workers send results via channel instead of returning through receivers
crates/trie/parallel/src/proof.rs Updated to use crossbeam channel for receiving proof results directly from workers
crates/engine/tree/src/tree/payload_processor/multiproof.rs Removed spawn_blocking; workers send results to shared channel; event loop uses select! to handle state updates and proof results
crates/engine/tree/src/tree/payload_processor/prewarm.rs Updated channel type from std mpsc to crossbeam for multiproof messages
crates/engine/tree/src/tree/payload_processor/mod.rs Updated channel types to use crossbeam channels for multiproof messages
crates/engine/tree/Cargo.toml Added crossbeam-channel dependency

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@yongkangc yongkangc requested a review from Copilot October 22, 2025 09:16
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

crates/engine/tree/src/tree/payload_processor/multiproof.rs:463

  • The elapsed variable calculation on line 422 was removed but is still used on line 485. This will cause a compilation error as elapsed is undefined at the point of use in the storage multiproof result message.
            let elapsed = start.elapsed();

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@mattsse
Copy link
Collaborator

mattsse commented Oct 22, 2025

this is looking pretty good already

@yongkangc yongkangc moved this from Backlog to In Progress in Reth Tracker Oct 23, 2025
@yongkangc yongkangc added the A-engine Related to the engine implementation label Oct 23, 2025
@yongkangc yongkangc requested a review from Copilot October 23, 2025 10:56
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@yongkangc yongkangc marked this pull request as ready for review October 23, 2025 11:25
@yongkangc yongkangc requested a review from Copilot October 23, 2025 11:25
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link
Collaborator

@shekhirin shekhirin left a comment

Choose a reason for hiding this comment

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

Will review properly in a bit, but looks good directionally

collect_branch_node_masks: self.collect_branch_node_masks,
multi_added_removed_keys: self.multi_added_removed_keys.clone(),
missed_leaves_storage_roots: self.missed_leaves_storage_roots.clone(),
proof_result_sender: (
Copy link
Collaborator

Choose a reason for hiding this comment

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

this is getting harder to read, let's introduce a separate struct?

Copy link
Member Author

@yongkangc yongkangc Oct 24, 2025

Choose a reason for hiding this comment

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

good suggestion! addressed in 5aa0087

prefetch_proofs_requested,
updates_finished,
) {
crossbeam_channel::select! {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it's a very nice change: we now have a channel that we receive the state updates on, and a separate channel that we receive the proof results on. Much cleaner imo, we can maybe separate the handling of messages into separate methods so it's easier to navigate, but not a blocker

Copy link
Member Author

Choose a reason for hiding this comment

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

this makes sense, let me configure and see how to clean that up

we can maybe separate the handling of messages into separate methods so it's easier to navigate, but not a blocker

Copy link
Member Author

Choose a reason for hiding this comment

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

@shekhirin just did a prototype, i think its easier to review in a separate pr :) noted down in #19182

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.

I like this a lot especially because this removes a totally redundant spawn

imo there's room for cleanup (not in this pr tho) see also #19182

}

/// Spawns a single storage proof calculation task.
fn spawn_storage_proof(&mut self, storage_multiproof_input: StorageMultiproofInput) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd like to rename this now to just send_ removing spawn_ from the fn names because misleading

Copy link
Member Author

@yongkangc yongkangc Oct 24, 2025

Choose a reason for hiding this comment

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

i changed to the hierarchy and naming to dispatch (felt more fitting than send) :

dispatch() // Entry point - checks if empty, then sends
→ send_multiproof_task() // removed shallow function
→ dispatch_storage_proof() // dispatch to storage workers
→ dispatch_multiproof() // dispatch to account workers

what do you think @mattsse ?

prefetch_proofs_requested,
updates_finished,
) {
crossbeam_channel::select! {
Copy link
Collaborator

Choose a reason for hiding this comment

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

all of this looks good to me

}
}
Err(_) => {
error!(target: "engine::tree::payload_processor::multiproof", "Proof result channel closed unexpectedly");
Copy link
Collaborator

Choose a reason for hiding this comment

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

this should be unreachable because always keep the channel open via the

MultiproofManager.proof_result_tx

Copy link
Member Author

Choose a reason for hiding this comment

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

good pt, changed to unreachable! 01f1db8

/// 4. `MultiProofTask` consumes the message from the same channel and sequences it with
/// `ProofSequencer`.
#[derive(Debug)]
pub struct MultiproofManager {
Copy link
Collaborator

Choose a reason for hiding this comment

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

this type now simply acts as container to groups certain fields within the MultiProofTask

I personally find this introduces more complexity because now you also need to know what this entity does even if this now only does simple dispatch, similar situation with the ProofSequencer

all of this could be squashed into the
MultiProofTask

but we can decide this later

Copy link
Member Author

@yongkangc yongkangc Oct 24, 2025

Choose a reason for hiding this comment

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

thanks for the note, noted this down as a cleanup in
#19182

Copy link
Member Author

Choose a reason for hiding this comment

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

receiving the ProofCalculated in multiproof manager
This is incorrect. we should get rid of them

@yongkangc yongkangc moved this from In Progress to In Review in Reth Tracker Oct 24, 2025
@yongkangc yongkangc requested a review from shekhirin October 24, 2025 07:08
Copy link
Collaborator

@shekhirin shekhirin left a comment

Choose a reason for hiding this comment

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

Very nice, LGTM

proof_sequence_number,
state_root_message_sender,
multi_added_removed_keys,
..
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit, to be the same as in dispatch_storage_proof

Suggested change
..
state_root_message_sender: _,

Copy link
Member Author

Choose a reason for hiding this comment

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

makes sense! resolved

break
}
}
MultiProofMessage::EmptyProof { sequence_number, state } => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

something is going on with indentation here, rustfmt gave up I guess

Copy link
Collaborator

Choose a reason for hiding this comment

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

@yongkangc I think we may need to fix this manually, idk why rustfmt doesn't do it for us

proof_targets: MultiProofTargets,
proof_sequence_number: u64,
state_root_message_sender: Sender<MultiProofMessage>,
state_root_message_sender: CrossbeamSender<MultiProofMessage>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

now it's only used for EmptyProof message, I wonder if we can remove it altogether and just send the empty proof to proof_result_tx? No need to address it in this PR, just a thought cc @mattsse

Copy link
Collaborator

Choose a reason for hiding this comment

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

makes sense

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.

one last question otherwise lgtm

proof_sequence_number,
state_root_message_sender,
multi_added_removed_keys,
state_root_message_sender: _,
Copy link
Collaborator

Choose a reason for hiding this comment

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

yeah this feels a bit weird because we first clone stuff from self into the

StorageMultiproofInput only to drop it here

imo this entire type StorageMultiproofInput is redundant

Comment on lines 1170 to 1174
// SAFETY: This is unreachable because `self.multiproof_manager` owns
// `proof_result_tx` (the sender), which keeps the channel open for
// the entire lifetime of this task. The channel cannot close while
// `self` exists.
unreachable!("proof result channel closed while multiproof manager still exists")
Copy link
Collaborator

Choose a reason for hiding this comment

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

this checks out because select does return RecvError and not TryRecvError so this is only reachable if the channel is disconnected.

tho maybe we should just return the an error here

Comment on lines +661 to +668
// Extract storage proof from the multiproof wrapper
let (mut multiproof, _stats) = proof_msg.result?;
let proof =
multiproof.storages.remove(&hashed_address).ok_or_else(|| {
ParallelStateRootError::Other(format!(
"storage proof not found in multiproof for {hashed_address}"
))
})?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

why do we need to do this now?

this is added a few times and unclear what changed here?

if let Ok(proof_msg) = receiver.recv() {
// Extract storage proof from the multiproof wrapper
if let Ok((mut multiproof, _stats)) = proof_msg.result &&
let Some(proof) = multiproof.storages.remove(&hashed_address)
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here

@github-project-automation github-project-automation bot moved this from In Review to In Progress in Reth Tracker Oct 24, 2025
Err(e) => Err(e),
};

// Send result directly to MultiProofTask
Copy link
Collaborator

Choose a reason for hiding this comment

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

let's remove this comment, because this codepath is also used from ParallelProof::storage_proof

Copy link
Member Author

Choose a reason for hiding this comment

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

@shekhirin removed comment

@yongkangc yongkangc enabled auto-merge October 24, 2025 11:53
@yongkangc yongkangc added this pull request to the merge queue Oct 24, 2025
Merged via the queue into main with commit f29f4ca Oct 24, 2025
39 of 40 checks passed
@yongkangc yongkangc deleted the yk/mpm_wiring branch October 24, 2025 12:13
@github-project-automation github-project-automation bot moved this from In Progress to Done in Reth Tracker Oct 24, 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 C-perf A change motivated by improving speed, memory usage or disk footprint S-needs-benchmark This set of changes needs performance benchmarking to double-check that they help

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

perf: remove spawn_blocking for workers in multiproof

3 participants