-
Notifications
You must be signed in to change notification settings - Fork 20k
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
Fjl state cache #3028
Fjl state cache #3028
Conversation
@karalabe, thanks for your PR! By analyzing the annotation information on this pull request, we identified @obscuren, @fjl and @Gustav-Simonsson to be potential reviewers |
} | ||
} | ||
|
||
// UpdateRoot sets the trie root to the current root hash of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this comment can be finished like "... of the trie."
stateObject.nonce = self.nonce | ||
func (self *StateObject) Copy(db trie.Database) *StateObject { | ||
stateObject := NewObject(self.address, self.data) | ||
stateObject.data.Balance.Set(self.data.Balance) | ||
stateObject.trie = self.trie | ||
stateObject.code = self.code | ||
stateObject.storage = self.storage.Copy() | ||
stateObject.remove = self.remove | ||
stateObject.dirty = self.dirty |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should dirtyCode
be copied as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Brilliant catch. I just found it but was looking for it for an hour straight... whyyyy didn't I see your message..
d45927a
to
a5cd548
Compare
stateObject.nonce = self.nonce | ||
func (self *StateObject) Copy(db trie.Database) *StateObject { | ||
stateObject := NewObject(self.address, self.data) | ||
stateObject.data.Balance.Set(self.data.Balance) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this line.
stateObject.data is the same object as self.data at this point. So, to me, it looks like:
stateObject.data.Balance.Set(stateObject.data.Balance))
Am I missing something or is this unnecessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I wanted to delete this too. Accident that it got left in.
} | ||
|
||
// Creates creates a new state object and takes ownership. This is different from "NewStateObject" | ||
// Creates creates a new state object and takes ownership. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd just like to verify; the CreateStateObject
does not stash away the data
into s.all
, so it does not wind up in there until after commit
. Is that intended behaviour?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes. The idea is that s.all should mirror DB content at all times.
@@ -242,12 +242,12 @@ func (self *StateDB) DeleteStateObject(stateObject *StateObject) { | |||
|
|||
addr := stateObject.Address() | |||
self.trie.Delete(addr[:]) | |||
//delete(self.stateObjects, addr.Str()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just delete this?
return value | ||
} | ||
// Load from DB in case it is missing. | ||
self.initTrie(db) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just have an accessor getTrie
that returns it if it's not initialized instead?
self.data.Root = self.trie.Hash() | ||
} | ||
|
||
// CommitTrie the storage trie of the object to dwb. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if stateObject.remove { | ||
// If the object has been removed, don't bother syncing it | ||
// and just mark it for deletion in the trie. | ||
s.DeleteStateObject(stateObject) | ||
delete(s.all, addr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't s.all
get obliterated anyway when the state root changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cache is retained as long as blocks follow a progression. It gets obliterated only if a new block is being imported that has a different parent than the previous import (since then the cached state is invalid from the new block's perspective).
This could be worked around with a journal for the cache, but that's a whole new can of worms.
blockCache *lru.Cache // Cache for the most recent entire blocks | ||
futureBlocks *lru.Cache // future blocks are blocks added for later processing | ||
stateCache *state.StateDB // State database to reuse between imports (contains state cache) | ||
bodyCache *lru.Cache // Cache for the most recent block bodies |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have two different caches caching the same thing in different encodings? And a third caching the blocks themselves?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The caches should be thoroughly reexamined. I think noone ever had the time to dissect every cache's purpose and drop them. The only reasonable way is with a lot of benchmarking which is a PITA so noone took the initiative. I agree we should do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will remove bodyCache in this PR
// CodeSize returns the size of the contract code associated with this object. | ||
func (self *StateObject) CodeSize(db trie.Database) int { | ||
if self.data.codeSize == nil { | ||
self.data.codeSize = new(int) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we using a pointer to an int here? What's wrong with -1 to indicate we haven't loaded the code size?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I usually prefer using the default value to mean non-initialized. Using -1 would require taking extra care to always initialize it properly (e.g. there is code that just did Account{}
, which would be invalid from now on).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, using naked constructors everywhere where there are critical fields that have to be set correctly is another antipattern. :)
Testing this on mainnet reveals that it does significantly speed up processing of the tx spam blocks, but only for some blocks - it looks like the regular (small) reorgs of 1-2 blocks resets the |
0db8282
to
e8db0f6
Compare
db is essentially unused and removing it shrinks StateObject by 16 bytes. I'd love to remove address as well but that's a very invasive change.
With this change, StateObject can work without loading the storage trie and contract code. This is required because we're about to cache state objects and want to reduce the amount of memory referenced by idle objects.
This change introduces a global, per-state cache that keeps account data in the canon state.
Until now the dirty flag was unset in intermediate root. That resulted in not knowing whether a state object should be writte to disk or not. Being removed in 1.4.12, we can rely on it to avoid a ton of writes.
47c1768
to
8e4687e
Compare
@@ -46,7 +46,7 @@ type SecureTrie struct { | |||
secKeyCache map[string][]byte | |||
} | |||
|
|||
// NewSecure creates a trie with an existing root node from db. | |||
// NewSecure creFGetates a trie with an existing root node from db. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
No description provided.