perf: put all prefix sets in Rc (#3582)

This commit is contained in:
Matthias Seitz
2023-07-05 11:58:48 +02:00
committed by GitHub
parent 1e3f0c0e86
commit 9cd31f1487
6 changed files with 110 additions and 36 deletions

View File

@@ -7,21 +7,23 @@ use proptest::{
test_runner::{basic_result_cache, TestRunner},
};
use reth_primitives::trie::Nibbles;
use reth_trie::prefix_set::PrefixSet;
use reth_trie::prefix_set::PrefixSetMut;
use std::collections::BTreeSet;
/// Abstractions used for benching
pub trait PrefixSetAbstraction: Default {
fn insert(&mut self, key: Nibbles);
fn contains(&mut self, key: Nibbles) -> bool;
}
impl PrefixSetAbstraction for PrefixSet {
/// Abstractions used for benching
impl PrefixSetAbstraction for PrefixSetMut {
fn insert(&mut self, key: Nibbles) {
self.insert(key)
}
fn contains(&mut self, key: Nibbles) -> bool {
PrefixSet::contains(self, key)
PrefixSetMut::contains(self, key)
}
}

View File

@@ -1,5 +1,5 @@
use super::{HashedAccountCursor, HashedCursorFactory, HashedStorageCursor};
use crate::prefix_set::PrefixSet;
use crate::prefix_set::{PrefixSet, PrefixSetMut};
use reth_db::{
cursor::{DbCursorRO, DbDupCursorRO},
tables,
@@ -27,13 +27,13 @@ pub struct HashedPostState {
}
impl HashedPostState {
/// Construct (PrefixSets)[PrefixSet] from hashed post state.
/// Construct (PrefixSet)[PrefixSet] from hashed post state.
/// The prefix sets contain the hashed account and storage keys that have been changed in the
/// post state.
pub fn construct_prefix_sets(&self) -> (PrefixSet, HashMap<H256, PrefixSet>) {
// Initialize prefix sets.
let mut account_prefix_set = PrefixSet::default();
let mut storage_prefix_set: HashMap<H256, PrefixSet> = HashMap::default();
let mut account_prefix_set = PrefixSetMut::default();
let mut storage_prefix_set: HashMap<H256, PrefixSetMut> = HashMap::default();
for hashed_address in self.accounts.keys() {
account_prefix_set.insert(Nibbles::unpack(hashed_address));
@@ -49,7 +49,10 @@ impl HashedPostState {
}
}
(account_prefix_set, storage_prefix_set)
(
account_prefix_set.freeze(),
storage_prefix_set.into_iter().map(|(k, v)| (k, v.freeze())).collect(),
)
}
}

View File

@@ -1,4 +1,4 @@
use super::PrefixSet;
use super::PrefixSetMut;
use derive_more::Deref;
use reth_db::{
cursor::DbCursorRO,
@@ -29,10 +29,10 @@ where
pub fn load(
self,
range: RangeInclusive<BlockNumber>,
) -> Result<(PrefixSet, HashMap<H256, PrefixSet>), DatabaseError> {
) -> Result<(PrefixSetMut, HashMap<H256, PrefixSetMut>), DatabaseError> {
// Initialize prefix sets.
let mut account_prefix_set = PrefixSet::default();
let mut storage_prefix_set: HashMap<H256, PrefixSet> = HashMap::default();
let mut account_prefix_set = PrefixSetMut::default();
let mut storage_prefix_set: HashMap<H256, PrefixSetMut> = HashMap::default();
// Walk account changeset and insert account prefixes.
let mut account_cursor = self.cursor_read::<tables::AccountChangeSet>()?;

View File

@@ -1,4 +1,5 @@
use reth_primitives::trie::Nibbles;
use std::rc::Rc;
mod loader;
pub use loader::PrefixSetLoader;
@@ -14,22 +15,22 @@ pub use loader::PrefixSetLoader;
/// # Examples
///
/// ```
/// use reth_trie::prefix_set::PrefixSet;
/// use reth_trie::prefix_set::PrefixSetMut;
///
/// let mut prefix_set = PrefixSet::default();
/// let mut prefix_set = PrefixSetMut::default();
/// prefix_set.insert(b"key1");
/// prefix_set.insert(b"key2");
///
/// assert_eq!(prefix_set.contains(b"key"), true);
/// ```
#[derive(Debug, Default, Clone)]
pub struct PrefixSet {
pub struct PrefixSetMut {
keys: Vec<Nibbles>,
sorted: bool,
index: usize,
}
impl PrefixSet {
impl PrefixSetMut {
/// Returns `true` if any of the keys in the set has the given prefix or
/// if the given prefix is a prefix of any key in the set.
pub fn contains<T: Into<Nibbles>>(&mut self, prefix: T) -> bool {
@@ -75,6 +76,64 @@ impl PrefixSet {
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
/// Returns a `PrefixSet` with the same elements as this set.
///
/// If not yet sorted, the elements will be sorted and deduplicated.
pub fn freeze(mut self) -> PrefixSet {
if !self.sorted {
self.keys.sort();
self.keys.dedup();
}
PrefixSet { keys: Rc::new(self.keys), index: self.index }
}
}
/// A sorted prefix set that has an immutable _sorted_ list of unique keys.
///
/// See also [PrefixSetMut::freeze].
#[derive(Debug, Default, Clone)]
pub struct PrefixSet {
keys: Rc<Vec<Nibbles>>,
index: usize,
}
impl PrefixSet {
/// Returns `true` if any of the keys in the set has the given prefix or
/// if the given prefix is a prefix of any key in the set.
#[inline]
pub fn contains<T: Into<Nibbles>>(&mut self, prefix: T) -> bool {
let prefix = prefix.into();
while self.index > 0 && self.keys[self.index] > prefix {
self.index -= 1;
}
for (idx, key) in self.keys[self.index..].iter().enumerate() {
if key.has_prefix(&prefix) {
self.index += idx;
return true
}
if key > &prefix {
self.index += idx;
return false
}
}
false
}
/// Returns the number of elements in the set.
pub fn len(&self) -> usize {
self.keys.len()
}
/// Returns `true` if the set is empty.
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
}
#[cfg(test)]
@@ -83,7 +142,7 @@ mod tests {
#[test]
fn test_contains_with_multiple_inserts_and_duplicates() {
let mut prefix_set = PrefixSet::default();
let mut prefix_set = PrefixSetMut::default();
prefix_set.insert(b"123");
prefix_set.insert(b"124");
prefix_set.insert(b"456");

View File

@@ -1,7 +1,7 @@
use crate::{
account::EthAccount,
hashed_cursor::{HashedAccountCursor, HashedCursorFactory, HashedStorageCursor},
prefix_set::{PrefixSet, PrefixSetLoader},
prefix_set::{PrefixSet, PrefixSetLoader, PrefixSetMut},
progress::{IntermediateStateRootState, StateRootProgress},
trie_cursor::{AccountTrieCursor, StorageTrieCursor},
updates::{TrieKey, TrieOp, TrieUpdates},
@@ -90,7 +90,7 @@ where
pub fn new(tx: &'a TX) -> Self {
Self {
tx,
changed_account_prefixes: PrefixSet::default(),
changed_account_prefixes: PrefixSetMut::default().freeze(),
changed_storage_prefixes: HashMap::default(),
previous_state: None,
threshold: 100_000,
@@ -110,8 +110,10 @@ where
) -> Result<Self, StateRootError> {
let (account_prefixes, storage_prefixes) = PrefixSetLoader::new(tx).load(range)?;
Ok(Self::new(tx)
.with_changed_account_prefixes(account_prefixes)
.with_changed_storage_prefixes(storage_prefixes))
.with_changed_account_prefixes(account_prefixes.freeze())
.with_changed_storage_prefixes(
storage_prefixes.into_iter().map(|(k, v)| (k, v.freeze())).collect(),
))
}
/// Computes the state root of the trie with the changed account and storage prefixes and
@@ -371,7 +373,7 @@ where
Self {
tx,
hashed_address,
changed_prefixes: PrefixSet::default(),
changed_prefixes: PrefixSetMut::default().freeze(),
hashed_cursor_factory: tx,
}
}
@@ -389,7 +391,12 @@ impl<'a, 'b, TX, H> StorageRoot<'a, 'b, TX, H> {
hashed_cursor_factory: &'b H,
hashed_address: H256,
) -> Self {
Self { tx, hashed_address, changed_prefixes: PrefixSet::default(), hashed_cursor_factory }
Self {
tx,
hashed_address,
changed_prefixes: PrefixSetMut::default().freeze(),
hashed_cursor_factory,
}
}
/// Set the changed prefixes.
@@ -591,10 +598,10 @@ mod tests {
trie_updates.flush(tx.tx_ref()).unwrap();
// 3. Calculate the incremental root
let mut storage_changes = PrefixSet::default();
let mut storage_changes = PrefixSetMut::default();
storage_changes.insert(Nibbles::unpack(modified_key));
let loader = StorageRoot::new_hashed(tx.tx_ref(), hashed_address)
.with_changed_prefixes(storage_changes);
.with_changed_prefixes(storage_changes.freeze());
let incremental_root = loader.root().unwrap();
assert_eq!(modified_root, incremental_root);
@@ -1014,7 +1021,7 @@ mod tests {
Account { nonce: 0, balance: U256::from(5).mul(ether), bytecode_hash: None };
hashed_account_cursor.upsert(key4b, account4b).unwrap();
let mut prefix_set = PrefixSet::default();
let mut prefix_set = PrefixSetMut::default();
prefix_set.insert(Nibbles::unpack(key4b));
let expected_state_root =
@@ -1022,7 +1029,7 @@ mod tests {
.unwrap();
let (root, trie_updates) = StateRoot::new(tx.tx_ref())
.with_changed_account_prefixes(prefix_set)
.with_changed_account_prefixes(prefix_set.freeze())
.root_with_updates()
.unwrap();
assert_eq!(root, expected_state_root);
@@ -1060,7 +1067,7 @@ mod tests {
let account = hashed_account_cursor.seek_exact(key2).unwrap().unwrap();
hashed_account_cursor.delete_current().unwrap();
let mut account_prefix_set = PrefixSet::default();
let mut account_prefix_set = PrefixSetMut::default();
account_prefix_set.insert(Nibbles::unpack(account.0));
let computed_expected_root: H256 = triehash::trie_root::<KeccakHasher, _, _, _>([
@@ -1074,7 +1081,7 @@ mod tests {
]);
let (root, trie_updates) = StateRoot::new(tx.tx_ref())
.with_changed_account_prefixes(account_prefix_set)
.with_changed_account_prefixes(account_prefix_set.freeze())
.root_with_updates()
.unwrap();
assert_eq!(root, computed_expected_root);
@@ -1116,7 +1123,7 @@ mod tests {
let account3 = hashed_account_cursor.seek_exact(key3).unwrap().unwrap();
hashed_account_cursor.delete_current().unwrap();
let mut account_prefix_set = PrefixSet::default();
let mut account_prefix_set = PrefixSetMut::default();
account_prefix_set.insert(Nibbles::unpack(account2.0));
account_prefix_set.insert(Nibbles::unpack(account3.0));
@@ -1131,7 +1138,7 @@ mod tests {
]);
let (root, trie_updates) = StateRoot::new(tx.tx_ref())
.with_changed_account_prefixes(account_prefix_set)
.with_changed_account_prefixes(account_prefix_set.freeze())
.root_with_updates()
.unwrap();
assert_eq!(root, computed_expected_root);
@@ -1227,7 +1234,7 @@ mod tests {
let mut state = BTreeMap::default();
for accounts in account_changes {
let should_generate_changeset = !state.is_empty();
let mut changes = PrefixSet::default();
let mut changes = PrefixSetMut::default();
for (hashed_address, balance) in accounts.clone() {
hashed_account_cursor.upsert(hashed_address, Account { balance,..Default::default() }).unwrap();
if should_generate_changeset {
@@ -1236,7 +1243,7 @@ mod tests {
}
let (state_root, trie_updates) = StateRoot::new(tx.tx_ref())
.with_changed_account_prefixes(changes)
.with_changed_account_prefixes(changes.freeze())
.root_with_updates()
.unwrap();

View File

@@ -256,7 +256,10 @@ impl<'a, K: Key + From<Vec<u8>>, C: TrieCursor<K>> TrieWalker<'a, K, C> {
mod tests {
use super::*;
use crate::trie_cursor::{AccountTrieCursor, StorageTrieCursor};
use crate::{
prefix_set::PrefixSetMut,
trie_cursor::{AccountTrieCursor, StorageTrieCursor},
};
use reth_db::{
cursor::DbCursorRW, tables, test_utils::create_test_rw_db, transaction::DbTxMut,
};
@@ -378,9 +381,9 @@ mod tests {
assert_eq!(cursor.key(), None);
// We insert something that's not part of the existing trie/prefix.
let mut changed = PrefixSet::default();
let mut changed = PrefixSetMut::default();
changed.insert(&[0xF, 0x1]);
let mut cursor = TrieWalker::new(&mut trie, changed);
let mut cursor = TrieWalker::new(&mut trie, changed.freeze());
// Root node
assert_eq!(cursor.key(), Some(Nibbles::from_hex(vec![])));