diff --git a/Cargo.lock b/Cargo.lock index 8a726f63aa7..4d6f599a22f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3774,6 +3774,7 @@ dependencies = [ "ethrex-trie", "hex", "hex-literal", + "lru 0.16.2", "qfilter", "rayon", "rocksdb", @@ -4567,6 +4568,8 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.2.0", "serde", ] @@ -5912,6 +5915,15 @@ dependencies = [ "hashbrown 0.15.5", ] +[[package]] +name = "lru" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f" +dependencies = [ + "hashbrown 0.16.0", +] + [[package]] name = "lru-slab" version = "0.1.2" @@ -7961,7 +7973,7 @@ dependencies = [ "indoc", "instability", "itertools 0.13.0", - "lru", + "lru 0.12.5", "paste", "strum 0.26.3", "unicode-segmentation", @@ -9868,7 +9880,7 @@ dependencies = [ "hashbrown 0.14.5", "hex", "itertools 0.13.0", - "lru", + "lru 0.12.5", "num-bigint 0.4.6", "p3-baby-bear", "p3-bn254-fr", diff --git a/crates/common/types/account.rs b/crates/common/types/account.rs index 0945039f93f..8449561c550 100644 --- a/crates/common/types/account.rs +++ b/crates/common/types/account.rs @@ -65,6 +65,21 @@ impl Code { } targets } + + /// Estimates the size of the Code struct in bytes + /// (including stack size and heap allocation). + /// + /// Note: This is an estimation and may not be exact. + /// + /// # Returns + /// + /// usize - Estimated size in bytes + pub fn size(&self) -> usize { + let hash_size = size_of::(); + let bytes_size = size_of::(); + let vec_size = size_of::>() + self.jump_targets.len() * size_of::(); + hash_size + bytes_size + vec_size + } } impl AsRef for Code { diff --git a/crates/l2/prover/src/guest_program/src/risc0/Cargo.lock b/crates/l2/prover/src/guest_program/src/risc0/Cargo.lock index 2c0a84ecc94..9abb8e41a31 100644 --- a/crates/l2/prover/src/guest_program/src/risc0/Cargo.lock +++ b/crates/l2/prover/src/guest_program/src/risc0/Cargo.lock @@ -1301,6 +1301,7 @@ dependencies = [ "ethrex-rlp", "ethrex-trie", "hex", + "lru", "qfilter", "rayon", "rustc-hash", @@ -1430,6 +1431,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.5.0" @@ -1608,7 +1615,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -1616,6 +1623,11 @@ name = "hashbrown" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] [[package]] name = "heck" @@ -2019,6 +2031,15 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +[[package]] +name = "lru" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f" +dependencies = [ + "hashbrown 0.16.0", +] + [[package]] name = "malachite" version = "0.6.1" diff --git a/crates/l2/prover/src/guest_program/src/sp1/Cargo.lock b/crates/l2/prover/src/guest_program/src/sp1/Cargo.lock index be8d0c50620..9bb35b34770 100644 --- a/crates/l2/prover/src/guest_program/src/sp1/Cargo.lock +++ b/crates/l2/prover/src/guest_program/src/sp1/Cargo.lock @@ -1073,6 +1073,7 @@ dependencies = [ "ethrex-rlp", "ethrex-trie", "hex", + "lru", "qfilter", "rayon", "rustc-hash", @@ -1202,6 +1203,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1347,7 +1354,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -1355,6 +1362,11 @@ name = "hashbrown" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] [[package]] name = "heck" @@ -1755,6 +1767,15 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +[[package]] +name = "lru" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f" +dependencies = [ + "hashbrown 0.16.0", +] + [[package]] name = "malachite" version = "0.6.1" diff --git a/crates/l2/tee/quote-gen/Cargo.lock b/crates/l2/tee/quote-gen/Cargo.lock index ebcb465c136..d220ca04174 100644 --- a/crates/l2/tee/quote-gen/Cargo.lock +++ b/crates/l2/tee/quote-gen/Cargo.lock @@ -2451,6 +2451,7 @@ dependencies = [ "ethrex-rlp", "ethrex-trie", "hex", + "lru 0.16.2", "qfilter", "rayon", "rustc-hash", @@ -2631,6 +2632,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "font8x8" version = "0.3.1" @@ -2968,7 +2975,18 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", ] [[package]] @@ -3873,6 +3891,15 @@ dependencies = [ "hashbrown 0.15.5", ] +[[package]] +name = "lru" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f" +dependencies = [ + "hashbrown 0.16.0", +] + [[package]] name = "malachite" version = "0.6.1" @@ -4964,7 +4991,7 @@ dependencies = [ "indoc", "instability", "itertools 0.13.0", - "lru", + "lru 0.12.5", "paste", "strum 0.26.3", "unicode-segmentation", diff --git a/crates/storage/Cargo.toml b/crates/storage/Cargo.toml index 307f4f869a3..c42f18229fb 100644 --- a/crates/storage/Cargo.toml +++ b/crates/storage/Cargo.toml @@ -28,6 +28,7 @@ tokio = { workspace = true, optional = true, features = ["rt"] } bincode = "1.3.3" qfilter = "0.2.5" rayon.workspace = true +lru = "0.16.2" [features] default = [] diff --git a/crates/storage/store_db/rocksdb.rs b/crates/storage/store_db/rocksdb.rs index 4792bd446fd..d59dfbc3c38 100644 --- a/crates/storage/store_db/rocksdb.rs +++ b/crates/storage/store_db/rocksdb.rs @@ -14,15 +14,18 @@ use ethrex_common::{ }, }; use ethrex_trie::{Nibbles, Node, Trie}; +use lru::LruCache; use rocksdb::{ BlockBasedOptions, BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, Options, WriteBatch, checkpoint::Checkpoint, }; +use rustc_hash::FxBuildHasher; use std::{ collections::HashSet, path::{Path, PathBuf}, sync::{ Arc, Mutex, + atomic::AtomicU64, mpsc::{SyncSender, sync_channel}, }, }; @@ -149,13 +152,40 @@ enum FKVGeneratorControlMessage { Continue, } -#[derive(Debug, Clone)] +// 64mb +const CODE_CACHE_MAX_SIZE: u64 = 64 * 1024 * 1024; +type CodeCache = LruCache; + +#[derive(Debug)] pub struct Store { db: Arc>, trie_cache: Arc>>, flatkeyvalue_control_tx: std::sync::mpsc::SyncSender, trie_update_worker_tx: TriedUpdateWorkerTx, last_computed_flatkeyvalue: Arc>>, + /// Cache for account bytecodes, keyed by the bytecode hash. + /// Note that we don't remove entries on account code changes, since + /// those changes already affect the code hash stored in the account, and only + /// may result in this cache having useless data. + account_code_cache: Arc>, + account_code_cache_size: AtomicU64, +} + +impl Clone for Store { + fn clone(&self) -> Self { + Self { + db: self.db.clone(), + trie_cache: self.trie_cache.clone(), + flatkeyvalue_control_tx: self.flatkeyvalue_control_tx.clone(), + trie_update_worker_tx: self.trie_update_worker_tx.clone(), + last_computed_flatkeyvalue: self.last_computed_flatkeyvalue.clone(), + account_code_cache: self.account_code_cache.clone(), + account_code_cache_size: AtomicU64::new( + self.account_code_cache_size + .load(std::sync::atomic::Ordering::Relaxed), + ), + } + } } impl Store { @@ -399,6 +429,10 @@ impl Store { flatkeyvalue_control_tx: fkv_tx, trie_update_worker_tx: trie_upd_tx, last_computed_flatkeyvalue: Arc::new(Mutex::new(last_written)), + account_code_cache: Arc::new(Mutex::new(LruCache::unbounded_with_hasher( + FxBuildHasher, + ))), + account_code_cache_size: AtomicU64::new(0), }; let store_clone = store.clone(); std::thread::spawn(move || { @@ -1363,7 +1397,21 @@ impl StoreEngine for Store { .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))? } + /// Get account code by its hash. + /// + /// Check if the code exists in the cache (attribute `account_code_cache`), if not, + /// reads the database, and if it exists, decodes and returns it. fn get_account_code(&self, code_hash: H256) -> Result, StoreError> { + // check cache first + if let Some(code) = self + .account_code_cache + .lock() + .map_err(|_| StoreError::LockError)? + .get(&code_hash) + { + return Ok(Some(code.clone())); + } + let cf = self.cf_handle(CF_ACCOUNT_CODES)?; let Some(bytes) = self .db @@ -1378,6 +1426,39 @@ impl StoreEngine for Store { bytecode: Bytes::copy_from_slice(bytecode), jump_targets: >::decode(targets)?, }; + + // insert into cache and evict if needed + { + let mut cache = self + .account_code_cache + .lock() + .map_err(|_| StoreError::LockError)?; + let code_size = code.size(); + self.account_code_cache_size + .fetch_add(code_size as u64, std::sync::atomic::Ordering::SeqCst); + let cache_len = cache.len() + 1; + let mut current_size = self + .account_code_cache_size + .load(std::sync::atomic::Ordering::SeqCst); + debug!( + "[ACCOUNT CODE CACHE] cache elements (): {cache_len}, total size: {current_size} bytes" + ); + + while current_size > CODE_CACHE_MAX_SIZE { + if let Some((_, code)) = cache.pop_lru() { + self.account_code_cache_size + .fetch_sub(code.size() as u64, std::sync::atomic::Ordering::SeqCst); + current_size = self + .account_code_cache_size + .load(std::sync::atomic::Ordering::SeqCst); + } else { + break; + } + } + + cache.get_or_insert(code_hash, || code.clone()); + } + Ok(Some(code)) }