-
Notifications
You must be signed in to change notification settings - Fork 21.5k
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.
Outdated
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 { | ||
|
||
| 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 { | ||
|
||
| // 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.