trie: improve the node iterator seek operation (#22470)
This change improves the efficiency of the nodeIterator seek operation. Previously, seek essentially ran the iterator forward until it found the matching node. With this change, it skips over fullnode children and avoids resolving them from the database.
This commit is contained in:
committed by
GitHub
parent
3e68d627b1
commit
4b783c0064
@ -18,11 +18,14 @@ package trie
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||
)
|
||||
|
||||
@ -440,3 +443,81 @@ func checkIteratorNoDups(t *testing.T, it NodeIterator, seen map[string]bool) in
|
||||
}
|
||||
return len(seen)
|
||||
}
|
||||
|
||||
type loggingDb struct {
|
||||
getCount uint64
|
||||
backend ethdb.KeyValueStore
|
||||
}
|
||||
|
||||
func (l *loggingDb) Has(key []byte) (bool, error) {
|
||||
return l.backend.Has(key)
|
||||
}
|
||||
|
||||
func (l *loggingDb) Get(key []byte) ([]byte, error) {
|
||||
l.getCount++
|
||||
return l.backend.Get(key)
|
||||
}
|
||||
|
||||
func (l *loggingDb) Put(key []byte, value []byte) error {
|
||||
return l.backend.Put(key, value)
|
||||
}
|
||||
|
||||
func (l *loggingDb) Delete(key []byte) error {
|
||||
return l.backend.Delete(key)
|
||||
}
|
||||
|
||||
func (l *loggingDb) NewBatch() ethdb.Batch {
|
||||
return l.backend.NewBatch()
|
||||
}
|
||||
|
||||
func (l *loggingDb) NewIterator(prefix []byte, start []byte) ethdb.Iterator {
|
||||
fmt.Printf("NewIterator\n")
|
||||
return l.backend.NewIterator(prefix, start)
|
||||
}
|
||||
func (l *loggingDb) Stat(property string) (string, error) {
|
||||
return l.backend.Stat(property)
|
||||
}
|
||||
|
||||
func (l *loggingDb) Compact(start []byte, limit []byte) error {
|
||||
return l.backend.Compact(start, limit)
|
||||
}
|
||||
|
||||
func (l *loggingDb) Close() error {
|
||||
return l.backend.Close()
|
||||
}
|
||||
|
||||
// makeLargeTestTrie create a sample test trie
|
||||
func makeLargeTestTrie() (*Database, *SecureTrie, *loggingDb) {
|
||||
// Create an empty trie
|
||||
logDb := &loggingDb{0, memorydb.New()}
|
||||
triedb := NewDatabase(logDb)
|
||||
trie, _ := NewSecure(common.Hash{}, triedb)
|
||||
|
||||
// Fill it with some arbitrary data
|
||||
for i := 0; i < 10000; i++ {
|
||||
key := make([]byte, 32)
|
||||
val := make([]byte, 32)
|
||||
binary.BigEndian.PutUint64(key, uint64(i))
|
||||
binary.BigEndian.PutUint64(val, uint64(i))
|
||||
key = crypto.Keccak256(key)
|
||||
val = crypto.Keccak256(val)
|
||||
trie.Update(key, val)
|
||||
}
|
||||
trie.Commit(nil)
|
||||
// Return the generated trie
|
||||
return triedb, trie, logDb
|
||||
}
|
||||
|
||||
// Tests that the node iterator indeed walks over the entire database contents.
|
||||
func TestNodeIteratorLargeTrie(t *testing.T) {
|
||||
// Create some arbitrary test trie to iterate
|
||||
db, trie, logDb := makeLargeTestTrie()
|
||||
db.Cap(0) // flush everything
|
||||
// Do a seek operation
|
||||
trie.NodeIterator(common.FromHex("0x77667766776677766778855885885885"))
|
||||
// master: 24 get operations
|
||||
// this pr: 5 get operations
|
||||
if have, want := logDb.getCount, uint64(5); have != want {
|
||||
t.Fatalf("Too many lookups during seek, have %d want %d", have, want)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user