mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-16 11:22:56 +00:00
Deduplicate block root computation (#3590)
## Issue Addressed NA ## Proposed Changes This PR removes duplicated block root computation. Computing the `SignedBeaconBlock::canonical_root` has become more expensive since the merge as we need to compute the merke root of each transaction inside an `ExecutionPayload`. Computing the root for [a mainnet block](https://beaconcha.in/slot/4704236) is taking ~10ms on my i7-8700K CPU @ 3.70GHz (no sha extensions). Given that our median seen-to-imported time for blocks is presently 300-400ms, removing a few duplicated block roots (~30ms) could represent an easy 10% improvement. When we consider that the seen-to-imported times include operations *after* the block has been placed in the early attester cache, we could expect the 30ms to be more significant WRT our seen-to-attestable times. ## Additional Info NA
This commit is contained in:
@@ -489,6 +489,7 @@ impl<T: BeaconChainTypes> WorkEvent<T> {
|
||||
/// Create a new `Work` event for some block, where the result from computation (if any) is
|
||||
/// sent to the other side of `result_tx`.
|
||||
pub fn rpc_beacon_block(
|
||||
block_root: Hash256,
|
||||
block: Arc<SignedBeaconBlock<T::EthSpec>>,
|
||||
seen_timestamp: Duration,
|
||||
process_type: BlockProcessType,
|
||||
@@ -496,6 +497,7 @@ impl<T: BeaconChainTypes> WorkEvent<T> {
|
||||
Self {
|
||||
drop_during_sync: false,
|
||||
work: Work::RpcBlock {
|
||||
block_root,
|
||||
block,
|
||||
seen_timestamp,
|
||||
process_type,
|
||||
@@ -577,6 +579,7 @@ impl<T: BeaconChainTypes> std::convert::From<ReadyWork<T>> for WorkEvent<T> {
|
||||
},
|
||||
},
|
||||
ReadyWork::RpcBlock(QueuedRpcBlock {
|
||||
block_root,
|
||||
block,
|
||||
seen_timestamp,
|
||||
process_type,
|
||||
@@ -584,6 +587,7 @@ impl<T: BeaconChainTypes> std::convert::From<ReadyWork<T>> for WorkEvent<T> {
|
||||
}) => Self {
|
||||
drop_during_sync: false,
|
||||
work: Work::RpcBlock {
|
||||
block_root,
|
||||
block,
|
||||
seen_timestamp,
|
||||
process_type,
|
||||
@@ -705,6 +709,7 @@ pub enum Work<T: BeaconChainTypes> {
|
||||
seen_timestamp: Duration,
|
||||
},
|
||||
RpcBlock {
|
||||
block_root: Hash256,
|
||||
block: Arc<SignedBeaconBlock<T::EthSpec>>,
|
||||
seen_timestamp: Duration,
|
||||
process_type: BlockProcessType,
|
||||
@@ -1532,11 +1537,13 @@ impl<T: BeaconChainTypes> BeaconProcessor<T> {
|
||||
* Verification for beacon blocks received during syncing via RPC.
|
||||
*/
|
||||
Work::RpcBlock {
|
||||
block_root,
|
||||
block,
|
||||
seen_timestamp,
|
||||
process_type,
|
||||
should_process,
|
||||
} => task_spawner.spawn_async(worker.process_rpc_block(
|
||||
block_root,
|
||||
block,
|
||||
seen_timestamp,
|
||||
process_type,
|
||||
|
||||
@@ -242,6 +242,7 @@ impl TestRig {
|
||||
|
||||
pub fn enqueue_rpc_block(&self) {
|
||||
let event = WorkEvent::rpc_beacon_block(
|
||||
self.next_block.canonical_root(),
|
||||
self.next_block.clone(),
|
||||
std::time::Duration::default(),
|
||||
BlockProcessType::ParentLookup {
|
||||
@@ -253,6 +254,7 @@ impl TestRig {
|
||||
|
||||
pub fn enqueue_single_lookup_rpc_block(&self) {
|
||||
let event = WorkEvent::rpc_beacon_block(
|
||||
self.next_block.canonical_root(),
|
||||
self.next_block.clone(),
|
||||
std::time::Duration::default(),
|
||||
BlockProcessType::SingleBlock { id: 1 },
|
||||
|
||||
@@ -109,6 +109,7 @@ pub struct QueuedGossipBlock<T: BeaconChainTypes> {
|
||||
/// A block that arrived for processing when the same block was being imported over gossip.
|
||||
/// It is queued for later import.
|
||||
pub struct QueuedRpcBlock<T: EthSpec> {
|
||||
pub block_root: Hash256,
|
||||
pub block: Arc<SignedBeaconBlock<T>>,
|
||||
pub process_type: BlockProcessType,
|
||||
pub seen_timestamp: Duration,
|
||||
|
||||
@@ -713,16 +713,28 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
block_delay,
|
||||
);
|
||||
|
||||
let verification_result = self
|
||||
.chain
|
||||
.clone()
|
||||
.verify_block_for_gossip(block.clone())
|
||||
.await;
|
||||
|
||||
let block_root = if let Ok(verified_block) = &verification_result {
|
||||
verified_block.block_root
|
||||
} else {
|
||||
block.canonical_root()
|
||||
};
|
||||
|
||||
// Write the time the block was observed into delay cache.
|
||||
self.chain.block_times_cache.write().set_time_observed(
|
||||
block.canonical_root(),
|
||||
block_root,
|
||||
block.slot(),
|
||||
seen_duration,
|
||||
Some(peer_id.to_string()),
|
||||
Some(peer_client.to_string()),
|
||||
);
|
||||
|
||||
let verified_block = match self.chain.clone().verify_block_for_gossip(block).await {
|
||||
let verified_block = match verification_result {
|
||||
Ok(verified_block) => {
|
||||
if block_delay >= self.chain.slot_clock.unagg_attestation_production_delay() {
|
||||
metrics::inc_counter(&metrics::BEACON_BLOCK_GOSSIP_ARRIVED_LATE_TOTAL);
|
||||
@@ -762,9 +774,9 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
debug!(
|
||||
self.log,
|
||||
"Unknown parent for gossip block";
|
||||
"root" => ?block.canonical_root()
|
||||
"root" => ?block_root
|
||||
);
|
||||
self.send_sync_message(SyncMessage::UnknownBlock(peer_id, block));
|
||||
self.send_sync_message(SyncMessage::UnknownBlock(peer_id, block, block_root));
|
||||
return None;
|
||||
}
|
||||
Err(e @ BlockError::BeaconChainError(_)) => {
|
||||
@@ -918,10 +930,11 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
_seen_duration: Duration,
|
||||
) {
|
||||
let block: Arc<_> = verified_block.block.clone();
|
||||
let block_root = verified_block.block_root;
|
||||
|
||||
match self
|
||||
.chain
|
||||
.process_block(verified_block, CountUnrealized::True)
|
||||
.process_block(block_root, verified_block, CountUnrealized::True)
|
||||
.await
|
||||
{
|
||||
Ok(block_root) => {
|
||||
@@ -956,7 +969,7 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
"Block with unknown parent attempted to be processed";
|
||||
"peer_id" => %peer_id
|
||||
);
|
||||
self.send_sync_message(SyncMessage::UnknownBlock(peer_id, block));
|
||||
self.send_sync_message(SyncMessage::UnknownBlock(peer_id, block, block_root));
|
||||
}
|
||||
Err(ref e @ BlockError::ExecutionPayloadError(ref epe)) if !epe.penalize_peer() => {
|
||||
debug!(
|
||||
@@ -970,7 +983,7 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
self.log,
|
||||
"Invalid gossip beacon block";
|
||||
"outcome" => ?other,
|
||||
"block root" => ?block.canonical_root(),
|
||||
"block root" => ?block_root,
|
||||
"block slot" => block.slot()
|
||||
);
|
||||
self.gossip_penalize_peer(
|
||||
|
||||
@@ -38,8 +38,10 @@ struct ChainSegmentFailed {
|
||||
|
||||
impl<T: BeaconChainTypes> Worker<T> {
|
||||
/// Attempt to process a block received from a direct RPC request.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn process_rpc_block(
|
||||
self,
|
||||
block_root: Hash256,
|
||||
block: Arc<SignedBeaconBlock<T::EthSpec>>,
|
||||
seen_timestamp: Duration,
|
||||
process_type: BlockProcessType,
|
||||
@@ -56,17 +58,18 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
return;
|
||||
}
|
||||
// Check if the block is already being imported through another source
|
||||
let handle = match duplicate_cache.check_and_insert(block.canonical_root()) {
|
||||
let handle = match duplicate_cache.check_and_insert(block_root) {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
debug!(
|
||||
self.log,
|
||||
"Gossip block is being processed";
|
||||
"action" => "sending rpc block to reprocessing queue",
|
||||
"block_root" => %block.canonical_root(),
|
||||
"block_root" => %block_root,
|
||||
);
|
||||
// Send message to work reprocess queue to retry the block
|
||||
let reprocess_msg = ReprocessQueueMessage::RpcBlock(QueuedRpcBlock {
|
||||
block_root,
|
||||
block: block.clone(),
|
||||
process_type,
|
||||
seen_timestamp,
|
||||
@@ -74,13 +77,16 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
});
|
||||
|
||||
if reprocess_tx.try_send(reprocess_msg).is_err() {
|
||||
error!(self.log, "Failed to inform block import"; "source" => "rpc", "block_root" => %block.canonical_root())
|
||||
error!(self.log, "Failed to inform block import"; "source" => "rpc", "block_root" => %block_root)
|
||||
};
|
||||
return;
|
||||
}
|
||||
};
|
||||
let slot = block.slot();
|
||||
let result = self.chain.process_block(block, CountUnrealized::True).await;
|
||||
let result = self
|
||||
.chain
|
||||
.process_block(block_root, block, CountUnrealized::True)
|
||||
.await;
|
||||
|
||||
metrics::inc_counter(&metrics::BEACON_PROCESSOR_RPC_BLOCK_IMPORTED_TOTAL);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user