mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
trie: tweak cache formula to favour low-depth nodes
This change ensures that nodes closer to the root node are kept around longer.
This commit is contained in:
parent
ca49510e6d
commit
766d71d7c2
5 changed files with 22 additions and 18 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
17
trie/node.go
17
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 }
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue