This repository has been archived by the owner on Nov 6, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Less cloning when importing blocks #11531
Merged
Merged
Changes from 3 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
48264be
WIP
dvdplm dfeec91
cleanup
dvdplm ef1b059
check_and_lock_block() only needs reference
dvdplm 80c7a03
Cleanup&docs
dvdplm 9ed5dec
Push uncles by ref to clone at the last possible time.
dvdplm 05ec463
Missing import
dvdplm 08be43b
Review grumbles
dvdplm 025adb7
Update util/journaldb/src/overlayrecentdb.rs
dvdplm e64b691
Merge branch 'master' into dp/perf/less-cloning-when-importing-blocks
dvdplm b286035
Update ethcore/types/src/client_types.rs
dvdplm 9b1f080
deref U256 before adding
dvdplm 2106ad9
More review grumbles
dvdplm 0b56f30
review grumbles: pass by value
dvdplm d5d2e07
cleanup
dvdplm 35e4c50
Move the block
dvdplm f217876
Don't clone the header
dvdplm 004bd0b
Update ethcore/src/client/client.rs
dvdplm bcd3d3a
Update ethcore/src/client/client.rs
dvdplm ea2c2bf
Add comment
dvdplm 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 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 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 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 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 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 |
---|---|---|
|
@@ -283,10 +283,9 @@ impl Importer { | |
} | ||
|
||
let max_blocks_to_import = client.config.max_round_blocks_to_import; | ||
let (imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, duration, has_more_blocks_to_import) = { | ||
let (imported_blocks, import_results, invalid_blocks, imported, duration, has_more_blocks_to_import) = { | ||
let mut imported_blocks = Vec::with_capacity(max_blocks_to_import); | ||
let mut invalid_blocks = HashSet::new(); | ||
let proposed_blocks = Vec::with_capacity(max_blocks_to_import); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this was actually not ever used? Nice catch 👍 |
||
let mut import_results = Vec::with_capacity(max_blocks_to_import); | ||
|
||
let _import_lock = self.import_lock.lock(); | ||
|
@@ -298,26 +297,25 @@ impl Importer { | |
let start = Instant::now(); | ||
|
||
for block in blocks { | ||
let header = block.header.clone(); | ||
let bytes = block.bytes.clone(); | ||
let hash = header.hash(); | ||
let hash = block.header.hash(); | ||
|
||
let is_invalid = invalid_blocks.contains(header.parent_hash()); | ||
let is_invalid = invalid_blocks.contains(block.header.parent_hash()); | ||
if is_invalid { | ||
invalid_blocks.insert(hash); | ||
continue; | ||
} | ||
|
||
match self.check_and_lock_block(&bytes, block, client) { | ||
Ok((closed_block, pending)) => { | ||
match self.check_and_lock_block(&block, client) { | ||
Ok((locked_block, pending)) => { | ||
imported_blocks.push(hash); | ||
let transactions_len = closed_block.transactions.len(); | ||
let route = self.commit_block(closed_block, &header, encoded::Block::new(bytes), pending, client); | ||
let transactions_len = locked_block.transactions.len(); | ||
let gas_used = locked_block.header.gas_used().clone(); | ||
dvdplm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let route = self.commit_block(locked_block, encoded::Block::new(block.bytes), pending, client); | ||
import_results.push(route); | ||
client.report.write().accrue_block(&header, transactions_len); | ||
}, | ||
client.report.write().accrue_block(&gas_used, transactions_len); | ||
} | ||
Err(err) => { | ||
self.bad_blocks.report(bytes, format!("{:?}", err)); | ||
self.bad_blocks.report(block.bytes, format!("{:?}", err)); | ||
invalid_blocks.insert(hash); | ||
}, | ||
} | ||
|
@@ -330,9 +328,9 @@ impl Importer { | |
self.block_queue.mark_as_bad(&invalid_blocks); | ||
} | ||
let has_more_blocks_to_import = !self.block_queue.mark_as_good(&imported_blocks); | ||
(imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, start.elapsed(), has_more_blocks_to_import) | ||
(imported_blocks, import_results, invalid_blocks, imported, start.elapsed(), has_more_blocks_to_import) | ||
}; | ||
|
||
trace!(target: "dp", "[import_verified_blocks] {} blocks processed in {:?}", imported, duration); | ||
{ | ||
if !imported_blocks.is_empty() { | ||
let route = ChainRoute::from(import_results.as_ref()); | ||
|
@@ -348,23 +346,27 @@ impl Importer { | |
invalid_blocks.clone(), | ||
route.clone(), | ||
Vec::new(), | ||
proposed_blocks.clone(), | ||
Vec::new(), | ||
duration, | ||
has_more_blocks_to_import, | ||
) | ||
); | ||
}); | ||
} | ||
} | ||
|
||
let start = Instant::now(); | ||
let db = client.db.read(); | ||
db.key_value().flush().expect("DB flush failed."); | ||
trace!(target: "dp", "[import_verified_blocks] flushed the db in: {:?}", | ||
dvdplm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
start.elapsed() | ||
); | ||
|
||
imported | ||
} | ||
|
||
fn check_and_lock_block(&self, bytes: &[u8], block: PreverifiedBlock, client: &Client) -> EthcoreResult<(LockedBlock, Option<PendingTransition>)> { | ||
fn check_and_lock_block(&self, block: &PreverifiedBlock, client: &Client) -> EthcoreResult<(LockedBlock, Option<PendingTransition>)> { | ||
let engine = &*self.engine; | ||
let header = block.header.clone(); | ||
let header = &block.header; | ||
|
||
// Check the block isn't so old we won't be able to enact it. | ||
let best_block_number = client.chain.read().best_block_number(); | ||
|
@@ -389,7 +391,7 @@ impl Importer { | |
&parent, | ||
engine, | ||
verification::FullFamilyParams { | ||
block: &block, | ||
block, | ||
block_provider: &**chain, | ||
client | ||
}, | ||
|
@@ -448,7 +450,6 @@ impl Importer { | |
|
||
let pending = self.check_epoch_end_signal( | ||
&header, | ||
bytes, | ||
&locked_block.receipts, | ||
locked_block.state.db(), | ||
client | ||
|
@@ -490,22 +491,22 @@ impl Importer { | |
fn commit_block<B>( | ||
&self, | ||
block: B, | ||
header: &Header, | ||
block_data: encoded::Block, | ||
pending: Option<PendingTransition>, | ||
client: &Client | ||
) -> ImportRoute | ||
where B: Drain | ||
{ | ||
let block = block.drain(); | ||
let header = block.header; | ||
let hash = &header.hash(); | ||
let number = header.number(); | ||
let parent = header.parent_hash(); | ||
let chain = client.chain.read(); | ||
let mut is_finalized = false; | ||
|
||
// Commit results | ||
let block = block.drain(); | ||
debug_assert_eq!(header.hash(), block_data.header_view().hash()); | ||
debug_assert_eq!(*hash, block_data.header_view().hash()); | ||
|
||
let mut batch = DBTransaction::new(); | ||
|
||
|
@@ -543,15 +544,15 @@ impl Importer { | |
// check epoch end signal, potentially generating a proof on the current | ||
// state. | ||
if let Some(pending) = pending { | ||
chain.insert_pending_transition(&mut batch, header.hash(), pending); | ||
chain.insert_pending_transition(&mut batch, hash, pending); | ||
} | ||
|
||
state.journal_under(&mut batch, number, hash).expect("DB commit failed"); | ||
|
||
let finalized: Vec<_> = ancestry_actions.into_iter().map(|ancestry_action| { | ||
let AncestryAction::MarkFinalized(a) = ancestry_action; | ||
|
||
if a != header.hash() { | ||
if a != *hash { | ||
chain.mark_finalized(&mut batch, a).expect("Engine's ancestry action must be known blocks; qed"); | ||
} else { | ||
// we're finalizing the current block | ||
|
@@ -596,7 +597,6 @@ impl Importer { | |
fn check_epoch_end_signal( | ||
&self, | ||
header: &Header, | ||
block_bytes: &[u8], | ||
receipts: &[Receipt], | ||
state_db: &StateDB, | ||
client: &Client, | ||
|
@@ -605,7 +605,6 @@ impl Importer { | |
|
||
let hash = header.hash(); | ||
let auxiliary = AuxiliaryData { | ||
bytes: Some(block_bytes), | ||
receipts: Some(&receipts), | ||
}; | ||
|
||
|
@@ -1065,14 +1064,15 @@ impl Client { | |
}; | ||
|
||
self.block_header(id).and_then(|header| { | ||
let db = self.state_db.read().boxed_clone(); | ||
|
||
let state_db = self.state_db.read(); | ||
dvdplm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// early exit for pruned blocks | ||
if db.is_prunable() && self.pruning_info().earliest_state > block_number { | ||
// if db.is_prunable() && self.pruning_info().earliest_state > block_number { | ||
if state_db.is_prunable() && self.pruning_info().earliest_state > block_number { | ||
trace!(target: "client", "State for block #{} is pruned. Earliest state: {:?}", block_number, self.pruning_info().earliest_state); | ||
return None; | ||
} | ||
|
||
let db = state_db.boxed_clone(); | ||
let root = header.state_root(); | ||
State::from_existing(db, root, self.engine.account_start_nonce(block_number), self.factories.clone()).ok() | ||
}) | ||
|
@@ -2402,14 +2402,14 @@ impl ImportSealedBlock for Client { | |
|
||
let pending = self.importer.check_epoch_end_signal( | ||
&header, | ||
&block_bytes, | ||
// &block_bytes, | ||
&block.receipts, | ||
block.state.db(), | ||
self | ||
)?; | ||
let route = self.importer.commit_block( | ||
block, | ||
&header, | ||
// &header, | ||
encoded::Block::new(block_bytes), | ||
pending, | ||
self | ||
|
This file contains 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
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rationale behind this change?
The transaction is still cloned if the else branch is taken, are you doing this because of saving an allocation if the if branch is taken?
Not sure how this plays along w.r.t. to
speculative execution
on modern CPUsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, which
else
-branch do you mean?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant the implicit else branch, i.e if https://github.com/OpenEthereum/open-ethereum/pull/11531/files#diff-e2e171033e005c3b329818aaff213767R172-R174 is missed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, well there's that also, but mainly passing the tx by ref is fallout from a borrow from higher up in the call chain, in
enact_verified()
. (I was also unsure wth theinto()
was doing).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
into()
was not doing anythingThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also not sure why we need this change, if anything, it makes more allocations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in
import_verified_blocks()
we callcheck_and_lock_block()
passing in an ownedPreverifiedBlock
, so to make the rest of the code work there we do some cloning that didn't seem necessary and that's where this PR started.When we call
enact_verified()
we move the block but what is actually needed forenact()
is actually the header, transactions and the (max) 2 uncles.What I'd really would like to do is have a way to split up the
PreverifiedBlock
into (owned) components and be able to move them independently. I don't know of a way to do that, and that's why I ended up with this: it avoids cloning the blockbytes
inimport_verified_blocks()
and a fewHeader
structs, and passes references around instead. Maybe this mean more clones, as each transaction that is added to the block is cloned, but they are also smaller (I hope).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you could use some mem::take tricks, for example, replace Bytes with an empty Vec.
But, why can't you take
PreverifiedBlock
by value instead inenact
? As I did here: https://github.com/OpenEthereum/open-ethereum/compare/na-ethcore-blockerror#diff-e2e171033e005c3b329818aaff213767R411-R564I'm not saying that it is elegant but it worked for me at least :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole thing reminds me of something :P
Seriously though,
create a type for parts or pass them directly instead of
PreferifiedBlock
? or what Niklas suggestedThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, thank you for the input, much appreciated. At the end I went with
mem::take()
-ing the block RLP bytes and after some further shuffling things around I think I have something decent.