mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-02 16:21:42 +00:00
Optimise out block header calculation (#8446)
This is a `tracing`-driven optimisation. While investigating why Lighthouse is slow to send `newPayload`, I found a suspicious 13ms of computation on the hot path in `gossip_block_into_execution_pending_block_slashable`: <img width="1998" height="1022" alt="headercalc" src="https://github.com/user-attachments/assets/e4f88c1a-da23-47b4-b533-cf5479a1c55c" /> Looking at the current implementation we can see that the _only_ thing that happens prior to calling into `from_gossip_verified_block` is the calculation of a `header`. We first call `SignatureVerifiedBlock::from_gossip_verified_block_check_slashable`:261322c3e3/beacon_node/beacon_chain/src/block_verification.rs (L1075-L1076)Which is where the `header` is calculated prior to calling `from_gossip_verified_block`:261322c3e3/beacon_node/beacon_chain/src/block_verification.rs (L1224-L1226)Notice that the `header` is _only_ used in the case of an error, yet we spend time computing it every time! This PR moves the calculation of the header (which involves hashing the whole beacon block, including the execution payload), into the error case. We take a cheap clone of the `Arc`'d beacon block on the hot path, and use this for calculating the header _only_ in the case an error actually occurs. This shaves 10-20ms off our pre-newPayload delays, and 10-20ms off every block processing 🎉 Co-Authored-By: Michael Sproul <michael@sigmaprime.io>
This commit is contained in:
@@ -1164,9 +1164,9 @@ impl<T: BeaconChainTypes> SignatureVerifiedBlock<T> {
|
||||
block_root: Hash256,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<Self, BlockSlashInfo<BlockError>> {
|
||||
let header = block.signed_block_header();
|
||||
let arc_block = block.block_cloned();
|
||||
Self::new(block, block_root, chain)
|
||||
.map_err(|e| BlockSlashInfo::from_early_error_block(header, e))
|
||||
.map_err(|e| BlockSlashInfo::from_early_error_block(arc_block.signed_block_header(), e))
|
||||
}
|
||||
|
||||
/// Finishes signature verification on the provided `GossipVerifedBlock`. Does not re-verify
|
||||
@@ -1221,9 +1221,13 @@ impl<T: BeaconChainTypes> SignatureVerifiedBlock<T> {
|
||||
from: GossipVerifiedBlock<T>,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<Self, BlockSlashInfo<BlockError>> {
|
||||
let header = from.block.signed_block_header();
|
||||
Self::from_gossip_verified_block(from, chain)
|
||||
.map_err(|e| BlockSlashInfo::from_early_error_block(header, e))
|
||||
let block = from.block.clone();
|
||||
Self::from_gossip_verified_block(from, chain).map_err(|e| {
|
||||
// Lazily create the header from the block in case of error. Computing the header
|
||||
// involves some hashing and takes ~13ms which we DO NOT want to do on the hot path of
|
||||
// block processing (prior to sending newPayload pre-Gloas).
|
||||
BlockSlashInfo::from_early_error_block(block.signed_block_header(), e)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn block_root(&self) -> Hash256 {
|
||||
@@ -1248,12 +1252,12 @@ impl<T: BeaconChainTypes> IntoExecutionPendingBlock<T> for SignatureVerifiedBloc
|
||||
chain: &Arc<BeaconChain<T>>,
|
||||
notify_execution_layer: NotifyExecutionLayer,
|
||||
) -> Result<ExecutionPendingBlock<T>, BlockSlashInfo<BlockError>> {
|
||||
let header = self.block.signed_block_header();
|
||||
let arc_block = self.block.block_cloned();
|
||||
let (parent, block) = if let Some(parent) = self.parent {
|
||||
(parent, self.block)
|
||||
} else {
|
||||
load_parent(self.block, chain)
|
||||
.map_err(|e| BlockSlashInfo::SignatureValid(header.clone(), e))?
|
||||
.map_err(|e| BlockSlashInfo::SignatureValid(arc_block.signed_block_header(), e))?
|
||||
};
|
||||
|
||||
ExecutionPendingBlock::from_signature_verified_components(
|
||||
@@ -1264,7 +1268,7 @@ impl<T: BeaconChainTypes> IntoExecutionPendingBlock<T> for SignatureVerifiedBloc
|
||||
chain,
|
||||
notify_execution_layer,
|
||||
)
|
||||
.map_err(|e| BlockSlashInfo::SignatureValid(header, e))
|
||||
.map_err(|e| BlockSlashInfo::SignatureValid(arc_block.signed_block_header(), e))
|
||||
}
|
||||
|
||||
fn block(&self) -> &SignedBeaconBlock<T::EthSpec> {
|
||||
|
||||
@@ -8,6 +8,7 @@ use ssz_derive::{Decode, Encode};
|
||||
use std::fmt;
|
||||
use superstruct::superstruct;
|
||||
use test_random_derive::TestRandom;
|
||||
use tracing::instrument;
|
||||
use tree_hash::TreeHash;
|
||||
use tree_hash_derive::TreeHash;
|
||||
|
||||
@@ -253,6 +254,7 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedBeaconBlock<E, Payload>
|
||||
}
|
||||
|
||||
/// Produce a signed beacon block header corresponding to this block.
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub fn signed_block_header(&self) -> SignedBeaconBlockHeader {
|
||||
SignedBeaconBlockHeader {
|
||||
message: self.message().block_header(),
|
||||
|
||||
Reference in New Issue
Block a user