core/state: paralleize the account trie hash along with storages

This commit is contained in:
Gary Rong 2025-10-28 14:59:27 +08:00
parent cfa3b96103
commit 03a4b24aca

View file

@ -818,8 +818,11 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
// method will internally call a blocking trie fetch from the prefetcher, // method will internally call a blocking trie fetch from the prefetcher,
// so there's no need to explicitly wait for the prefetchers to finish. // so there's no need to explicitly wait for the prefetchers to finish.
var ( var (
start = time.Now()
workers errgroup.Group workers errgroup.Group
start = time.Now()
storageUpdates atomic.Int64
pendingAccounts = make(chan *stateObject, len(s.mutations))
) )
if s.db.TrieDB().IsVerkle() { if s.db.TrieDB().IsVerkle() {
// Whilst MPT storage tries are independent, Verkle has one single trie // Whilst MPT storage tries are independent, Verkle has one single trie
@ -846,6 +849,8 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
s.witness.AddState(obj.trie.Witness()) s.witness.AddState(obj.trie.Witness())
} }
} }
pendingAccounts <- obj
storageUpdates.Store(time.Since(start).Nanoseconds()) // overwrite with the longest storage update runtime
return nil return nil
}) })
} }
@ -898,9 +903,6 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
} }
} }
} }
workers.Wait()
s.StorageUpdates += time.Since(start)
// Now we're about to start to write changes to the trie. The trie is so far // Now we're about to start to write changes to the trie. The trie is so far
// _untouched_. We can check with the prefetcher, if it can give us a trie // _untouched_. We can check with the prefetcher, if it can give us a trie
// which has the same root, but also has some content loaded into it. // which has the same root, but also has some content loaded into it.
@ -908,7 +910,6 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
// Don't check prefetcher if verkle trie has been used. In the context of verkle, // Don't check prefetcher if verkle trie has been used. In the context of verkle,
// only a single trie is used for state hashing. Replacing a non-nil verkle tree // only a single trie is used for state hashing. Replacing a non-nil verkle tree
// here could result in losing uncommitted changes from storage. // here could result in losing uncommitted changes from storage.
start = time.Now()
if s.prefetcher != nil { if s.prefetcher != nil {
if trie := s.prefetcher.trie(common.Hash{}, s.originalRoot); trie == nil { if trie := s.prefetcher.trie(common.Hash{}, s.originalRoot); trie == nil {
log.Error("Failed to retrieve account pre-fetcher trie") log.Error("Failed to retrieve account pre-fetcher trie")
@ -927,8 +928,9 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
// into a shortnode. This requires `B` to be resolved from disk. // into a shortnode. This requires `B` to be resolved from disk.
// Whereas if the created node is handled first, then the collapse is avoided, and `B` is not resolved. // Whereas if the created node is handled first, then the collapse is avoided, and `B` is not resolved.
var ( var (
usedAddrs []common.Address usedAddrs []common.Address
deletedAddrs []common.Address deletedAddrs []common.Address
pendingUpdates int
) )
for addr, op := range s.mutations { for addr, op := range s.mutations {
if op.applied { if op.applied {
@ -939,16 +941,24 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
if op.isDelete() { if op.isDelete() {
deletedAddrs = append(deletedAddrs, addr) deletedAddrs = append(deletedAddrs, addr)
} else { } else {
s.updateStateObject(s.stateObjects[addr]) pendingUpdates += 1
s.AccountUpdated += 1 s.AccountUpdated += 1
} }
usedAddrs = append(usedAddrs, addr) // Copy needed for closure usedAddrs = append(usedAddrs, addr) // Copy needed for closure
} }
for pendingUpdates > 0 {
select {
case obj := <-pendingAccounts:
s.updateStateObject(obj)
pendingUpdates--
}
}
for _, deletedAddr := range deletedAddrs { for _, deletedAddr := range deletedAddrs {
s.deleteStateObject(deletedAddr) s.deleteStateObject(deletedAddr)
s.AccountDeleted += 1 s.AccountDeleted += 1
} }
s.AccountUpdates += time.Since(start) s.AccountUpdates += time.Since(start)
s.StorageUpdates += time.Duration(storageUpdates.Load())
if s.prefetcher != nil { if s.prefetcher != nil {
s.prefetcher.used(common.Hash{}, s.originalRoot, usedAddrs, nil) s.prefetcher.used(common.Hash{}, s.originalRoot, usedAddrs, nil)