Skip to content
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

Closed
wants to merge 14 commits into from
Closed

Fjl state cache #3028

wants to merge 14 commits into from

Conversation

karalabe
Copy link
Member

No description provided.

@mention-bot
Copy link

@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
Copy link
Member

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
Copy link
Member

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?

Copy link
Member Author

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..

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)
Copy link
Contributor

@holiman holiman Sep 23, 2016

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?

Copy link
Member Author

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.
Copy link
Contributor

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?

Copy link
Contributor

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())
Copy link
Contributor

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)
Copy link
Contributor

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh?

Copy link
Member Author

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)
Copy link
Contributor

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?

Copy link
Member Author

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
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

@fjl fjl Sep 23, 2016

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)
Copy link
Contributor

@Arachnid Arachnid Sep 23, 2016

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?

Copy link
Member Author

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).

Copy link
Contributor

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. :)

@Gustav-Simonsson
Copy link

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 all cache - so it only speeds up spam blocks until the next reorg.

fjl and others added 14 commits September 26, 2016 09:18
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.
@@ -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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

@fjl fjl closed this Sep 26, 2016
@fjl fjl removed the in progress label Sep 26, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants