From 38c7021c73bbb9ba9be4629a794eec7eaabdd15a Mon Sep 17 00:00:00 2001 From: Gary Rong Date: Fri, 20 Mar 2026 16:05:46 +0800 Subject: [PATCH] core/state: invoke prefetcher --- core/state/database_hasher.go | 4 ++-- core/state/state_object.go | 14 ++++++++++++++ core/state/statedb.go | 16 ++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/core/state/database_hasher.go b/core/state/database_hasher.go index 4046645284..63d7aa25c4 100644 --- a/core/state/database_hasher.go +++ b/core/state/database_hasher.go @@ -80,10 +80,10 @@ type Hasher interface { // asynchronously warm up trie/state data ahead of mutations or hashing. type Prefetcher interface { // PrefetchAccount schedules the account for prefetching. - PrefetchAccount(addresses []common.Address) + PrefetchAccount(addresses []common.Address, read bool) // PrefetchStorage schedules the storage slot for prefetching. - PrefetchStorage(addr common.Address, keys []common.Hash) + PrefetchStorage(addr common.Address, keys []common.Hash, read bool) } // WitnessCollector is an optional extension implemented by hashers that can diff --git a/core/state/state_object.go b/core/state/state_object.go index f93cd96848..165c216649 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -191,6 +191,12 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { s.db.StorageReads += time.Since(start) s.originStorage[key] = value + + // Schedule the resolved storage slots for prefetching if it's enabled. + prefetch, ok := s.db.hasher.(Prefetcher) + if ok { + prefetch.PrefetchStorage(s.address, []common.Hash{key}, true) + } return value } @@ -223,6 +229,7 @@ func (s *stateObject) setState(key common.Hash, value common.Hash, origin common // finalise moves all dirty storage slots into the pending area to be hashed or // committed later. It is invoked at the end of every transaction. func (s *stateObject) finalise() { + slotsToPrefetch := make([]common.Hash, 0, len(s.dirtyStorage)) for key, value := range s.dirtyStorage { if origin, exist := s.uncommittedStorage[key]; exist && origin == value { // The slot is reverted to its original value, delete the entry @@ -235,6 +242,7 @@ func (s *stateObject) finalise() { // The slot is different from its original value and hasn't been // tracked for commit yet. s.uncommittedStorage[key] = s.GetCommittedState(key) + slotsToPrefetch = append(slotsToPrefetch, key) } // Aggregate the dirty storage slots into the pending area. It might // be possible that the value of tracked slot here is same with the @@ -251,6 +259,12 @@ func (s *stateObject) finalise() { // of the newly-created object as it's no longer eligible for self-destruct // by EIP-6780. For non-newly-created objects, it's a no-op. s.newContract = false + + // Schedule the resolved storage slots for prefetching if it's enabled. + prefetch, ok := s.db.hasher.(Prefetcher) + if ok { + prefetch.PrefetchStorage(s.address, slotsToPrefetch, false) + } } // updateTrie is responsible for persisting cached storage changes into the diff --git a/core/state/statedb.go b/core/state/statedb.go index 3c1241428e..02ac12cf7b 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -555,6 +555,12 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject { // Insert into the live set obj := newObject(s, addr, acct) s.setStateObject(obj) + + // Schedule the resolved account for prefetching if it's enabled. + prefetcher, ok := s.hasher.(Prefetcher) + if ok { + prefetcher.PrefetchAccount([]common.Address{addr}, true) + } return obj } @@ -725,6 +731,7 @@ func (s *StateDB) LogsForBurnAccounts() []*types.Log { // the journal as well as the refunds. Finalise, however, will not push any updates // into the tries just yet. Only IntermediateRoot or Commit will do that. func (s *StateDB) Finalise(deleteEmptyObjects bool) { + addressesToPrefetch := make([]common.Address, 0, len(s.journal.dirties)) for addr := range s.journal.dirties { obj, exist := s.stateObjects[addr] if !exist { @@ -749,9 +756,18 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { obj.finalise() s.markUpdate(addr) } + // At this point, also ship the address off to the prefetcher. The prefetcher + // will start loading tries, and when the change is eventually committed, + // the commit-phase will be a lot faster + addressesToPrefetch = append(addressesToPrefetch, addr) } // Invalidate journal because reverting across transactions is not allowed. s.clearJournalAndRefund() + + prefetcher, ok := s.hasher.(Prefetcher) + if ok { + prefetcher.PrefetchAccount(addressesToPrefetch, false) + } } // IntermediateRoot computes the current root hash of the state trie.