From 766d71d7c2a764f5b6e45cd0ea6f13bf65010a4f Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 19 Oct 2016 15:06:56 +0200 Subject: [PATCH 1/2] trie: tweak cache formula to favour low-depth nodes This change ensures that nodes closer to the root node are kept around longer. --- trie/hasher.go | 17 +++++++++++------ trie/node.go | 17 ++++++++--------- trie/node_test.go | 2 +- trie/proof.go | 2 +- trie/trie.go | 2 +- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/trie/hasher.go b/trie/hasher.go index b6223bf32b..301e879e49 100644 --- a/trie/hasher.go +++ b/trie/hasher.go @@ -49,15 +49,20 @@ func returnHasherToPool(h *hasher) { hasherPool.Put(h) } +// canUnload decides whether a node can be unloaded. +func (n *nodeFlag) canUnload(cachegen, cachelimit uint16, depth int) bool { + return !n.dirty && cachegen-n.gen >= cachelimit/uint16(depth+1) +} + // hash collapses a node down into a hash node, also returning a copy of the // original node initialzied with the computed hash to replace the original one. -func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error) { +func (h *hasher) hash(n node, db DatabaseWriter, force bool, depth int) (node, node, error) { // If we're not storing the node, just hashing, use avaialble cached data if hash, dirty := n.cache(); hash != nil { if db == nil { return hash, n, nil } - if n.canUnload(h.cachegen, h.cachelimit) { + if n.canUnload(h.cachegen, h.cachelimit, depth) { // Unload the node from cache. All of its subnodes will have a lower or equal // cache generation number. return hash, hash, nil @@ -67,7 +72,7 @@ func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error) } } // Trie not processed yet or needs storage, walk the children - collapsed, cached, err := h.hashChildren(n, db) + collapsed, cached, err := h.hashChildren(n, db, depth) if err != nil { return hashNode{}, n, err } @@ -97,7 +102,7 @@ func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error) // hashChildren replaces the children of a node with their hashes if the encoded // size of the child is larger than a hash, returning the collapsed node as well // as a replacement for the original node with the child hashes cached in. -func (h *hasher) hashChildren(original node, db DatabaseWriter) (node, node, error) { +func (h *hasher) hashChildren(original node, db DatabaseWriter, depth int) (node, node, error) { var err error switch n := original.(type) { @@ -108,7 +113,7 @@ func (h *hasher) hashChildren(original node, db DatabaseWriter) (node, node, err cached.Key = common.CopyBytes(n.Key) if _, ok := n.Val.(valueNode); !ok { - collapsed.Val, cached.Val, err = h.hash(n.Val, db, false) + collapsed.Val, cached.Val, err = h.hash(n.Val, db, false, depth+1) if err != nil { return original, original, err } @@ -124,7 +129,7 @@ func (h *hasher) hashChildren(original node, db DatabaseWriter) (node, node, err for i := 0; i < 16; i++ { if n.Children[i] != nil { - collapsed.Children[i], cached.Children[i], err = h.hash(n.Children[i], db, false) + collapsed.Children[i], cached.Children[i], err = h.hash(n.Children[i], db, false, depth+1) if err != nil { return original, original, err } diff --git a/trie/node.go b/trie/node.go index 4aa0cab65a..859677cd12 100644 --- a/trie/node.go +++ b/trie/node.go @@ -30,7 +30,7 @@ var indices = []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b type node interface { fstring(string) string cache() (hashNode, bool) - canUnload(cachegen, cachelimit uint16) bool + canUnload(cachegen, cachelimit uint16, depth int) bool } type ( @@ -62,15 +62,14 @@ type nodeFlag struct { dirty bool // whether the node has changes that must be written to the database } -// canUnload tells whether a node can be unloaded. -func (n *nodeFlag) canUnload(cachegen, cachelimit uint16) bool { - return !n.dirty && cachegen-n.gen >= cachelimit +func (n *fullNode) canUnload(gen, limit uint16, depth int) bool { + return n.flags.canUnload(gen, limit, depth) } - -func (n *fullNode) canUnload(gen, limit uint16) bool { return n.flags.canUnload(gen, limit) } -func (n *shortNode) canUnload(gen, limit uint16) bool { return n.flags.canUnload(gen, limit) } -func (n hashNode) canUnload(uint16, uint16) bool { return false } -func (n valueNode) canUnload(uint16, uint16) bool { return false } +func (n *shortNode) canUnload(gen, limit uint16, depth int) bool { + return n.flags.canUnload(gen, limit, depth) +} +func (n hashNode) canUnload(uint16, uint16, int) bool { return false } +func (n valueNode) canUnload(uint16, uint16, int) bool { return false } func (n *fullNode) cache() (hashNode, bool) { return n.flags.hash, n.flags.dirty } func (n *shortNode) cache() (hashNode, bool) { return n.flags.hash, n.flags.dirty } diff --git a/trie/node_test.go b/trie/node_test.go index 7ad1ff9e7b..4ad5ae0fac 100644 --- a/trie/node_test.go +++ b/trie/node_test.go @@ -51,7 +51,7 @@ func TestCanUnload(t *testing.T) { } for _, test := range tests { - if got := test.flag.canUnload(test.cachegen, test.cachelimit); got != test.want { + if got := test.flag.canUnload(test.cachegen, test.cachelimit, 0); got != test.want { t.Errorf("%+v\n got %t, want %t", test, got, test.want) } } diff --git a/trie/proof.go b/trie/proof.go index bea5e5c098..69f3e8183e 100644 --- a/trie/proof.go +++ b/trie/proof.go @@ -75,7 +75,7 @@ func (t *Trie) Prove(key []byte) []rlp.RawValue { for i, n := range nodes { // Don't bother checking for errors here since hasher panics // if encoding doesn't work and we're not writing to any database. - n, _, _ = hasher.hashChildren(n, nil) + n, _, _ = hasher.hashChildren(n, nil, 0) hn, _ := hasher.store(n, nil, false) if _, ok := hn.(hashNode); ok || i == 0 { // If the node's database encoding is a hash (or is the diff --git a/trie/trie.go b/trie/trie.go index 914bf20fa5..c0c30c39c4 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -495,5 +495,5 @@ func (t *Trie) hashRoot(db DatabaseWriter) (node, node, error) { } h := newHasher(t.cachegen, t.cachelimit) defer returnHasherToPool(h) - return h.hash(t.root, db, true) + return h.hash(t.root, db, true, 0) } From 48fe6f3bb1282a4bb84fc2836cc503ea39e79412 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 19 Oct 2016 15:08:32 +0200 Subject: [PATCH 2/2] core/state: bump default trie cache generations to 10000 --- core/state/statedb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 0e46e5e2cb..dc001914ce 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -44,7 +44,7 @@ const ( maxPastTries = 12 // Trie cache generation limit. - maxTrieCacheGen = 120 + maxTrieCacheGen = 10000 // Number of codehash->size associations to keep. codeSizeCacheSize = 100000