forked from cerc-io/plugeth
trie: reduce hasher allocations (#16896)
* trie: reduce hasher allocations name old time/op new time/op delta Hash-8 4.05µs ±12% 3.56µs ± 9% -12.13% (p=0.000 n=20+19) name old alloc/op new alloc/op delta Hash-8 1.30kB ± 0% 0.66kB ± 0% -49.15% (p=0.000 n=20+20) name old allocs/op new allocs/op delta Hash-8 11.0 ± 0% 8.0 ± 0% -27.27% (p=0.000 n=20+20) * trie: bump initial buffer cap in hasher
This commit is contained in:
parent
5bee5d69d7
commit
e8ea5aa0d5
@ -17,7 +17,6 @@
|
|||||||
package trie
|
package trie
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"hash"
|
"hash"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@ -27,17 +26,39 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type hasher struct {
|
type hasher struct {
|
||||||
tmp *bytes.Buffer
|
tmp sliceBuffer
|
||||||
sha hash.Hash
|
sha keccakState
|
||||||
cachegen uint16
|
cachegen uint16
|
||||||
cachelimit uint16
|
cachelimit uint16
|
||||||
onleaf LeafCallback
|
onleaf LeafCallback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
|
||||||
|
// Read to get a variable amount of data from the hash state. Read is faster than Sum
|
||||||
|
// because it doesn't copy the internal state, but also modifies the internal state.
|
||||||
|
type keccakState interface {
|
||||||
|
hash.Hash
|
||||||
|
Read([]byte) (int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type sliceBuffer []byte
|
||||||
|
|
||||||
|
func (b *sliceBuffer) Write(data []byte) (n int, err error) {
|
||||||
|
*b = append(*b, data...)
|
||||||
|
return len(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *sliceBuffer) Reset() {
|
||||||
|
*b = (*b)[:0]
|
||||||
|
}
|
||||||
|
|
||||||
// hashers live in a global db.
|
// hashers live in a global db.
|
||||||
var hasherPool = sync.Pool{
|
var hasherPool = sync.Pool{
|
||||||
New: func() interface{} {
|
New: func() interface{} {
|
||||||
return &hasher{tmp: new(bytes.Buffer), sha: sha3.NewKeccak256()}
|
return &hasher{
|
||||||
|
tmp: make(sliceBuffer, 0, 550), // cap is as large as a full fullNode.
|
||||||
|
sha: sha3.NewKeccak256().(keccakState),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,26 +178,23 @@ func (h *hasher) store(n node, db *Database, force bool) (node, error) {
|
|||||||
}
|
}
|
||||||
// Generate the RLP encoding of the node
|
// Generate the RLP encoding of the node
|
||||||
h.tmp.Reset()
|
h.tmp.Reset()
|
||||||
if err := rlp.Encode(h.tmp, n); err != nil {
|
if err := rlp.Encode(&h.tmp, n); err != nil {
|
||||||
panic("encode error: " + err.Error())
|
panic("encode error: " + err.Error())
|
||||||
}
|
}
|
||||||
if h.tmp.Len() < 32 && !force {
|
if len(h.tmp) < 32 && !force {
|
||||||
return n, nil // Nodes smaller than 32 bytes are stored inside their parent
|
return n, nil // Nodes smaller than 32 bytes are stored inside their parent
|
||||||
}
|
}
|
||||||
// Larger nodes are replaced by their hash and stored in the database.
|
// Larger nodes are replaced by their hash and stored in the database.
|
||||||
hash, _ := n.cache()
|
hash, _ := n.cache()
|
||||||
if hash == nil {
|
if hash == nil {
|
||||||
h.sha.Reset()
|
hash = h.makeHashNode(h.tmp)
|
||||||
h.sha.Write(h.tmp.Bytes())
|
|
||||||
hash = hashNode(h.sha.Sum(nil))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if db != nil {
|
if db != nil {
|
||||||
// We are pooling the trie nodes into an intermediate memory cache
|
// We are pooling the trie nodes into an intermediate memory cache
|
||||||
db.lock.Lock()
|
db.lock.Lock()
|
||||||
|
|
||||||
hash := common.BytesToHash(hash)
|
hash := common.BytesToHash(hash)
|
||||||
db.insert(hash, h.tmp.Bytes())
|
db.insert(hash, h.tmp)
|
||||||
|
|
||||||
// Track all direct parent->child node references
|
// Track all direct parent->child node references
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
case *shortNode:
|
case *shortNode:
|
||||||
@ -210,3 +228,11 @@ func (h *hasher) store(n node, db *Database, force bool) (node, error) {
|
|||||||
}
|
}
|
||||||
return hash, nil
|
return hash, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *hasher) makeHashNode(data []byte) hashNode {
|
||||||
|
n := make(hashNode, h.sha.Size())
|
||||||
|
h.sha.Reset()
|
||||||
|
h.sha.Write(data)
|
||||||
|
h.sha.Read(n)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user