@@ -24,6 +24,7 @@ import (
24
24
"slices"
25
25
"sort"
26
26
"sync"
27
+ "sync/atomic"
27
28
"time"
28
29
29
30
"github.com/ethereum/go-ethereum/common"
@@ -92,10 +93,12 @@ type StateDB struct {
92
93
93
94
// These maps hold the state changes (including the corresponding
94
95
// original value) that occurred in this **block**.
95
- accounts map [common.Hash ][]byte // The mutated accounts in 'slim RLP' encoding
96
+ accounts map [common.Hash ][]byte // The mutated accounts in 'slim RLP' encoding
97
+ accountsOrigin map [common.Address ][]byte // The original value of mutated accounts in 'slim RLP' encoding
98
+
96
99
storages map [common.Hash ]map [common.Hash ][]byte // The mutated slots in prefix-zero trimmed rlp format
97
- accountsOrigin map [common.Address ][]byte // The original value of mutated accounts in 'slim RLP' encoding
98
100
storagesOrigin map [common.Address ]map [common.Hash ][]byte // The original value of mutated slots in prefix-zero trimmed rlp format
101
+ storagesLock sync.Mutex // Mutex protecting the maps during concurrent updates/commits
99
102
100
103
// This map holds 'live' objects, which will get modified while
101
104
// processing a state transition.
@@ -161,9 +164,9 @@ type StateDB struct {
161
164
TrieDBCommits time.Duration
162
165
163
166
AccountUpdated int
164
- StorageUpdated int
167
+ StorageUpdated atomic. Int64
165
168
AccountDeleted int
166
- StorageDeleted int
169
+ StorageDeleted atomic. Int64
167
170
168
171
// Testing hooks
169
172
onCommit func (states * triestate.Set ) // Hook invoked when commit is performed
@@ -210,7 +213,7 @@ func (s *StateDB) SetLogger(l *tracing.Hooks) {
210
213
// commit phase, most of the needed data is already hot.
211
214
func (s * StateDB ) StartPrefetcher (namespace string ) {
212
215
if s .prefetcher != nil {
213
- s .prefetcher .terminate ()
216
+ s .prefetcher .terminate (false )
214
217
s .prefetcher .report ()
215
218
s .prefetcher = nil
216
219
}
@@ -223,7 +226,7 @@ func (s *StateDB) StartPrefetcher(namespace string) {
223
226
// from the gathered metrics.
224
227
func (s * StateDB ) StopPrefetcher () {
225
228
if s .prefetcher != nil {
226
- s .prefetcher .terminate ()
229
+ s .prefetcher .terminate (false )
227
230
s .prefetcher .report ()
228
231
s .prefetcher = nil
229
232
}
@@ -542,9 +545,6 @@ func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common
542
545
543
546
// updateStateObject writes the given object to the trie.
544
547
func (s * StateDB ) updateStateObject (obj * stateObject ) {
545
- // Track the amount of time wasted on updating the account from the trie
546
- defer func (start time.Time ) { s .AccountUpdates += time .Since (start ) }(time .Now ())
547
-
548
548
// Encode the account and update the account trie
549
549
addr := obj .Address ()
550
550
if err := s .trie .UpdateAccount (addr , & obj .data ); err != nil {
@@ -573,10 +573,6 @@ func (s *StateDB) updateStateObject(obj *stateObject) {
573
573
574
574
// deleteStateObject removes the given object from the state trie.
575
575
func (s * StateDB ) deleteStateObject (addr common.Address ) {
576
- // Track the amount of time wasted on deleting the account from the trie
577
- defer func (start time.Time ) { s .AccountUpdates += time .Since (start ) }(time .Now ())
578
-
579
- // Delete the account from the trie
580
576
if err := s .trie .DeleteAccount (addr ); err != nil {
581
577
s .setError (fmt .Errorf ("deleteStateObject (%x) error: %v" , addr [:], err ))
582
578
}
@@ -835,48 +831,40 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
835
831
// Finalise all the dirty storage states and write them into the tries
836
832
s .Finalise (deleteEmptyObjects )
837
833
838
- // If there was a trie prefetcher operating, terminate it (blocking until
839
- // all tasks finish) and then proceed with the trie hashing.
840
- var subfetchers chan * subfetcher
834
+ // If there was a trie prefetcher operating, terminate it async so that the
835
+ // individual storage tries can be updated as soon as the disk load finishes.
841
836
if s .prefetcher != nil {
842
- subfetchers = s .prefetcher .terminateAsync ( )
837
+ s .prefetcher .terminate ( true )
843
838
defer func () {
844
839
s .prefetcher .report ()
845
840
s .prefetcher = nil // Pre-byzantium, unset any used up prefetcher
846
841
}()
847
842
}
848
- // Although naively it makes sense to retrieve the account trie and then do
849
- // the contract storage and account updates sequentially, that short circuits
850
- // the account prefetcher. Instead, let's process all the storage updates
851
- // first, giving the account prefetches just a few more milliseconds of time
852
- // to pull useful data from disk.
853
- start := time .Now ()
854
-
855
- updated := make (map [common.Address ]struct {})
856
- if subfetchers != nil {
857
- for f := range subfetchers {
858
- if op , ok := s .mutations [f .addr ]; ok {
859
- if ! op .applied && ! op .isDelete () {
860
- s .stateObjects [f .addr ].updateRoot ()
861
- }
862
- updated [f .addr ] = struct {}{}
863
- }
864
- }
865
- }
843
+ // Process all storage updates concurrently. The state object update root
844
+ // method will internally call a blocking trie fetch from the prefetcher,
845
+ // so there's no need to explicitly wait for the prefetchers to finish.
846
+ var (
847
+ start = time .Now ()
848
+ workers errgroup.Group
849
+ )
866
850
for addr , op := range s .mutations {
867
- if op .applied {
868
- continue
869
- }
870
- if op .isDelete () {
851
+ if op .applied || op .isDelete () {
871
852
continue
872
853
}
873
- s .stateObjects [addr ].updateRoot ()
854
+ obj := s .stateObjects [addr ] // closure for the task runner below
855
+ workers .Go (func () error {
856
+ obj .updateRoot ()
857
+ return nil
858
+ })
874
859
}
860
+ workers .Wait ()
875
861
s .StorageUpdates += time .Since (start )
876
862
877
863
// Now we're about to start to write changes to the trie. The trie is so far
878
864
// _untouched_. We can check with the prefetcher, if it can give us a trie
879
865
// which has the same root, but also has some content loaded into it.
866
+ start = time .Now ()
867
+
880
868
if s .prefetcher != nil {
881
869
if trie , err := s .prefetcher .trie (common.Hash {}, s .originalRoot ); err != nil {
882
870
log .Error ("Failed to retrieve account pre-fetcher trie" , "err" , err )
@@ -916,6 +904,8 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
916
904
s .deleteStateObject (deletedAddr )
917
905
s .AccountDeleted += 1
918
906
}
907
+ s .AccountUpdates += time .Since (start )
908
+
919
909
if s .prefetcher != nil {
920
910
s .prefetcher .used (common.Hash {}, s .originalRoot , usedAddrs )
921
911
}
@@ -1258,15 +1248,16 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er
1258
1248
return common.Hash {}, err
1259
1249
}
1260
1250
accountUpdatedMeter .Mark (int64 (s .AccountUpdated ))
1261
- storageUpdatedMeter .Mark (int64 ( s .StorageUpdated ))
1251
+ storageUpdatedMeter .Mark (s .StorageUpdated . Load ( ))
1262
1252
accountDeletedMeter .Mark (int64 (s .AccountDeleted ))
1263
- storageDeletedMeter .Mark (int64 ( s .StorageDeleted ))
1253
+ storageDeletedMeter .Mark (s .StorageDeleted . Load ( ))
1264
1254
accountTrieUpdatedMeter .Mark (int64 (accountTrieNodesUpdated ))
1265
1255
accountTrieDeletedMeter .Mark (int64 (accountTrieNodesDeleted ))
1266
1256
storageTriesUpdatedMeter .Mark (int64 (storageTrieNodesUpdated ))
1267
1257
storageTriesDeletedMeter .Mark (int64 (storageTrieNodesDeleted ))
1268
1258
s .AccountUpdated , s .AccountDeleted = 0 , 0
1269
- s .StorageUpdated , s .StorageDeleted = 0 , 0
1259
+ s .StorageUpdated .Store (0 )
1260
+ s .StorageDeleted .Store (0 )
1270
1261
1271
1262
// If snapshotting is enabled, update the snapshot tree with this new version
1272
1263
if s .snap != nil {
0 commit comments