Skip to content
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

Introduce clone-able state provider builder for tree state #14136

Closed
Rjected opened this issue Jan 31, 2025 · 2 comments · Fixed by #14279
Closed

Introduce clone-able state provider builder for tree state #14136

Rjected opened this issue Jan 31, 2025 · 2 comments · Fixed by #14279
Assignees
Labels
A-engine Related to the engine implementation C-enhancement New feature or request C-perf A change motivated by improving speed, memory usage or disk footprint

Comments

@Rjected
Copy link
Member

Rjected commented Jan 31, 2025

Creating a state provider can take anywhere from 20us to 200us, with average time for each prewarm transaction being ~70 microseconds. If the block has 150 transaction, creating these state providers in a loop can cost us about 10.5 milliseconds.

The way to fix this latency is to create the state provider in the thread rather than in the critical path.

This means creating a state provider builder, that can be returned from the tree. This should clone the tree's provider field:

And call blocks_by_hash:

/// Returns all available blocks for the given hash that lead back to the canonical chain, from
/// newest to oldest. And the parent hash of the oldest block that is missing from the buffer.
///
/// Returns `None` if the block for the given hash is not found.
fn blocks_by_hash(&self, hash: B256) -> Option<(B256, Vec<ExecutedBlockWithTrieUpdates<N>>)> {
let block = self.blocks_by_hash.get(&hash).cloned()?;
let mut parent_hash = block.recovered_block().parent_hash();
let mut blocks = vec![block];
while let Some(executed) = self.blocks_by_hash.get(&parent_hash) {
parent_hash = executed.recovered_block().parent_hash();
blocks.push(executed.clone());
}
Some((parent_hash, blocks))
}

To construct the builder data structure.

This data structure should have a method for creating a state provider:

/// Returns the state provider for the requested block hash.
///
/// This merges the state of all blocks that are part of the chain that the requested block is
/// the head of and are not yet persisted on disk. This includes all blocks that connect back to
/// a canonical block on disk.
///
/// Returns `None` if the state for the requested hash is not found, this happens if the
/// requested state belongs to a block that is not connected to the canonical chain.
///
/// Returns an error if we failed to fetch the state from the database.
fn state_provider(&self, hash: B256) -> ProviderResult<Option<StateProviderBox>> {
if let Some((historical, blocks)) = self.state.tree_state.blocks_by_hash(hash) {
debug!(target: "engine::tree", %hash, %historical, "found canonical state for block in memory");
// the block leads back to the canonical chain
let historical = self.provider.state_by_block_hash(historical)?;
return Ok(Some(Box::new(MemoryOverlayStateProvider::new(historical, blocks))))
}
// the hash could belong to an unknown block or a persisted block
if let Some(header) = self.provider.header(&hash)? {
debug!(target: "engine::tree", %hash, number = %header.number(), "found canonical state for block in database");
// the block is known and persisted
let historical = self.provider.state_by_block_hash(hash)?;
return Ok(Some(historical))
}
debug!(target: "engine::tree", %hash, "no canonical state found for block");
Ok(None)
}

This can then be called in each prewarm thread to create the state provider.

@Rjected Rjected added A-engine Related to the engine implementation C-enhancement New feature or request C-perf A change motivated by improving speed, memory usage or disk footprint labels Jan 31, 2025
@namn-grg
Copy link
Contributor

namn-grg commented Feb 5, 2025

Hi, interesting issue can I work on this?

@Rjected
Copy link
Member Author

Rjected commented Feb 5, 2025

@namn-grg sure, assigned, please ping me when you have a PR or if you have more questions

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-enhancement New feature or request C-perf A change motivated by improving speed, memory usage or disk footprint
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

2 participants