Skip to content

Commit 063e64d

Browse files
authored
Cache bloom filters (#598)
## 📝 Summary Currently finalize time is often 30% bigger than root hash time but ideally it should be really close. One of the reasons is that we calculate bloom filters every single finalize which can be expensive operation, this PR introduces caching for bloom filters. It also moves finalize to a separate thread so it can persist local thread cache for the slot finalization and in addition the thread is not named which makes profiling easier. This shaves up to 5ms in the worst case from finalize but its a good start for faster finalization ## 💡 Motivation and Context <!--- (Optional) Why is this change required? What problem does it solve? Remove this section if not applicable. --> --- ## ✅ I have completed the following steps: * [ ] Run `make lint` * [ ] Run `make test` * [ ] Added tests (if applicable)
1 parent 0284c0f commit 063e64d

File tree

13 files changed

+347
-79
lines changed

13 files changed

+347
-79
lines changed

crates/rbuilder/src/backtest/redistribute/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::{
1818
live_builder::{block_list_provider::BlockList, cli::LiveBuilderConfig},
1919
primitives::{Order, OrderId},
2020
provider::StateProviderFactory,
21-
utils::{signed_uint_delta, u256decimal_serde_helper},
21+
utils::{elapsed_s, signed_uint_delta, u256decimal_serde_helper},
2222
};
2323
use ahash::{HashMap, HashSet};
2424
use alloy_primitives::{utils::format_ether, Address, B256, I256, U256};
@@ -170,7 +170,7 @@ where
170170
config.base_config().evm_caching_enable,
171171
)?;
172172

173-
let time_preparation_s = start.elapsed().as_millis() as f64 / 1000.0;
173+
let time_preparation_s = elapsed_s(start);
174174
let start = Instant::now();
175175

176176
let results_without_exclusion = calculate_backtest_without_exclusion(
@@ -180,7 +180,7 @@ where
180180
block_data.clone(),
181181
)?;
182182

183-
let time_no_exclusion_s = start.elapsed().as_millis() as f64 / 1000.0;
183+
let time_no_exclusion_s = elapsed_s(start);
184184
let start = Instant::now();
185185

186186
let exclusion_results = calculate_backtest_identity_and_order_exclusion(
@@ -192,7 +192,7 @@ where
192192
&results_without_exclusion,
193193
)?;
194194

195-
let time_single_exclusion_s = start.elapsed().as_millis() as f64 / 1000.0;
195+
let time_single_exclusion_s = elapsed_s(start);
196196
let start = Instant::now();
197197

198198
let exclusion_results = calc_joint_exclusion_results(
@@ -206,7 +206,7 @@ where
206206
distribute_to_mempool_txs,
207207
)?;
208208

209-
let time_joint_exclusion_s = start.elapsed().as_millis() as f64 / 1000.0;
209+
let time_joint_exclusion_s = elapsed_s(start);
210210
let start = Instant::now();
211211

