@@ -63,7 +63,7 @@ func (n *proofList) Delete(key []byte) error {
63
63
// * Accounts
64
64
type StateDB struct {
65
65
db Database
66
- prefetcher * TriePrefetcher
66
+ prefetcher * triePrefetcher
67
67
originalRoot common.Hash // The pre-state root, before any changes were made
68
68
trie Trie
69
69
hasher crypto.KeccakState
@@ -149,10 +149,25 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
149
149
return sdb , nil
150
150
}
151
151
152
- func (s * StateDB ) UsePrefetcher (prefetcher * TriePrefetcher ) {
153
- if prefetcher != nil {
154
- s .prefetcher = prefetcher
155
- s .prefetcher .Resume (s .originalRoot )
152
+ // StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
153
+ // state trie concurrently while the state is mutated so that when we reach the
154
+ // commit phase, most of the needed data is already hot.
155
+ func (s * StateDB ) StartPrefetcher (namespace string ) {
156
+ if s .prefetcher != nil {
157
+ s .prefetcher .close ()
158
+ s .prefetcher = nil
159
+ }
160
+ if s .snap != nil {
161
+ s .prefetcher = newTriePrefetcher (s .db , s .originalRoot , namespace )
162
+ }
163
+ }
164
+
165
+ // StopPrefetcher terminates a running prefetcher and reports any leftover stats
166
+ // from the gathered metrics.
167
+ func (s * StateDB ) StopPrefetcher () {
168
+ if s .prefetcher != nil {
169
+ s .prefetcher .close ()
170
+ s .prefetcher = nil
156
171
}
157
172
}
158
173
@@ -167,37 +182,6 @@ func (s *StateDB) Error() error {
167
182
return s .dbErr
168
183
}
169
184
170
- // Reset clears out all ephemeral state objects from the state db, but keeps
171
- // the underlying state trie to avoid reloading data for the next operations.
172
- func (s * StateDB ) Reset (root common.Hash ) error {
173
- tr , err := s .db .OpenTrie (root )
174
- if err != nil {
175
- return err
176
- }
177
- s .trie = tr
178
- s .stateObjects = make (map [common.Address ]* stateObject )
179
- s .stateObjectsPending = make (map [common.Address ]struct {})
180
- s .stateObjectsDirty = make (map [common.Address ]struct {})
181
- s .thash = common.Hash {}
182
- s .bhash = common.Hash {}
183
- s .txIndex = 0
184
- s .logs = make (map [common.Hash ][]* types.Log )
185
- s .logSize = 0
186
- s .preimages = make (map [common.Hash ][]byte )
187
- s .clearJournalAndRefund ()
188
-
189
- if s .snaps != nil {
190
- s .snapAccounts , s .snapDestructs , s .snapStorage = nil , nil , nil
191
- if s .snap = s .snaps .Snapshot (root ); s .snap != nil {
192
- s .snapDestructs = make (map [common.Hash ]struct {})
193
- s .snapAccounts = make (map [common.Hash ][]byte )
194
- s .snapStorage = make (map [common.Hash ]map [common.Hash ][]byte )
195
- }
196
- }
197
- s .accessList = newAccessList ()
198
- return nil
199
- }
200
-
201
185
func (s * StateDB ) AddLog (log * types.Log ) {
202
186
s .journal .append (addLogChange {txhash : s .thash })
203
187
@@ -737,6 +721,13 @@ func (s *StateDB) Copy() *StateDB {
737
721
// However, it doesn't cost us much to copy an empty list, so we do it anyway
738
722
// to not blow up if we ever decide copy it in the middle of a transaction
739
723
state .accessList = s .accessList .Copy ()
724
+
725
+ // If there's a prefetcher running, make an inactive copy of it that can
726
+ // only access data but does not actively preload (since the user will not
727
+ // know that they need to explicitly terminate an active copy).
728
+ if s .prefetcher != nil {
729
+ state .prefetcher = s .prefetcher .copy ()
730
+ }
740
731
return state
741
732
}
742
733
@@ -773,7 +764,7 @@ func (s *StateDB) GetRefund() uint64 {
773
764
// the journal as well as the refunds. Finalise, however, will not push any updates
774
765
// into the tries just yet. Only IntermediateRoot or Commit will do that.
775
766
func (s * StateDB ) Finalise (deleteEmptyObjects bool ) {
776
- var addressesToPrefetch []common. Address
767
+ addressesToPrefetch := make ([][] byte , 0 , len ( s . journal . dirties ))
777
768
for addr := range s .journal .dirties {
778
769
obj , exist := s .stateObjects [addr ]
779
770
if ! exist {
@@ -798,21 +789,19 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
798
789
delete (s .snapStorage , obj .addrHash ) // Clear out any previously updated storage data (may be recreated via a ressurrect)
799
790
}
800
791
} else {
801
- obj .finalise ()
792
+ obj .finalise (true ) // Prefetch slots in the background
802
793
}
803
794
s .stateObjectsPending [addr ] = struct {}{}
804
795
s .stateObjectsDirty [addr ] = struct {}{}
796
+
805
797
// At this point, also ship the address off to the precacher. The precacher
806
798
// will start loading tries, and when the change is eventually committed,
807
799
// the commit-phase will be a lot faster
808
- if s .prefetcher != nil {
809
- addressesToPrefetch = append (addressesToPrefetch , addr )
810
- }
800
+ addressesToPrefetch = append (addressesToPrefetch , common .CopyBytes (addr [:])) // Copy needed for closure
811
801
}
812
- if s .prefetcher != nil {
813
- s .prefetcher .PrefetchAddresses ( addressesToPrefetch )
802
+ if s .prefetcher != nil && len ( addressesToPrefetch ) > 0 {
803
+ s .prefetcher .prefetch ( s . originalRoot , addressesToPrefetch )
814
804
}
815
-
816
805
// Invalidate journal because reverting across transactions is not allowed.
817
806
s .clearJournalAndRefund ()
818
807
}
@@ -824,29 +813,49 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
824
813
// Finalise all the dirty storage states and write them into the tries
825
814
s .Finalise (deleteEmptyObjects )
826
815
827
- // Now we're about to start to write changes to the trie. The trie is so
828
- // far _untouched_. We can check with the prefetcher, if it can give us
829
- // a trie which has the same root, but also has some content loaded into it.
830
- // If so, use that one instead.
816
+ // If there was a trie prefetcher operating, it gets aborted and irrevocably
817
+ // modified after we start retrieving tries. Remove it from the statedb after
818
+ // this round of use.
819
+ //
820
+ // This is weird pre-byzantium since the first tx runs with a prefetcher and
821
+ // the remainder without, but pre-byzantium even the initial prefetcher is
822
+ // useless, so no sleep lost.
823
+ prefetcher := s .prefetcher
831
824
if s .prefetcher != nil {
832
- s .prefetcher .Pause ()
833
- // We only want to do this _once_, if someone calls IntermediateRoot again,
834
- // we shouldn't fetch the trie again
835
- if s .originalRoot != (common.Hash {}) {
836
- if trie := s .prefetcher .GetTrie (s .originalRoot ); trie != nil {
837
- s .trie = trie
838
- }
839
- s .originalRoot = common.Hash {}
825
+ defer func () {
826
+ s .prefetcher .close ()
827
+ s .prefetcher = nil
828
+ }()
829
+ }
830
+ // Although naively it makes sense to retrieve the account trie and then do
831
+ // the contract storage and account updates sequentially, that short circuits
832
+ // the account prefetcher. Instead, let's process all the storage updates
833
+ // first, giving the account prefeches just a few more milliseconds of time
834
+ // to pull useful data from disk.
835
+ for addr := range s .stateObjectsPending {
836
+ if obj := s .stateObjects [addr ]; ! obj .deleted {
837
+ obj .updateRoot (s .db )
838
+ }
839
+ }
840
+ // Now we're about to start to write changes to the trie. The trie is so far
841
+ // _untouched_. We can check with the prefetcher, if it can give us a trie
842
+ // which has the same root, but also has some content loaded into it.
843
+ if prefetcher != nil {
844
+ if trie := prefetcher .trie (s .originalRoot ); trie != nil {
845
+ s .trie = trie
840
846
}
841
847
}
848
+ usedAddrs := make ([][]byte , 0 , len (s .stateObjectsPending ))
842
849
for addr := range s .stateObjectsPending {
843
- obj := s .stateObjects [addr ]
844
- if obj .deleted {
850
+ if obj := s .stateObjects [addr ]; obj .deleted {
845
851
s .deleteStateObject (obj )
846
852
} else {
847
- obj .updateRoot (s .db )
848
853
s .updateStateObject (obj )
849
854
}
855
+ usedAddrs = append (usedAddrs , common .CopyBytes (addr [:])) // Copy needed for closure
856
+ }
857
+ if prefetcher != nil {
858
+ prefetcher .used (s .originalRoot , usedAddrs )
850
859
}
851
860
if len (s .stateObjectsPending ) > 0 {
852
861
s .stateObjectsPending = make (map [common.Address ]struct {})
0 commit comments