-
Notifications
You must be signed in to change notification settings - Fork 21k
core: improve trie updates (part 2) #21047
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -157,11 +157,20 @@ func (s *stateObject) touch() { | |
|
||
func (s *stateObject) getTrie(db Database) Trie { | ||
if s.trie == nil { | ||
var err error | ||
s.trie, err = db.OpenStorageTrie(s.addrHash, s.data.Root) | ||
if err != nil { | ||
s.trie, _ = db.OpenStorageTrie(s.addrHash, common.Hash{}) | ||
s.setError(fmt.Errorf("can't create storage trie: %v", err)) | ||
// Try fetching from prefetcher first | ||
rjl493456442 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// We don't prefetch empty tries | ||
if s.data.Root != emptyRoot && s.db.prefetcher != nil { | ||
// When the miner is creating the pending state, there is no | ||
// prefetcher | ||
s.trie = s.db.prefetcher.trie(s.data.Root) | ||
} | ||
if s.trie == nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we load the data from the trie directly(without hitting in the snapshot for some reasons), is it ok to reload the path again by triePrefetcher? I think it's fine. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we would only not use the snapshot if we're in the process of creating it, which is a temporary headache, and it's ok to load it twice in that case, since it would also have been warmed up in the cache. Another similar thing is if we have two identical storage tries, in two separate contracts. We can only "hand out' the trie to one of them, and the second one will have to load from disk/cache. Otherwise, the first one would modify the trie, and the second one would get the modified version. |
||
var err error | ||
s.trie, err = db.OpenStorageTrie(s.addrHash, s.data.Root) | ||
if err != nil { | ||
s.trie, _ = db.OpenStorageTrie(s.addrHash, common.Hash{}) | ||
s.setError(fmt.Errorf("can't create storage trie: %v", err)) | ||
} | ||
} | ||
} | ||
return s.trie | ||
|
@@ -197,12 +206,24 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has | |
} | ||
// If no live objects are available, attempt to use snapshots | ||
var ( | ||
enc []byte | ||
err error | ||
enc []byte | ||
err error | ||
meter *time.Duration | ||
) | ||
readStart := time.Now() | ||
if metrics.EnabledExpensive { | ||
// If the snap is 'under construction', the first lookup may fail. If that | ||
// happens, we don't want to double-count the time elapsed. Thus this | ||
// dance with the metering. | ||
defer func() { | ||
if meter != nil { | ||
*meter += time.Since(readStart) | ||
} | ||
}() | ||
} | ||
if s.db.snap != nil { | ||
if metrics.EnabledExpensive { | ||
defer func(start time.Time) { s.db.SnapshotStorageReads += time.Since(start) }(time.Now()) | ||
meter = &s.db.SnapshotStorageReads | ||
} | ||
// If the object was destructed in *this* block (and potentially resurrected), | ||
// the storage has been cleared out, and we should *not* consult the previous | ||
|
@@ -217,8 +238,14 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has | |
} | ||
// If snapshot unavailable or reading from it failed, load from the database | ||
if s.db.snap == nil || err != nil { | ||
if meter != nil { | ||
// If we already spent time checking the snapshot, account for it | ||
// and reset the readStart | ||
*meter += time.Since(readStart) | ||
readStart = time.Now() | ||
} | ||
if metrics.EnabledExpensive { | ||
defer func(start time.Time) { s.db.StorageReads += time.Since(start) }(time.Now()) | ||
meter = &s.db.StorageReads | ||
} | ||
if enc, err = s.getTrie(db).TryGet(key.Bytes()); err != nil { | ||
s.setError(err) | ||
|
@@ -282,9 +309,16 @@ func (s *stateObject) setState(key, value common.Hash) { | |
|
||
// 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() { | ||
func (s *stateObject) finalise(prefetch bool) { | ||
slotsToPrefetch := make([][]byte, 0, len(s.dirtyStorage)) | ||
for key, value := range s.dirtyStorage { | ||
s.pendingStorage[key] = value | ||
if value != s.originStorage[key] { | ||
slotsToPrefetch = append(slotsToPrefetch, common.CopyBytes(key[:])) // Copy needed for closure | ||
} | ||
} | ||
if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != emptyRoot { | ||
s.db.prefetcher.prefetch(s.data.Root, slotsToPrefetch) | ||
} | ||
if len(s.dirtyStorage) > 0 { | ||
s.dirtyStorage = make(Storage) | ||
|
@@ -295,26 +329,21 @@ func (s *stateObject) finalise() { | |
// It will return nil if the trie has not been loaded and no changes have been made | ||
func (s *stateObject) updateTrie(db Database) Trie { | ||
// Make sure all dirty slots are finalized into the pending storage area | ||
s.finalise() | ||
s.finalise(false) // Don't prefetch any more, pull directly if need be | ||
if len(s.pendingStorage) == 0 { | ||
return s.trie | ||
} | ||
// Track the amount of time wasted on updating the storage trie | ||
if metrics.EnabledExpensive { | ||
defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now()) | ||
} | ||
// Retrieve the snapshot storage map for the object | ||
// The snapshot storage map for the object | ||
var storage map[common.Hash][]byte | ||
if s.db.snap != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any particular reason to move this storage initialization? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we might not need to do it at all, if there are no pending storage changes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is, if the |
||
// Retrieve the old storage map, if available, create a new one otherwise | ||
storage = s.db.snapStorage[s.addrHash] | ||
if storage == nil { | ||
storage = make(map[common.Hash][]byte) | ||
s.db.snapStorage[s.addrHash] = storage | ||
} | ||
} | ||
// Insert all the pending updates into the trie | ||
tr := s.getTrie(db) | ||
hasher := s.db.hasher | ||
|
||
usedStorage := make([][]byte, 0, len(s.pendingStorage)) | ||
for key, value := range s.pendingStorage { | ||
// Skip noop changes, persist actual changes | ||
if value == s.originStorage[key] { | ||
|
@@ -331,9 +360,20 @@ func (s *stateObject) updateTrie(db Database) Trie { | |
s.setError(tr.TryUpdate(key[:], v)) | ||
} | ||
// If state snapshotting is active, cache the data til commit | ||
if storage != nil { | ||
storage[crypto.Keccak256Hash(key[:])] = v // v will be nil if value is 0x00 | ||
if s.db.snap != nil { | ||
if storage == nil { | ||
// Retrieve the old storage map, if available, create a new one otherwise | ||
if storage = s.db.snapStorage[s.addrHash]; storage == nil { | ||
storage = make(map[common.Hash][]byte) | ||
s.db.snapStorage[s.addrHash] = storage | ||
} | ||
} | ||
storage[crypto.HashData(hasher, key[:])] = v // v will be nil if value is 0x00 | ||
} | ||
usedStorage = append(usedStorage, common.CopyBytes(key[:])) // Copy needed for closure | ||
} | ||
if s.db.prefetcher != nil { | ||
s.db.prefetcher.used(s.data.Root, usedStorage) | ||
} | ||
if len(s.pendingStorage) > 0 { | ||
s.pendingStorage = make(Storage) | ||
|
Uh oh!
There was an error while loading. Please reload this page.