mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 10:50:44 +00:00
trie: cache rlp encoded blobs
This commit is contained in:
parent
4d2181aa41
commit
f674fdc18f
8 changed files with 44 additions and 13 deletions
|
|
@ -149,8 +149,18 @@ func (c *committer) store(path []byte, n node) node {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
// Collect the dirty node to nodeset for return.
|
// Collect the dirty node to nodeset for return.
|
||||||
|
var blob []byte
|
||||||
|
switch n := n.(type) {
|
||||||
|
case *shortNode:
|
||||||
|
blob = n.flags.blob
|
||||||
|
case *fullNode:
|
||||||
|
blob = n.flags.blob
|
||||||
|
}
|
||||||
|
if len(blob) == 0 {
|
||||||
|
blob = nodeToBytes(n)
|
||||||
|
}
|
||||||
nhash := common.BytesToHash(hash)
|
nhash := common.BytesToHash(hash)
|
||||||
c.nodes.AddNode(path, trienode.NewNodeWithPrev(nhash, nodeToBytes(n), c.tracer.Get(path)))
|
c.nodes.AddNode(path, trienode.NewNodeWithPrev(nhash, blob, c.tracer.Get(path)))
|
||||||
|
|
||||||
// Collect the corresponding leaf node if it's required. We don't check
|
// Collect the corresponding leaf node if it's required. We don't check
|
||||||
// full node since it's impossible to store value in fullNode. The key
|
// full node since it's impossible to store value in fullNode. The key
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
@ -28,10 +29,11 @@ import (
|
||||||
// hasher is a type used for the trie Hash operation. A hasher has some
|
// hasher is a type used for the trie Hash operation. A hasher has some
|
||||||
// internal preallocated temp space
|
// internal preallocated temp space
|
||||||
type hasher struct {
|
type hasher struct {
|
||||||
sha crypto.KeccakState
|
sha crypto.KeccakState
|
||||||
tmp []byte
|
tmp []byte
|
||||||
encbuf rlp.EncoderBuffer
|
encbuf rlp.EncoderBuffer
|
||||||
parallel bool // Whether to use parallel threads when hashing
|
parallel bool // Whether to use parallel threads when hashing
|
||||||
|
cacheBlob bool // Whether to stash each node's encoding for the committer
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasherPool holds pureHashers
|
// hasherPool holds pureHashers
|
||||||
|
|
@ -45,9 +47,10 @@ var hasherPool = sync.Pool{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHasher(parallel bool) *hasher {
|
func newHasher(parallel, cacheBlob bool) *hasher {
|
||||||
h := hasherPool.Get().(*hasher)
|
h := hasherPool.Get().(*hasher)
|
||||||
h.parallel = parallel
|
h.parallel = parallel
|
||||||
|
h.cacheBlob = cacheBlob
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,6 +79,9 @@ func (h *hasher) hash(n node, force bool) []byte {
|
||||||
}
|
}
|
||||||
hash := h.hashData(enc)
|
hash := h.hashData(enc)
|
||||||
n.flags.hash = hash
|
n.flags.hash = hash
|
||||||
|
if h.cacheBlob {
|
||||||
|
n.flags.blob = common.CopyBytes(enc)
|
||||||
|
}
|
||||||
return hash
|
return hash
|
||||||
|
|
||||||
case *fullNode:
|
case *fullNode:
|
||||||
|
|
@ -91,6 +97,9 @@ func (h *hasher) hash(n node, force bool) []byte {
|
||||||
}
|
}
|
||||||
hash := h.hashData(enc)
|
hash := h.hashData(enc)
|
||||||
n.flags.hash = hash
|
n.flags.hash = hash
|
||||||
|
if h.cacheBlob {
|
||||||
|
n.flags.blob = common.CopyBytes(enc)
|
||||||
|
}
|
||||||
return hash
|
return hash
|
||||||
|
|
||||||
case hashNode:
|
case hashNode:
|
||||||
|
|
@ -148,9 +157,9 @@ func (h *hasher) encodeFullNode(n *fullNode) []byte {
|
||||||
go func(i int) {
|
go func(i int) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
h := newHasher(false)
|
h2 := newHasher(false, h.cacheBlob)
|
||||||
fn.Children[i] = h.hash(n.Children[i], false)
|
fn.Children[i] = h2.hash(n.Children[i], false)
|
||||||
returnHasherToPool(h)
|
returnHasherToPool(h2)
|
||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ func (it *nodeIterator) LeafBlob() []byte {
|
||||||
func (it *nodeIterator) LeafProof() [][]byte {
|
func (it *nodeIterator) LeafProof() [][]byte {
|
||||||
if len(it.stack) > 0 {
|
if len(it.stack) > 0 {
|
||||||
if _, ok := it.stack[len(it.stack)-1].node.(valueNode); ok {
|
if _, ok := it.stack[len(it.stack)-1].node.(valueNode); ok {
|
||||||
hasher := newHasher(false)
|
hasher := newHasher(false, false)
|
||||||
defer returnHasherToPool(hasher)
|
defer returnHasherToPool(hasher)
|
||||||
proofs := make([][]byte, 0, len(it.stack))
|
proofs := make([][]byte, 0, len(it.stack))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,12 +80,14 @@ func (n *fullNode) EncodeRLP(w io.Writer) error {
|
||||||
// nodeFlag contains caching-related metadata about a node.
|
// nodeFlag contains caching-related metadata about a node.
|
||||||
type nodeFlag struct {
|
type nodeFlag struct {
|
||||||
hash hashNode // cached hash of the node (may be nil)
|
hash hashNode // cached hash of the node (may be nil)
|
||||||
|
blob []byte // cached rlp encoding of the node, set along with the hash (may be nil)
|
||||||
dirty bool // whether the node has changes that must be written to the database
|
dirty bool // whether the node has changes that must be written to the database
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n nodeFlag) copy() nodeFlag {
|
func (n nodeFlag) copy() nodeFlag {
|
||||||
return nodeFlag{
|
return nodeFlag{
|
||||||
hash: common.CopyBytes(n.hash),
|
hash: common.CopyBytes(n.hash),
|
||||||
|
blob: common.CopyBytes(n.blob),
|
||||||
dirty: n.dirty,
|
dirty: n.dirty,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ func (t *Trie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
|
||||||
panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
|
panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hasher := newHasher(false)
|
hasher := newHasher(false, false)
|
||||||
defer returnHasherToPool(hasher)
|
defer returnHasherToPool(hasher)
|
||||||
|
|
||||||
for i, n := range nodes {
|
for i, n := range nodes {
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,10 @@ func NewStateTrie(id *ID, db database.NodeDatabase) (*StateTrie, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// State tries always follow a hash-then-commit lifecycle, so cache the node
|
||||||
|
// encodings produced during hashing for reuse by the subsequent commit.
|
||||||
|
trie.cacheBlob = true
|
||||||
|
|
||||||
tr := &StateTrie{
|
tr := &StateTrie{
|
||||||
trie: *trie,
|
trie: *trie,
|
||||||
db: db,
|
db: db,
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ type StackTrie struct {
|
||||||
func NewStackTrie(onTrieNode OnTrieNode) *StackTrie {
|
func NewStackTrie(onTrieNode OnTrieNode) *StackTrie {
|
||||||
return &StackTrie{
|
return &StackTrie{
|
||||||
root: stPool.Get().(*stNode),
|
root: stPool.Get().(*stNode),
|
||||||
h: newHasher(false),
|
h: newHasher(false, false),
|
||||||
onTrieNode: onTrieNode,
|
onTrieNode: onTrieNode,
|
||||||
kBuf: make([]byte, 64),
|
kBuf: make([]byte, 64),
|
||||||
pBuf: make([]byte, 64),
|
pBuf: make([]byte, 64),
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,11 @@ type Trie struct {
|
||||||
// uncommitted is the number of updates since last commit.
|
// uncommitted is the number of updates since last commit.
|
||||||
uncommitted int
|
uncommitted int
|
||||||
|
|
||||||
|
// cacheBlob makes the hasher cache encoded trie nodes.
|
||||||
|
// Saves re-encoding on commit. Can be turned off for
|
||||||
|
// Trie's that are never committed (e.g. for snap sync).
|
||||||
|
cacheBlob bool
|
||||||
|
|
||||||
// reader is the handler trie can retrieve nodes from.
|
// reader is the handler trie can retrieve nodes from.
|
||||||
reader *Reader
|
reader *Reader
|
||||||
|
|
||||||
|
|
@ -75,6 +80,7 @@ func (t *Trie) Copy() *Trie {
|
||||||
committed: t.committed,
|
committed: t.committed,
|
||||||
unhashed: t.unhashed,
|
unhashed: t.unhashed,
|
||||||
uncommitted: t.uncommitted,
|
uncommitted: t.uncommitted,
|
||||||
|
cacheBlob: t.cacheBlob,
|
||||||
reader: t.reader,
|
reader: t.reader,
|
||||||
opTracer: t.opTracer.copy(),
|
opTracer: t.opTracer.copy(),
|
||||||
prevalueTracer: t.prevalueTracer.Copy(),
|
prevalueTracer: t.prevalueTracer.Copy(),
|
||||||
|
|
@ -860,7 +866,7 @@ func (t *Trie) hashRoot() []byte {
|
||||||
return types.EmptyRootHash.Bytes()
|
return types.EmptyRootHash.Bytes()
|
||||||
}
|
}
|
||||||
// If the number of changes is below 100, we let one thread handle it
|
// If the number of changes is below 100, we let one thread handle it
|
||||||
h := newHasher(t.unhashed >= 100)
|
h := newHasher(t.unhashed >= 100, t.cacheBlob)
|
||||||
defer func() {
|
defer func() {
|
||||||
returnHasherToPool(h)
|
returnHasherToPool(h)
|
||||||
t.unhashed = 0
|
t.unhashed = 0
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue