Skip to content

Commit 0d0232e

Browse files
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`: https://github.com/sigp/lighthouse/blob/261322c3e3ee467c9454fa160a00866439cbc62f/beacon_node/beacon_chain/src/block_verification.rs#L1075-L1076 Which is where the `header` is calculated prior to calling `from_gossip_verified_block`: https://github.com/sigp/lighthouse/blob/261322c3e3ee467c9454fa160a00866439cbc62f/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>
1 parent 2ba8a8e commit 0d0232e

File tree

2 files changed

+14
-8
lines changed

2 files changed

+14
-8
lines changed

beacon_node/beacon_chain/src/block_verification.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,9 +1164,9 @@ impl<T: BeaconChainTypes> SignatureVerifiedBlock<T> {
11641164
block_root: Hash256,
11651165
chain: &BeaconChain<T>,
11661166
) -> Result<Self, BlockSlashInfo<BlockError>> {
1167-
let header = block.signed_block_header();
1167+
let arc_block = block.block_cloned();
11681168
Self::new(block, block_root, chain)
1169-
.map_err(|e| BlockSlashInfo::from_early_error_block(header, e))
1169+
.map_err(|e| BlockSlashInfo::from_early_error_block(arc_block.signed_block_header(), e))
11701170
}
11711171

11721172
/// Finishes signature verification on the provided `GossipVerifedBlock`. Does not re-verify
@@ -1221,9 +1221,13 @@ impl<T: BeaconChainTypes> SignatureVerifiedBlock<T> {
12211221
from: GossipVerifiedBlock<T>,
12221222
chain: &BeaconChain<T>,
12231223
) -> Result<Self, BlockSlashInfo<BlockError>> {
1224-
let header = from.block.signed_block_header();
1225-
Self::from_gossip_verified_block(from, chain)
1226-
.map_err(|e| BlockSlashInfo::from_early_error_block(header, e))
1224+
let block = from.block.clone();
1225+
Self::from_gossip_verified_block(from, chain).map_err(|e| {
1226+
// Lazily create the header from the block in case of error. Computing the header
1227+
// involves some hashing and takes ~13ms which we DO NOT want to do on the hot path of
1228+
// block processing (prior to sending newPayload pre-Gloas).
1229+
BlockSlashInfo::from_early_error_block(block.signed_block_header(), e)
1230+
})
12271231
}
12281232

12291233
pub fn block_root(&self) -> Hash256 {
@@ -1248,12 +1252,12 @@ impl<T: BeaconChainTypes> IntoExecutionPendingBlock<T> for SignatureVerifiedBloc
12481252
chain: &Arc<BeaconChain<T>>,
12491253
notify_execution_layer: NotifyExecutionLayer,
12501254
) -> Result<ExecutionPendingBlock<T>, BlockSlashInfo<BlockError>> {
1251-
let header = self.block.signed_block_header();
1255+
let arc_block = self.block.block_cloned();
12521256
let (parent, block) = if let Some(parent) = self.parent {
12531257
(parent, self.block)
12541258
} else {
12551259
load_parent(self.block, chain)
1256-
.map_err(|e| BlockSlashInfo::SignatureValid(header.clone(), e))?
1260+
.map_err(|e| BlockSlashInfo::SignatureValid(arc_block.signed_block_header(), e))?
12571261
};
12581262

12591263
ExecutionPendingBlock::from_signature_verified_components(
@@ -1264,7 +1268,7 @@ impl<T: BeaconChainTypes> IntoExecutionPendingBlock<T> for SignatureVerifiedBloc
12641268
chain,
12651269
notify_execution_layer,
12661270
)
1267-
.map_err(|e| BlockSlashInfo::SignatureValid(header, e))
1271+
.map_err(|e| BlockSlashInfo::SignatureValid(arc_block.signed_block_header(), e))
12681272
}
12691273

12701274
fn block(&self) -> &SignedBeaconBlock<T::EthSpec> {

consensus/types/src/signed_beacon_block.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use ssz_derive::{Decode, Encode};
88
use std::fmt;
99
use superstruct::superstruct;
1010
use test_random_derive::TestRandom;
11+
use tracing::instrument;
1112
use tree_hash::TreeHash;
1213
use tree_hash_derive::TreeHash;
1314

@@ -253,6 +254,7 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedBeaconBlock<E, Payload>
253254
}
254255

255256
/// Produce a signed beacon block header corresponding to this block.
257+
#[instrument(level = "debug", skip_all)]
256258
pub fn signed_block_header(&self) -> SignedBeaconBlockHeader {
257259
SignedBeaconBlockHeader {
258260
message: self.message().block_header(),

0 commit comments

Comments
 (0)