212212
let calculated_redistribution_result = apply_redistribution_formula(
@@ -237,7 +237,7 @@ where
237237
.map(|o| o.redistribution_value_received)
238238
.sum::<U256>();
239239

240-
let time_result_s = start.elapsed().as_millis() as f64 / 1000.0;
240+
let time_result_s = elapsed_s(start);
241241

242242
let time_total_s = time_preparation_s
243243
+ time_no_exclusion_s

crates/rbuilder/src/bin/debug-bench-machine.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ async fn main() -> eyre::Result<()> {
114114
let build_time = build_time.elapsed();
115115

116116
let finalize_time = Instant::now();
117-
let finalized_block = partial_block.finalize(&mut state, &ctx, &mut local_ctx)?;
117+
let finalized_block = partial_block.finalize(state, &ctx, &mut local_ctx)?;
118118
let finalize_time = finalize_time.elapsed();
119119

120120
debug!(

crates/rbuilder/src/building/builders/block_building_helper.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::{
1818
},
1919
primitives::{SimValue, SimulatedOrder},
2020
telemetry::{self, add_block_fill_time, add_order_simulation_time},
21-
utils::{check_block_hash_reader_health, HistoricalBlockError},
21+
utils::{check_block_hash_reader_health, elapsed_ms, HistoricalBlockError},
2222
};
2323

2424
use super::Block;
@@ -401,18 +401,22 @@ impl BlockBuildingHelper for BlockBuildingHelperFromProvider {
401401
return Err(BlockBuildingHelperError::PayoutTxNotAllowed);
402402
}
403403
let start_time = Instant::now();
404+
let step_start = Instant::now();
404405

405406
self.finalize_block_execution(local_ctx, payout_tx_value)?;
406407
// This could be moved outside of this func (pre finalize) since I don´t think the payout tx can change much.
407408
self.built_block_trace
408409
.verify_bundle_consistency(&self.building_ctx.blocklist)?;
409410

411+
let finalize_prep_time_ms = elapsed_ms(step_start);
412+
let step_start = Instant::now();
413+
410414
let sim_gas_used = self.partial_block.tracer.used_gas;
411415
let block_number = self.building_context().block();
412416
let finalized_block =
413417
match self
414418
.partial_block
415-
.finalize(&mut self.block_state, &self.building_ctx, local_ctx)
419+
.finalize(self.block_state, &self.building_ctx, local_ctx)
416420
{
417421
Ok(finalized_block) => finalized_block,
418422
Err(err) => {
@@ -427,6 +431,15 @@ impl BlockBuildingHelper for BlockBuildingHelperFromProvider {
427431
return Err(BlockBuildingHelperError::FinalizeError(err));
428432
}
429433
};
434+
435+
let finalize_block_time_ms = elapsed_ms(step_start);
436+
let finalize_time_ms = elapsed_ms(start_time);
437+
trace!(
438+
finalize_time_ms,
439+
finalize_prep_time_ms,
440+
finalize_block_time_ms,
441+
"Block building helper finalized block"
442+
);
430443
self.built_block_trace.update_orders_sealed_at();
431444
self.built_block_trace.root_hash_time = finalized_block.root_hash_time;
432445
self.built_block_trace.finalize_time = start_time.elapsed();

crates/rbuilder/src/building/builders/parallel_builder/block_building_result_assembler.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use crate::{
2121
BlockBuildingContext, ThreadBlockBuildingContext,
2222
},
2323
telemetry::mark_builder_considers_order,
24+
utils::elapsed_ms,
2425
};
2526

2627
/// Assembles block building results from the best orderings of order groups.
@@ -139,7 +140,7 @@ impl BlockBuildingResultAssembler {
139140
trace!(
140141
run_id = self.run_id,
141142
version = version,
142-
time_ms = time_start.elapsed().as_millis(),
143+
time_ms = elapsed_ms(time_start),
143144
profit = format_ether(value),
144145
"Parallel builder built new block",
145146
);

crates/rbuilder/src/building/builders/parallel_builder/conflict_resolving_pool.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use super::{
1515
simulation_cache::SharedSimulationCache, ConflictGroup, ConflictResolutionResultPerGroup,
1616
ConflictTask, GroupId, ResolutionResult, TaskPriority,
1717
};
18-
use crate::building::BlockBuildingContext;
1918
use crate::provider::StateProviderFactory;
19+
use crate::{building::BlockBuildingContext, utils::elapsed_ms};
2020

2121
pub type TaskQueue = Arc<SegQueue<ConflictTask>>;
2222

@@ -83,18 +83,18 @@ where
8383
match group_result_sender.send((task_id, result)) {
8484
Ok(_) => {
8585
trace!(
86-
task_id = %task_id,
87-
time_taken_ms = %task_start.elapsed().as_millis(),
88-
"Conflict resolving: successfully sent group result"
89-
);
86+
task_id = %task_id,
87+
time_taken_ms = %elapsed_ms(task_start),
88+
"Conflict resolving: successfully sent group result"
89+
);
9090
}
9191
Err(err) => {
9292
warn!(
93-
task_id = %task_id,
94-
error = ?err,
95-
time_taken_ms = %task_start.elapsed().as_millis(),
96-
"Conflict resolving: failed to send group result"
97-
);
93+
task_id = %task_id,
94+
error = ?err,
95+
time_taken_ms = %elapsed_ms(task_start),
96+
"Conflict resolving: failed to send group result"
97+
);
9898
return;
9999
}
100100
}

crates/rbuilder/src/building/builders/parallel_builder/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use crate::{
3535
LiveBuilderInput,
3636
},
3737
provider::StateProviderFactory,
38+
utils::elapsed_ms,
3839
};
3940

