diff --git a/Cargo.lock b/Cargo.lock index 8bc8c86ed..7ac3728aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1078,7 +1078,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rocksdb" version = "0.4.5" -source = "git+https://github.com/ethcore/rust-rocksdb#6472a9dce16c267a3acec2ee6fd01d1bf8de4913" +source = "git+https://github.com/ethcore/rust-rocksdb#dd597245bfcb621c6ffc45478e1fda0b05d2f409" dependencies = [ "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)", @@ -1087,7 +1087,7 @@ dependencies = [ [[package]] name = "rocksdb-sys" version = "0.3.0" -source = "git+https://github.com/ethcore/rust-rocksdb#6472a9dce16c267a3acec2ee6fd01d1bf8de4913" +source = "git+https://github.com/ethcore/rust-rocksdb#dd597245bfcb621c6ffc45478e1fda0b05d2f409" dependencies = [ "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethash/src/compute.rs b/ethash/src/compute.rs index 2b2e5dcf5..bb297d709 100644 --- a/ethash/src/compute.rs +++ b/ethash/src/compute.rs @@ -271,11 +271,12 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64) let page_size = 4 * MIX_WORDS; let num_full_pages = (full_size / page_size) as u32; + let cache: &[Node] = &light.cache; // deref once for better performance for i in 0..(ETHASH_ACCESSES as u32) { let index = fnv_hash(f_mix.get_unchecked(0).as_words().get_unchecked(0) ^ i, *mix.get_unchecked(0).as_words().get_unchecked((i as usize) % MIX_WORDS)) % num_full_pages; for n in 0..MIX_NODES { - let tmp_node = calculate_dag_item(index * MIX_NODES as u32 + n as u32, light); + let tmp_node = calculate_dag_item(index * MIX_NODES as u32 + n as u32, cache); for w in 0..NODE_WORDS { *mix.get_unchecked_mut(n).as_words_mut().get_unchecked_mut(w) = fnv_hash(*mix.get_unchecked(n).as_words().get_unchecked(w), *tmp_node.as_words().get_unchecked(w)); } @@ -306,18 +307,17 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64) } } -fn calculate_dag_item(node_index: u32, light: &Light) -> Node { +fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node { unsafe { - let num_parent_nodes = light.cache.len(); - let cache_nodes = &light.cache; - let init = cache_nodes.get_unchecked(node_index as usize % num_parent_nodes); + let num_parent_nodes = cache.len(); + let init = cache.get_unchecked(node_index as usize % num_parent_nodes); let mut ret = init.clone(); *ret.as_words_mut().get_unchecked_mut(0) ^= node_index; sha3::sha3_512(ret.bytes.as_mut_ptr(), ret.bytes.len(), ret.bytes.as_ptr(), ret.bytes.len()); for i in 0..ETHASH_DATASET_PARENTS { let parent_index = fnv_hash(node_index ^ i, *ret.as_words().get_unchecked(i as usize % NODE_WORDS)) % num_parent_nodes as u32; - let parent = cache_nodes.get_unchecked(parent_index as usize); + let parent = cache.get_unchecked(parent_index as usize); for w in 0..NODE_WORDS { *ret.as_words_mut().get_unchecked_mut(w) = fnv_hash(*ret.as_words().get_unchecked(w), *parent.as_words().get_unchecked(w)); } diff --git a/ethcore/src/account.rs b/ethcore/src/account.rs index 5119b2e33..3204eddcd 100644 --- a/ethcore/src/account.rs +++ b/ethcore/src/account.rs @@ -35,6 +35,8 @@ pub struct Account { code_hash: Option, // Code cache of the account. code_cache: Bytes, + // Account is new or has been modified + dirty: bool, } impl Account { @@ -47,7 +49,8 @@ impl Account { storage_root: SHA3_NULL_RLP, storage_overlay: RefCell::new(storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()), code_hash: Some(code.sha3()), - code_cache: code + code_cache: code, + dirty: true, } } @@ -59,7 +62,8 @@ impl Account { storage_root: SHA3_NULL_RLP, storage_overlay: RefCell::new(pod.storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()), code_hash: Some(pod.code.sha3()), - code_cache: pod.code + code_cache: pod.code, + dirty: true, } } @@ -72,6 +76,7 @@ impl Account { storage_overlay: RefCell::new(HashMap::new()), code_hash: Some(SHA3_EMPTY), code_cache: vec![], + dirty: true, } } @@ -85,6 +90,7 @@ impl Account { storage_overlay: RefCell::new(HashMap::new()), code_hash: Some(r.val_at(3)), code_cache: vec![], + dirty: false, } } @@ -98,6 +104,7 @@ impl Account { storage_overlay: RefCell::new(HashMap::new()), code_hash: None, code_cache: vec![], + dirty: true, } } @@ -106,6 +113,7 @@ impl Account { pub fn init_code(&mut self, code: Bytes) { assert!(self.code_hash.is_none()); self.code_cache = code; + self.dirty = true; } /// Reset this account's code to the given code. @@ -117,6 +125,7 @@ impl Account { /// Set (and cache) the contents of the trie's storage at `key` to `value`. pub fn set_storage(&mut self, key: H256, value: H256) { self.storage_overlay.borrow_mut().insert(key, (Filth::Dirty, value)); + self.dirty = true; } /// Get (and cache) the contents of the trie's storage at `key`. @@ -172,6 +181,10 @@ impl Account { !self.code_cache.is_empty() || (self.code_cache.is_empty() && self.code_hash == Some(SHA3_EMPTY)) } + /// Is this a new or modified account? + pub fn is_dirty(&self) -> bool { + self.dirty + } /// Provide a database to get `code_hash`. Should not be called if it is a contract without code. pub fn cache_code(&mut self, db: &AccountDB) -> bool { // TODO: fill out self.code_cache; @@ -201,16 +214,23 @@ impl Account { pub fn storage_overlay(&self) -> Ref> { self.storage_overlay.borrow() } /// Increment the nonce of the account by one. - pub fn inc_nonce(&mut self) { self.nonce = self.nonce + U256::from(1u8); } + pub fn inc_nonce(&mut self) { + self.nonce = self.nonce + U256::from(1u8); + self.dirty = true; + } /// Increment the nonce of the account by one. - pub fn add_balance(&mut self, x: &U256) { self.balance = self.balance + *x; } + pub fn add_balance(&mut self, x: &U256) { + self.balance = self.balance + *x; + self.dirty = true; + } /// Increment the nonce of the account by one. /// Panics if balance is less than `x` pub fn sub_balance(&mut self, x: &U256) { assert!(self.balance >= *x); self.balance = self.balance - *x; + self.dirty = true; } /// Commit the `storage_overlay` to the backing DB and update `storage_root`. diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index b736a8c77..8d3cf9eb3 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -359,6 +359,12 @@ impl BlockChain { bc } + /// Returns true if the given parent block has given child + /// (though not necessarily a part of the canon chain). + fn is_known_child(&self, parent: &H256, hash: &H256) -> bool { + self.extras_db.read_with_cache(&self.block_details, parent).map_or(false, |d| d.children.contains(hash)) + } + /// Set the cache configuration. pub fn configure_cache(&self, pref_cache_size: usize, max_cache_size: usize) { self.pref_cache_size.store(pref_cache_size, AtomicOrder::Relaxed); @@ -463,7 +469,7 @@ impl BlockChain { let header = block.header_view(); let hash = header.sha3(); - if self.is_known(&hash) { + if self.is_known_child(&header.parent_hash(), &hash) { return ImportRoute::none(); } diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index d35a01be7..74175d4a4 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -145,6 +145,7 @@ pub struct Client { notify: RwLock>>, queue_transactions: AtomicUsize, previous_enode: Mutex>, + last_hashes: RwLock>, } const HISTORY: u64 = 1200; @@ -232,6 +233,7 @@ impl Client { notify: RwLock::new(None), queue_transactions: AtomicUsize::new(0), previous_enode: Mutex::new(None), + last_hashes: RwLock::new(VecDeque::new()), }; Ok(Arc::new(client)) } @@ -253,6 +255,14 @@ impl Client { } fn build_last_hashes(&self, parent_hash: H256) -> LastHashes { + { + let hashes = self.last_hashes.read(); + if hashes.front().map_or(false, |h| h == &parent_hash) { + let mut res = Vec::from(hashes.clone()); + res.resize(256, H256::default()); + return res; + } + } let mut last_hashes = LastHashes::new(); last_hashes.resize(256, H256::new()); last_hashes[0] = parent_hash; @@ -264,6 +274,8 @@ impl Client { None => break, } } + let mut cached_hashes = self.last_hashes.write(); + *cached_hashes = VecDeque::from(last_hashes.clone()); last_hashes } @@ -418,6 +430,7 @@ impl Client { fn commit_block(&self, block: B, hash: &H256, block_data: &[u8]) -> ImportRoute where B: IsBlock + Drain { let number = block.header().number(); + let parent = block.header().parent_hash().clone(); // Are we committing an era? let ancient = if number >= HISTORY { let n = number - HISTORY; @@ -445,9 +458,20 @@ impl Client { enacted: route.enacted.clone(), retracted: route.retracted.len() }); + self.update_last_hashes(&parent, hash); route } + fn update_last_hashes(&self, parent: &H256, hash: &H256) { + let mut hashes = self.last_hashes.write(); + if hashes.front().map_or(false, |h| h == parent) { + if hashes.len() > 255 { + hashes.pop_back(); + } + hashes.push_front(hash.clone()); + } + } + /// Import transactions from the IO queue pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize { let _timer = PerfTimer::new("import_queued_transactions"); diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index 3c1b5e7c0..ca84d4ec1 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -235,6 +235,7 @@ impl State { // TODO uncomment once to_pod() works correctly. // trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod())); self.commit(); + self.clear(); let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs); // trace!("Transaction receipt: {:?}", receipt); Ok(ApplyOutcome{receipt: receipt, trace: e.trace}) @@ -248,12 +249,12 @@ impl State { // TODO: is this necessary or can we dispense with the `ref mut a` for just `a`? for (address, ref mut a) in accounts.iter_mut() { match a { - &mut&mut Some(ref mut account) => { + &mut&mut Some(ref mut account) if account.is_dirty() => { let mut account_db = AccountDBMut::new(db, address); account.commit_storage(trie_factory, &mut account_db); account.commit_code(&mut account_db); } - &mut&mut None => {} + _ => {} } } @@ -261,8 +262,9 @@ impl State { let mut trie = trie_factory.from_existing(db, root).unwrap(); for (address, ref a) in accounts.iter() { match **a { - Some(ref account) => trie.insert(address, &account.rlp()), + Some(ref account) if account.is_dirty() => trie.insert(address, &account.rlp()), None => trie.remove(address), + _ => (), } } } @@ -274,6 +276,11 @@ impl State { Self::commit_into(&self.trie_factory, self.db.as_hashdb_mut(), &mut self.root, self.cache.borrow_mut().deref_mut()); } + /// Clear state cache + pub fn clear(&mut self) { + self.cache.borrow_mut().clear(); + } + #[cfg(test)] #[cfg(feature = "json-tests")] /// Populate the state from `accounts`. diff --git a/util/src/kvdb.rs b/util/src/kvdb.rs index f6d25ae51..729583c52 100644 --- a/util/src/kvdb.rs +++ b/util/src/kvdb.rs @@ -177,16 +177,13 @@ impl Database { opts.set_block_based_table_factory(&block_opts); opts.set_prefix_extractor_fixed_size(size); if let Some(cache_size) = config.cache_size { - block_opts.set_cache(Cache::new(cache_size * 1024 * 256)); - opts.set_write_buffer_size(cache_size * 1024 * 256); + block_opts.set_cache(Cache::new(cache_size * 1024 * 1024)); } } else if let Some(cache_size) = config.cache_size { let mut block_opts = BlockBasedOptions::new(); // half goes to read cache - block_opts.set_cache(Cache::new(cache_size * 1024 * 256)); + block_opts.set_cache(Cache::new(cache_size * 1024 * 1024)); opts.set_block_based_table_factory(&block_opts); - // quarter goes to each of the two write buffers - opts.set_write_buffer_size(cache_size * 1024 * 256); } let mut write_opts = WriteOptions::new(); @@ -207,12 +204,12 @@ impl Database { /// Insert a key-value pair in the transaction. Any existing value value will be overwritten. pub fn put(&self, key: &[u8], value: &[u8]) -> Result<(), String> { - self.db.put(key, value) + self.db.put_opt(key, value, &self.write_opts) } /// Delete value by key. pub fn delete(&self, key: &[u8]) -> Result<(), String> { - self.db.delete(key) + self.db.delete_opt(key, &self.write_opts) } /// Commit transaction to database.