core/state, trie: avoid unnecessary storage trie load+commit

This commit is contained in:
Martin Holst Swende 2020-01-04 13:56:26 +01:00
parent 853a69141a
commit d76431b8df
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
4 changed files with 28 additions and 5 deletions

View file

@ -272,10 +272,15 @@ func (s *stateObject) finalise() {
} }
// updateTrie writes cached storage modifications into the object's storage trie. // updateTrie writes cached storage modifications into the object's storage trie.
// It will return nil if the trie has not been loaded and no changes have been made
// Note: It may return non-nil if the trie is already loaded due to previous changes
// in the same block
func (s *stateObject) updateTrie(db Database) Trie { func (s *stateObject) updateTrie(db Database) Trie {
// Make sure all dirty slots are finalized into the pending storage area // Make sure all dirty slots are finalized into the pending storage area
s.finalise() s.finalise()
if len(s.pendingStorage) == 0{
return s.trie
}
// Track the amount of time wasted on updating the storge trie // Track the amount of time wasted on updating the storge trie
if metrics.EnabledExpensive { if metrics.EnabledExpensive {
defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now()) defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now())
@ -305,8 +310,10 @@ func (s *stateObject) updateTrie(db Database) Trie {
// UpdateRoot sets the trie root to the current root hash of // UpdateRoot sets the trie root to the current root hash of
func (s *stateObject) updateRoot(db Database) { func (s *stateObject) updateRoot(db Database) {
s.updateTrie(db) if s.updateTrie(db) == nil{
// No changes, storage trie is not even loaded
return
}
// Track the amount of time wasted on hashing the storge trie // Track the amount of time wasted on hashing the storge trie
if metrics.EnabledExpensive { if metrics.EnabledExpensive {
defer func(start time.Time) { s.db.StorageHashes += time.Since(start) }(time.Now()) defer func(start time.Time) { s.db.StorageHashes += time.Since(start) }(time.Now())
@ -317,7 +324,10 @@ func (s *stateObject) updateRoot(db Database) {
// CommitTrie the storage trie of the object to db. // CommitTrie the storage trie of the object to db.
// This updates the trie root. // This updates the trie root.
func (s *stateObject) CommitTrie(db Database) error { func (s *stateObject) CommitTrie(db Database) error {
s.updateTrie(db) if s.updateTrie(db) == nil{
// No changes, storage trie is not even loaded
return nil
}
if s.dbErr != nil { if s.dbErr != nil {
return s.dbErr return s.dbErr
} }

View file

@ -330,7 +330,8 @@ func (s *StateDB) StorageTrie(addr common.Address) Trie {
return nil return nil
} }
cpy := stateObject.deepCopy(s) cpy := stateObject.deepCopy(s)
return cpy.updateTrie(s.db) cpy.updateTrie(s.db)
return cpy.getTrie(s.db)
} }
func (s *StateDB) HasSuicided(addr common.Address) bool { func (s *StateDB) HasSuicided(addr common.Address) bool {

View file

@ -67,6 +67,12 @@ func returnCommitterToPool(h *committer) {
committerPool.Put(h) committerPool.Put(h)
} }
// commitNeeded returns 'false' if the given node is already in sync with db
func (h *committer) commitNeeded(n node) bool {
hash, dirty := n.cache()
return hash == nil || dirty
}
// hash collapses a node down into a hash node, also returning a copy of the // hash collapses a node down into a hash node, also returning a copy of the
// original node initialized with the computed hash to replace the original one. // original node initialized with the computed hash to replace the original one.
func (h *committer) commit(n node, db *Database, force bool) (node, error) { func (h *committer) commit(n node, db *Database, force bool) (node, error) {

View file

@ -441,6 +441,12 @@ func (t *Trie) Commit(onleaf LeafCallback) (root common.Hash, err error) {
rootHash := t.Hash() rootHash := t.Hash()
h := newCommitter(onleaf) h := newCommitter(onleaf)
defer returnCommitterToPool(h) defer returnCommitterToPool(h)
// Do a quick check if we really need to commit, before we spin
// up goroutines. This can happen e.g. if we load a trie for reading storage
// values, but don't write to it.
if !h.commitNeeded(t.root) {
return rootHash, nil
}
var wg sync.WaitGroup var wg sync.WaitGroup
if onleaf != nil { if onleaf != nil {
wg.Add(1) wg.Add(1)