From 8a0e98d4cc23b6645c07b26902052902a6fddcdf Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 7 Oct 2016 12:18:42 +0200 Subject: [PATCH] configurable jump table cache size --- Cargo.lock | 12 ++--- ethcore/Cargo.toml | 2 +- ethcore/src/client/client.rs | 2 +- ethcore/src/client/config.rs | 2 + ethcore/src/client/test_client.rs | 2 +- ethcore/src/evm/factory.rs | 19 +++---- ethcore/src/evm/interpreter/shared_cache.rs | 55 ++++++++++++++++++--- ethcore/src/evm/tests.rs | 2 +- ethcore/src/executive.rs | 4 +- parity/cache.rs | 10 +++- parity/helpers.rs | 5 +- 11 files changed, 84 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 04bc46c9c..b335df4af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -295,7 +295,7 @@ dependencies = [ "hyper 0.9.4 (git+https://github.com/ethcore/hyper)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lru-cache 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "lru-cache 0.0.7 (git+https://github.com/contain-rs/lru-cache)", "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -881,7 +881,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "linked-hash-map" -version = "0.0.9" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -897,9 +897,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lru-cache" version = "0.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/contain-rs/lru-cache#13255e33c45ceb69a4b143f235a4322df5fb580e" dependencies = [ - "linked-hash-map 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1950,10 +1950,10 @@ dependencies = [ "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f" "checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2" -"checksum linked-hash-map 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "83f7ff3baae999fdf921cccf54b61842bb3b26868d50d02dff48052ebec8dd79" +"checksum linked-hash-map 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bda158e0dabeb97ee8a401f4d17e479d6b891a14de0bba79d5cc2d4d325b5e48" "checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd" "checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" -"checksum lru-cache 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "42d50dcb5d9f145df83b1043207e1ac0c37c9c779c4e128ca4655abc3f3cbf8c" +"checksum lru-cache 0.0.7 (git+https://github.com/contain-rs/lru-cache)" = "" "checksum matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e" "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" "checksum mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a74cc2587bf97c49f3f5bab62860d6abf3902ca73b66b51d9b049fbdcd727bd2" diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 7bda7e567..ad9e545ce 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -37,7 +37,7 @@ ethkey = { path = "../ethkey" } ethcore-ipc-nano = { path = "../ipc/nano" } rlp = { path = "../util/rlp" } rand = "0.3" -lru-cache = "0.0.7" +lru-cache = { git = "https://github.com/contain-rs/lru-cache" } ethcore-bloom-journal = { path = "../util/bloom" } byteorder = "0.5" diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 3e9a30616..3bfae2fb8 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -197,7 +197,7 @@ impl Client { let awake = match config.mode { Mode::Dark(..) => false, _ => true }; let factories = Factories { - vm: EvmFactory::new(config.vm_type.clone()), + vm: EvmFactory::new(config.vm_type.clone(), config.jump_table_size), trie: TrieFactory::new(trie_spec), accountdb: Default::default(), }; diff --git a/ethcore/src/client/config.rs b/ethcore/src/client/config.rs index 4894b5442..e0ac51f0a 100644 --- a/ethcore/src/client/config.rs +++ b/ethcore/src/client/config.rs @@ -108,6 +108,8 @@ pub struct ClientConfig { pub verifier_type: VerifierType, /// State db cache-size. pub state_cache_size: usize, + /// EVM jump-tables cache size. + pub jump_table_size: usize, } #[cfg(test)] diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 27e9f9b5f..5c00ffff1 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -122,7 +122,7 @@ impl TestBlockChainClient { queue_size: AtomicUsize::new(0), miner: Arc::new(Miner::with_spec(&spec)), spec: spec, - vm_factory: EvmFactory::new(VMType::Interpreter), + vm_factory: EvmFactory::new(VMType::Interpreter, 1024 * 1024), latest_block_timestamp: RwLock::new(10_000_000), }; client.add_blocks(1, EachBlockWith::Nothing); // add genesis block diff --git a/ethcore/src/evm/factory.rs b/ethcore/src/evm/factory.rs index 629b423da..a3d94bde8 100644 --- a/ethcore/src/evm/factory.rs +++ b/ethcore/src/evm/factory.rs @@ -118,11 +118,12 @@ impl Factory { } } - /// Create new instance of specific `VMType` factory - pub fn new(evm: VMType) -> Self { + /// Create new instance of specific `VMType` factory, with a size in bytes + /// for caching jump destinations. + pub fn new(evm: VMType, cache_size: usize) -> Self { Factory { evm: evm, - evm_cache: Arc::new(SharedCache::default()), + evm_cache: Arc::new(SharedCache::new(cache_size)), } } @@ -164,22 +165,22 @@ macro_rules! evm_test( #[ignore] #[cfg(feature = "jit")] fn $name_jit() { - $name_test(Factory::new(VMType::Jit)); + $name_test(Factory::new(VMType::Jit, 1024 * 32)); } #[test] fn $name_int() { - $name_test(Factory::new(VMType::Interpreter)); + $name_test(Factory::new(VMType::Interpreter, 1024 * 32)); } }; ($name_test: ident: $name_jit: ident, $name_int: ident) => { #[test] #[cfg(feature = "jit")] fn $name_jit() { - $name_test(Factory::new(VMType::Jit)); + $name_test(Factory::new(VMType::Jit, 1024 * 32)); } #[test] fn $name_int() { - $name_test(Factory::new(VMType::Interpreter)); + $name_test(Factory::new(VMType::Interpreter, 1024 * 32)); } } ); @@ -193,13 +194,13 @@ macro_rules! evm_test_ignore( #[cfg(feature = "jit")] #[cfg(feature = "ignored-tests")] fn $name_jit() { - $name_test(Factory::new(VMType::Jit)); + $name_test(Factory::new(VMType::Jit, 1024 * 32)); } #[test] #[ignore] #[cfg(feature = "ignored-tests")] fn $name_int() { - $name_test(Factory::new(VMType::Interpreter)); + $name_test(Factory::new(VMType::Interpreter, 1024 * 32)); } } ); diff --git a/ethcore/src/evm/interpreter/shared_cache.rs b/ethcore/src/evm/interpreter/shared_cache.rs index 922179b0b..dee557522 100644 --- a/ethcore/src/evm/interpreter/shared_cache.rs +++ b/ethcore/src/evm/interpreter/shared_cache.rs @@ -21,25 +21,66 @@ use util::sha3::*; use bit_set::BitSet; use super::super::instructions; -const CACHE_CODE_ITEMS: usize = 4096; +const INITIAL_CAPACITY: usize = 32; +const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024; /// Global cache for EVM interpreter pub struct SharedCache { - jump_destinations: Mutex>> + jump_destinations: Mutex>>, + max_size: usize, + cur_size: Mutex, } impl SharedCache { + /// Create a jump destinations cache with a maximum size in bytes + /// to cache. + pub fn new(max_size: usize) -> Self { + SharedCache { + jump_destinations: Mutex::new(LruCache::new(INITIAL_CAPACITY)), + max_size: max_size * 8, // dealing with bits here. + cur_size: Mutex::new(0), + } + } + /// Get jump destinations bitmap for a contract. pub fn jump_destinations(&self, code_hash: &H256, code: &[u8]) -> Arc { if code_hash == &SHA3_EMPTY { return Self::find_jump_destinations(code); } + if let Some(d) = self.jump_destinations.lock().get_mut(code_hash) { return d.clone(); } let d = Self::find_jump_destinations(code); - self.jump_destinations.lock().insert(code_hash.clone(), d.clone()); + + { + let mut cur_size = self.cur_size.lock(); + *cur_size += d.capacity(); + + let mut jump_dests = self.jump_destinations.lock(); + let cap = jump_dests.capacity(); + + // grow the cache as necessary; it operates on amount of items + // but we're working based on memory usage. + if jump_dests.len() == cap && *cur_size < self.max_size { + jump_dests.set_capacity(cap * 2); + } + + // account for any element displaced from the cache. + if let Some(lru) = jump_dests.insert(code_hash.clone(), d.clone()) { + *cur_size -= lru.capacity(); + } + + // remove elements until we are below the memory target. + while *cur_size > self.max_size { + match jump_dests.remove_lru() { + Some((_, v)) => *cur_size -= v.capacity(), + _ => break, + } + } + } + d } @@ -57,15 +98,15 @@ impl SharedCache { } position += 1; } + + jump_dests.shrink_to_fit(); Arc::new(jump_dests) } } impl Default for SharedCache { - fn default() -> SharedCache { - SharedCache { - jump_destinations: Mutex::new(LruCache::new(CACHE_CODE_ITEMS)), - } + fn default() -> Self { + SharedCache::new(DEFAULT_CACHE_SIZE) } } diff --git a/ethcore/src/evm/tests.rs b/ethcore/src/evm/tests.rs index eb7d168cf..f685e279d 100644 --- a/ethcore/src/evm/tests.rs +++ b/ethcore/src/evm/tests.rs @@ -817,7 +817,7 @@ fn test_signextend(factory: super::Factory) { #[test] // JIT just returns out of gas fn test_badinstruction_int() { - let factory = super::Factory::new(VMType::Interpreter); + let factory = super::Factory::new(VMType::Interpreter, 1024 * 32); let code = "af".from_hex().unwrap(); let mut params = ActionParams::default(); diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index f3186d6dd..3c8b6171e 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -598,7 +598,7 @@ mod tests { #[test] // Tracing is not suported in JIT fn test_call_to_create() { - let factory = Factory::new(VMType::Interpreter); + let factory = Factory::new(VMType::Interpreter, 1024 * 32); // code: // @@ -724,7 +724,7 @@ mod tests { #[test] fn test_create_contract() { // Tracing is not supported in JIT - let factory = Factory::new(VMType::Interpreter); + let factory = Factory::new(VMType::Interpreter, 1024 * 32); // code: // // 60 10 - push 16 diff --git a/parity/cache.rs b/parity/cache.rs index f51e560a1..d2fc30d6e 100644 --- a/parity/cache.rs +++ b/parity/cache.rs @@ -95,7 +95,12 @@ impl CacheConfig { /// Size of the state cache. pub fn state(&self) -> u32 { - self.state + self.state * 3 / 4 + } + + /// Size of the jump-tables cache. + pub fn jump_tables(&self) -> u32 { + self.state / 4 } } @@ -109,7 +114,8 @@ mod tests { assert_eq!(config.db, 140); assert_eq!(config.blockchain(), 20); assert_eq!(config.queue(), 50); - assert_eq!(config.state(), 40); + assert_eq!(config.state(), 30); + assert_eq!(config.jump_tables(), 10); } #[test] diff --git a/parity/helpers.rs b/parity/helpers.rs index 55a3b8beb..2f4613b2c 100644 --- a/parity/helpers.rs +++ b/parity/helpers.rs @@ -215,7 +215,10 @@ pub fn to_client_config( client_config.tracing.max_cache_size = cache_config.traces() as usize * mb; // in bytes client_config.tracing.pref_cache_size = cache_config.traces() as usize * 3 / 4 * mb; - client_config.state_cache_size = cache_config.state() as usize; + // in bytes + client_config.state_cache_size = cache_config.state() as usize * mb; + // in bytes + client_config.jump_table_size = cache_config.jump_tables() as usize * mb; client_config.mode = mode; client_config.tracing.enabled = tracing;