Performance optimizations (#1642)

* Optimize ethash verification

* disable WAL for puts

* Clear account cache after commit

* Commit only modified accounts

* Optimize existing block check

* Cache last hashes
This commit is contained in:
Arkadiy Paronyan
2016-07-17 09:18:15 +02:00
committed by Gav Wood
parent 5dba43178b
commit 5ab18d1313
7 changed files with 77 additions and 23 deletions

View File

@@ -35,6 +35,8 @@ pub struct Account {
code_hash: Option<H256>,
// 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<HashMap<H256, (Filth, H256)>> { 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`.

View File

@@ -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();
}

View File

@@ -145,6 +145,7 @@ pub struct Client {
notify: RwLock<Option<Weak<ChainNotify>>>,
queue_transactions: AtomicUsize,
previous_enode: Mutex<Option<String>>,
last_hashes: RwLock<VecDeque<H256>>,
}
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<B>(&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");

View File

@@ -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`.