4041
use self::{
@@ -269,7 +270,7 @@ fn run_order_intake(
269270
trace!(
270271
new_orders_count = len,
271272
groups_count = conflict_finder.get_order_groups().len(),
272-
time_taken_ms = %time_start.elapsed().as_millis(),
273+
time_taken_ms = %elapsed_ms(time_start),
273274
"Order intake: added new orders and processing groups"
274275
);
275276
conflict_task_generator.process_groups(conflict_finder.get_order_groups());

crates/rbuilder/src/building/mod.rs

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ use crate::{
33
primitives::{Order, OrderId, SimValue, SimulatedOrder, TransactionSignedEcRecoveredWithBlobs},
44
provider::RootHasher,
55
roothash::RootHashError,
6-
utils::{a2r_withdrawal, default_cfg_env, timestamp_as_u64, Signer},
6+
utils::{
7+
a2r_withdrawal, default_cfg_env, elapsed_ms,
8+
receipts::{calculate_receipt_root_and_block_logs_bloom, BloomCache},
9+
timestamp_as_u64, Signer,
10+
},
711
};
812
use alloy_consensus::{Header, EMPTY_OMMER_ROOT_HASH};
913
use alloy_eips::{
@@ -49,6 +53,7 @@ use std::{
4953
};
5054
use thiserror::Error;
5155
use time::OffsetDateTime;
56+
use tracing::trace;
5257
use tx_sim_cache::TxExecutionCache;
5358

5459
pub mod block_orders;
@@ -335,6 +340,7 @@ impl BlockBuildingContext {
335340
#[derive(Debug, Clone, Default)]
336341
pub struct ThreadBlockBuildingContext {
337342
pub cached_reads: LocalCachedReads,
343+
pub bloom_cache: BloomCache,
338344
}
339345

340346
#[derive(Debug, Clone, Copy)]
@@ -727,38 +733,53 @@ impl<Tracer: SimulationTracer> PartialBlock<Tracer> {
727733
#[allow(clippy::too_many_arguments)]
728734
pub fn finalize(
729735
self,
730-
state: &mut BlockState,
736+
mut state: BlockState,
731737
ctx: &BlockBuildingContext,
732738
local_ctx: &mut ThreadBlockBuildingContext,
733739
) -> Result<FinalizeResult, FinalizeError> {
734-
let (requests, withdrawals_root) = self.process_requests(state, ctx, local_ctx)?;
735-
let bundle = state.clone_bundle();
740+
let start = Instant::now();
741+
742+
let step_start = Instant::now();
743+
let (requests, withdrawals_root) = self.process_requests(&mut state, ctx, local_ctx)?;
736744
let block_number = ctx.evm_env.block_env.number;
737745

746+
let request_processsing_time_ms = elapsed_ms(step_start);
747+
let step_start = Instant::now();
748+
738749
let requests_hash = requests.as_ref().map(|requests| requests.requests_hash());
750+
751+
let (bundle, _) = state.into_parts();
739752
let execution_outcome = ExecutionOutcome::new(
740753
bundle,
741754
vec![self.receipts],
742755
block_number,
743756
vec![requests.clone().unwrap_or_default()],
744757
);
745758

746-
// @TODO: Check ethereum_receipts_root since it could fail on Op. Check reth crates/optimism/payload/src/builder.rs?
747-
let receipts_root = execution_outcome
748-
.ethereum_receipts_root(block_number)
749-
.expect("Number is in range");
750-
let logs_bloom = execution_outcome
751-
.block_logs_bloom(block_number)
752-
.expect("Number is in range");
759+
let exec_outcome_time_ms = elapsed_ms(step_start);
760+
let step_start = Instant::now();
761+
762+
let (receipts_root, logs_bloom) = calculate_receipt_root_and_block_logs_bloom(
763+
execution_outcome.receipts_by_block(block_number),
764+
&mut local_ctx.bloom_cache,
765+
);
766+
767+
let bloom_time_ms = elapsed_ms(step_start);
768+
let step_start = Instant::now();
753769

754770
// calculate the state root
755-
let start = Instant::now();
756771
let state_root = ctx.root_hasher.state_root(&execution_outcome)?;
757-
let root_hash_time = start.elapsed();
772+
let root_hash_time = step_start.elapsed();
773+
774+
let root_hash_time_ms = elapsed_ms(step_start);
775+
let step_start = Instant::now();
758776

759777
// create the block header
760778
let transactions_root = proofs::calculate_transaction_root(&self.executed_tx);
761779

780+
let transactions_root_time_ms = elapsed_ms(step_start);
781+
let step_start = Instant::now();
782+
762783
// double check blocked txs
763784
for tx_with_blob in &self.executed_tx {
764785
if ctx.blocklist.contains(&tx_with_blob.signer()) {
@@ -788,6 +809,9 @@ impl<Tracer: SimulationTracer> PartialBlock<Tracer> {
788809
(None, None)
789810
};
790811

812+
let blobs_time_ms = elapsed_ms(step_start);
813+
let step_start = Instant::now();
814+
791815
let header = Header {
792816
parent_hash: ctx.attributes.parent,
793817
ommers_hash: EMPTY_OMMER_ROOT_HASH,
@@ -830,13 +854,30 @@ impl<Tracer: SimulationTracer> PartialBlock<Tracer> {
830854
withdrawals,
831855
},
832856
};
833-
834-
Ok(FinalizeResult {
857+
let result = FinalizeResult {
835858
sealed_block: block.seal_slow(),
836859
txs_blob_sidecars,
837860
root_hash_time,
838861
execution_requests: requests.map(|er| er.take()).unwrap_or_default(),
839-
})
862+
};
863+
864+
let block_seal_time_ms = elapsed_ms(step_start);
865+
866+
let total_time_ms = elapsed_ms(start);
867+
868+
trace!(
869+
total_time_ms,
870+
exec_outcome_time_ms,
871+
bloom_time_ms,
872+
request_processsing_time_ms,
873+
root_hash_time_ms,
874+
transactions_root_time_ms,
875+
blobs_time_ms,
876+
block_seal_time_ms,
877+
"Partial block finalized block"
878+
);
879+
880+
Ok(result)
840881
}
841882

842883
pub fn pre_block_call(

0 commit comments

Comments
 (0)