diff --git a/core/blockchain.go b/core/blockchain.go index b7acd12aca78..22df1e366205 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2136,14 +2136,12 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s if statedb.StorageLoaded != 0 { storageReadSingleTimer.Update(statedb.StorageReads / time.Duration(statedb.StorageLoaded)) } - accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation) - storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation) - accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation) - triehash := statedb.AccountHashes // The time spent on tries hashing - trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update - blockExecutionTimer.Update(ptime - (statedb.AccountReads + statedb.StorageReads)) // The time spent on EVM processing - blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation - blockCrossValidationTimer.Update(xvtime) // The time spent on stateless cross validation + accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation) + storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation) + accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation) + blockExecutionTimer.Update(ptime - (statedb.AccountReads + statedb.StorageReads)) // The time spent on EVM processing + blockValidationTimer.Update(vtime - (statedb.AccountHashes + statedb.AccountUpdates)) // The time spent on block validation + blockCrossValidationTimer.Update(xvtime) // The time spent on stateless cross validation // Write the block to the chain and get the status. var ( diff --git a/core/state/statedb.go b/core/state/statedb.go index b770698255e4..3f7dfcab8dd2 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -818,8 +818,11 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // method will internally call a blocking trie fetch from the prefetcher, // so there's no need to explicitly wait for the prefetchers to finish. var ( - start = time.Now() workers errgroup.Group + start = time.Now() + + storageUpdates atomic.Int64 + pendingAccounts = make(chan *stateObject, len(s.mutations)) ) if s.db.TrieDB().IsVerkle() { // 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()) } } + pendingAccounts <- obj + storageUpdates.Store(time.Since(start).Nanoseconds()) // overwrite with the longest storage update runtime 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 // _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. @@ -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, // only a single trie is used for state hashing. Replacing a non-nil verkle tree // here could result in losing uncommitted changes from storage. - start = time.Now() if s.prefetcher != nil { if trie := s.prefetcher.trie(common.Hash{}, s.originalRoot); trie == nil { 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. // Whereas if the created node is handled first, then the collapse is avoided, and `B` is not resolved. var ( - usedAddrs []common.Address - deletedAddrs []common.Address + usedAddrs []common.Address + deletedAddrs []common.Address + pendingUpdates int ) for addr, op := range s.mutations { if op.applied { @@ -939,16 +941,24 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { if op.isDelete() { deletedAddrs = append(deletedAddrs, addr) } else { - s.updateStateObject(s.stateObjects[addr]) + pendingUpdates += 1 s.AccountUpdated += 1 } usedAddrs = append(usedAddrs, addr) // Copy needed for closure } + for pendingUpdates > 0 { + select { + case obj := <-pendingAccounts: + s.updateStateObject(obj) + pendingUpdates-- + } + } for _, deletedAddr := range deletedAddrs { s.deleteStateObject(deletedAddr) s.AccountDeleted += 1 } s.AccountUpdates += time.Since(start) + s.StorageUpdates += time.Duration(storageUpdates.Load()) if s.prefetcher != nil { s.prefetcher.used(common.Hash{}, s.originalRoot, usedAddrs, nil)