trie, core: polish

This commit is contained in:
Gary Rong 2025-09-02 11:29:29 +08:00 committed by Felix Lange
parent 89546e19d9
commit 8b1071fc34
4 changed files with 37 additions and 32 deletions

View file

@ -27,7 +27,7 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
// hasherPool holds LegacyKeccak256 hashers for rlpHash. // hasherPool holds LegacyKeccak256 buffer for rlpHash.
var hasherPool = sync.Pool{ var hasherPool = sync.Pool{
New: func() interface{} { return crypto.NewKeccakState() }, New: func() interface{} { return crypto.NewKeccakState() },
} }

View file

@ -74,15 +74,25 @@ func TestEIP2718DeriveSha(t *testing.T) {
} }
} }
// goos: darwin
// goarch: arm64
// pkg: github.com/ethereum/go-ethereum/core/types
// cpu: Apple M1 Pro
// BenchmarkDeriveSha200
// BenchmarkDeriveSha200/std_trie
// BenchmarkDeriveSha200/std_trie-8 6754 174074 ns/op 80054 B/op 1926 allocs/op
// BenchmarkDeriveSha200/stack_trie
// BenchmarkDeriveSha200/stack_trie-8 7296 162675 ns/op 745 B/op 19 allocs/op
func BenchmarkDeriveSha200(b *testing.B) { func BenchmarkDeriveSha200(b *testing.B) {
txs, err := genTxs(200) txs, err := genTxs(200)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
want := types.DeriveSha(txs, trie.NewListHasher()) want := types.DeriveSha(txs, trie.NewListHasher())
var have common.Hash
b.Run("std_trie", func(b *testing.B) { b.Run("std_trie", func(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
var have common.Hash
for b.Loop() { for b.Loop() {
have = types.DeriveSha(txs, trie.NewListHasher()) have = types.DeriveSha(txs, trie.NewListHasher())
} }
@ -94,6 +104,7 @@ func BenchmarkDeriveSha200(b *testing.B) {
st := trie.NewStackTrie(nil) st := trie.NewStackTrie(nil)
b.Run("stack_trie", func(b *testing.B) { b.Run("stack_trie", func(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
var have common.Hash
for b.Loop() { for b.Loop() {
st.Reset() st.Reset()
have = types.DeriveSha(txs, st) have = types.DeriveSha(txs, st)

View file

@ -79,7 +79,7 @@ func newUnsafeBytesPool(sliceCap, nitems int) *unsafeBytesPool {
} }
} }
// Get returns a slice. // Get returns a slice with pre-allocated space.
func (bp *unsafeBytesPool) get() []byte { func (bp *unsafeBytesPool) get() []byte {
if len(bp.items) > 0 { if len(bp.items) > 0 {
last := bp.items[len(bp.items)-1] last := bp.items[len(bp.items)-1]
@ -89,8 +89,8 @@ func (bp *unsafeBytesPool) get() []byte {
return make([]byte, 0, bp.w) return make([]byte, 0, bp.w)
} }
// put returns a slice to the pool. This method // put returns a slice to the pool. This method will ignore slices that are
// will ignore slices that are too small or too large (>3x the cap) // too small or too large (>3x the cap)
func (bp *unsafeBytesPool) put(b []byte) { func (bp *unsafeBytesPool) put(b []byte) {
if c := cap(b); c < bp.w || c > 3*bp.w { if c := cap(b); c < bp.w || c > 3*bp.w {
return return

View file

@ -77,8 +77,8 @@ func (t *StackTrie) grow(key []byte) {
// Update inserts a (key, value) pair into the stack trie. // Update inserts a (key, value) pair into the stack trie.
// //
// Note the supplied key value pair is copied and managed internally, they are // Note the supplied key value pair is copied and managed internally,
// safe to be modified after this method returns. // they are safe to be modified after this method returns.
func (t *StackTrie) Update(key, value []byte) error { func (t *StackTrie) Update(key, value []byte) error {
if len(value) == 0 { if len(value) == 0 {
return errors.New("trying to insert empty (deletion)") return errors.New("trying to insert empty (deletion)")
@ -120,14 +120,16 @@ func (t *StackTrie) TrieKey(key []byte) []byte {
// stNode represents a node within a StackTrie // stNode represents a node within a StackTrie
type stNode struct { type stNode struct {
typ uint8 // node type (as in branch, ext, leaf) typ uint8 // node type (as in branch, ext, leaf)
key []byte // key chunk covered by this (leaf|ext) node key []byte // exclusive owned key chunk covered by this (leaf|ext) node
val []byte // value contained by this node if it's a leaf val []byte // exclusive owned value contained by this node (leaf: value; hash: hash)
children [16]*stNode // list of children (for branch and exts) children [16]*stNode // list of children (for branch and ext)
} }
// newLeaf constructs a leaf node with provided node key and value. The key // newLeaf constructs a leaf node with provided node key and value.
// will be deep-copied in the function and safe to modify afterwards, but //
// value is not. // The key is deep-copied within the function, so it can be safely modified
// afterwards. The value is retained directly without copying, as it is
// exclusively owned by the stackTrie.
func newLeaf(key, val []byte) *stNode { func newLeaf(key, val []byte) *stNode {
st := stPool.Get().(*stNode) st := stPool.Get().(*stNode)
st.typ = leafNode st.typ = leafNode
@ -158,8 +160,8 @@ const (
func (n *stNode) reset() *stNode { func (n *stNode) reset() *stNode {
if n.typ == hashedNode { if n.typ == hashedNode {
// On hashnodes, we 'own' the val: it is guaranteed to be not held // On hashnodes, we 'own' the val: it is guaranteed to be not held
// by external caller. Hence, when we arrive here, we can put it back // by external caller. Hence, when we arrive here, we can put it
// into the pool // back into the pool
bPool.put(n.val) bPool.put(n.val)
} }
n.key = n.key[:0] n.key = n.key[:0]
@ -184,11 +186,6 @@ func (n *stNode) getDiffIndex(key []byte) int {
} }
// Helper function to that inserts a (key, value) pair into the trie. // Helper function to that inserts a (key, value) pair into the trie.
//
// - The key is not retained by this method, but always copied if needed.
// - The value is retained by this method, as long as the leaf that it represents
// remains unhashed. However: it is never modified.
// - The path is not retained by this method.
func (t *StackTrie) insert(st *stNode, key, value []byte, path []byte) { func (t *StackTrie) insert(st *stNode, key, value []byte, path []byte) {
switch st.typ { switch st.typ {
case branchNode: /* Branch */ case branchNode: /* Branch */
@ -247,16 +244,14 @@ func (t *StackTrie) insert(st *stNode, key, value []byte, path []byte) {
} }
var p *stNode var p *stNode
if diffidx == 0 { if diffidx == 0 {
// the break is on the first byte, so // the break is on the first byte, so the current node
// the current node is converted into // is converted into a branch node.
// a branch node.
st.children[0] = nil st.children[0] = nil
p = st
st.typ = branchNode st.typ = branchNode
p = st
} else { } else {
// the common prefix is at least one byte // the common prefix is at least one byte long, insert
// long, insert a new intermediate branch // a new intermediate branch node.
// node.
st.children[0] = stPool.Get().(*stNode) st.children[0] = stPool.Get().(*stNode)
st.children[0].typ = branchNode st.children[0].typ = branchNode
p = st.children[0] p = st.children[0]
@ -292,8 +287,8 @@ func (t *StackTrie) insert(st *stNode, key, value []byte, path []byte) {
if diffidx == 0 { if diffidx == 0 {
// Convert current leaf into a branch // Convert current leaf into a branch
st.typ = branchNode st.typ = branchNode
p = st
st.children[0] = nil st.children[0] = nil
p = st
} else { } else {
// Convert current node into an ext, // Convert current node into an ext,
// and insert a child branch node. // and insert a child branch node.
@ -319,9 +314,7 @@ func (t *StackTrie) insert(st *stNode, key, value []byte, path []byte) {
st.val = nil st.val = nil
case emptyNode: /* Empty */ case emptyNode: /* Empty */
st.typ = leafNode *st = *newLeaf(key, value)
st.key = append(st.key, key...) // deep-copy the key as it's volatile
st.val = value
case hashedNode: case hashedNode:
panic("trying to insert into hash") panic("trying to insert into hash")
@ -405,7 +398,8 @@ func (t *StackTrie) hash(st *stNode, path []byte) {
st.typ = hashedNode st.typ = hashedNode
st.key = st.key[:0] st.key = st.key[:0]
// Release reference to (potentially externally held) value-slice. // Release reference to value slice which is exclusively owned
// by stackTrie itself.
if cap(st.val) > 0 && t.vPool != nil { if cap(st.val) > 0 && t.vPool != nil {
t.vPool.put(st.val) t.vPool.put(st.val)
} }