triedb/pathdb: optimize allocation

This commit is contained in:
Gary Rong 2026-01-26 16:00:42 +08:00
parent 5df5020f0d
commit 990a71c952
5 changed files with 48 additions and 87 deletions

View file

@ -42,7 +42,7 @@ func parseIndex(blob []byte, bitmapSize int) ([]*indexBlockDesc, error) {
} }
var ( var (
lastID uint32 lastID uint32
descList []*indexBlockDesc descList = make([]*indexBlockDesc, 0, len(blob)/size)
) )
for i := 0; i < len(blob)/size; i++ { for i := 0; i < len(blob)/size; i++ {
var desc indexBlockDesc var desc indexBlockDesc

View file

@ -65,13 +65,13 @@ func (d *indexBlockDesc) encode() []byte {
return buf[:] return buf[:]
} }
// decode unpacks index block descriptor from byte stream. It's safe to mutate // decode unpacks index block descriptor from byte stream. It's unsafe to mutate
// the provided byte stream after the function call. // the provided byte stream after the function call.
func (d *indexBlockDesc) decode(blob []byte) { func (d *indexBlockDesc) decode(blob []byte) {
d.max = binary.BigEndian.Uint64(blob[:8]) d.max = binary.BigEndian.Uint64(blob[:8])
d.entries = binary.BigEndian.Uint16(blob[8:10]) d.entries = binary.BigEndian.Uint16(blob[8:10])
d.id = binary.BigEndian.Uint32(blob[10:14]) d.id = binary.BigEndian.Uint32(blob[10:14])
d.extBitmap = bytes.Clone(blob[indexBlockDescSize:]) d.extBitmap = blob[indexBlockDescSize:] // no-deep copy!
} }
// copy returns a deep-copied object. // copy returns a deep-copied object.

View file

@ -285,10 +285,7 @@ func newTrienodeReader(disk ethdb.KeyValueReader, freezer ethdb.AncientReader, r
// readTrienode retrieves the trienode data from the specified trienode history. // readTrienode retrieves the trienode data from the specified trienode history.
func (r *trienodeReader) readTrienode(addrHash common.Hash, path string, historyID uint64) ([]byte, bool, error) { func (r *trienodeReader) readTrienode(addrHash common.Hash, path string, historyID uint64) ([]byte, bool, error) {
tr, err := newTrienodeHistoryReader(historyID, r.freezer) tr := newTrienodeHistoryReader(historyID, r.freezer)
if err != nil {
return nil, false, err
}
return tr.read(addrHash, path) return tr.read(addrHash, path)
} }

View file

@ -719,80 +719,60 @@ func (sr *singleTrienodeHistoryReader) read(key []byte) ([]byte, bool, error) {
// It resolves data from the underlying ancient store only when needed, minimizing // It resolves data from the underlying ancient store only when needed, minimizing
// I/O overhead. // I/O overhead.
type trienodeHistoryReader struct { type trienodeHistoryReader struct {
id uint64 // ID of the associated trienode history id uint64 // ID of the associated trienode history
reader ethdb.AncientReader // Database reader of ancient store reader ethdb.AncientReader // Database reader of ancient store
keyRanges map[common.Hash]iRange // Key ranges identifying trie chunks
valRanges map[common.Hash]iRange // Value ranges identifying trie chunks
iReaders map[common.Hash]*singleTrienodeHistoryReader // readers for each individual trie chunk
} }
// newTrienodeHistoryReader constructs the reader for specific trienode history. // newTrienodeHistoryReader constructs the reader for specific trienode history.
func newTrienodeHistoryReader(id uint64, reader ethdb.AncientReader) (*trienodeHistoryReader, error) { func newTrienodeHistoryReader(id uint64, reader ethdb.AncientReader) *trienodeHistoryReader {
r := &trienodeHistoryReader{ return &trienodeHistoryReader{
id: id, id: id,
reader: reader, reader: reader,
iReaders: make(map[common.Hash]*singleTrienodeHistoryReader),
} }
if err := r.decodeHeader(); err != nil {
return nil, err
}
return r, nil
} }
// decodeHeader decodes the header section of trienode history. // decodeHeader decodes the header section of trienode history.
func (r *trienodeHistoryReader) decodeHeader() error { func (r *trienodeHistoryReader) decodeHeader(owner common.Hash) (iRange, iRange, bool, error) {
header, err := rawdb.ReadTrienodeHistoryHeader(r.reader, r.id) header, err := rawdb.ReadTrienodeHistoryHeader(r.reader, r.id)
if err != nil { if err != nil {
return err return iRange{}, iRange{}, false, err
} }
_, owners, keyOffsets, valOffsets, err := decodeHeader(header) _, owners, keyOffsets, valOffsets, err := decodeHeader(header)
if err != nil { if err != nil {
return err return iRange{}, iRange{}, false, err
} }
r.keyRanges = make(map[common.Hash]iRange, len(owners)) pos := sort.Search(len(owners), func(i int) bool {
r.valRanges = make(map[common.Hash]iRange, len(owners)) return owner.Cmp(owners[i]) <= 0
})
if pos == len(owners) || owners[pos] != owner {
return iRange{}, iRange{}, false, nil
}
var keyRange iRange
if pos != 0 {
keyRange.start = keyOffsets[pos-1]
}
keyRange.limit = keyOffsets[pos]
for i, owner := range owners { var valRange iRange
// Decode the key range for this trie chunk if pos != 0 {
var keyStart uint32 valRange.start = valOffsets[pos-1]
if i != 0 {
keyStart = keyOffsets[i-1]
}
r.keyRanges[owner] = iRange{
start: keyStart,
limit: keyOffsets[i],
}
// Decode the value range for this trie chunk
var valStart uint32
if i != 0 {
valStart = valOffsets[i-1]
}
r.valRanges[owner] = iRange{
start: valStart,
limit: valOffsets[i],
}
} }
return nil valRange.limit = valOffsets[pos]
return keyRange, valRange, true, nil
} }
// read retrieves the trie node data with the provided TrieID and node path. // read retrieves the trie node data with the provided TrieID and node path.
func (r *trienodeHistoryReader) read(owner common.Hash, path string) ([]byte, bool, error) { func (r *trienodeHistoryReader) read(owner common.Hash, path string) ([]byte, bool, error) {
ir, ok := r.iReaders[owner] keyRange, valRange, found, err := r.decodeHeader(owner)
if !ok { if err != nil {
keyRange, exists := r.keyRanges[owner] return nil, false, err
if !exists { }
return nil, false, nil // not found if !found {
} return nil, false, nil
valRange, exists := r.valRanges[owner] }
if !exists { ir, err := newSingleTrienodeHistoryReader(r.id, r.reader, keyRange, valRange)
return nil, false, nil // not found if err != nil {
} return nil, false, err
var err error
ir, err = newSingleTrienodeHistoryReader(r.id, r.reader, keyRange, valRange)
if err != nil {
return nil, false, err
}
r.iReaders[owner] = ir
} }
return ir.read([]byte(path)) return ir.read([]byte(path))
} }

View file

@ -138,10 +138,7 @@ func TestTrienodeHistoryReader(t *testing.T) {
} }
} }
for i, h := range hs { for i, h := range hs {
tr, err := newTrienodeHistoryReader(uint64(i+1), freezer) tr := newTrienodeHistoryReader(uint64(i+1), freezer)
if err != nil {
t.Fatalf("Failed to construct the history reader: %v", err)
}
for _, owner := range h.owners { for _, owner := range h.owners {
nodes := h.nodes[owner] nodes := h.nodes[owner]
for key, value := range nodes { for key, value := range nodes {
@ -418,14 +415,13 @@ func TestTrienodeHistoryReaderNonExistentPath(t *testing.T) {
if err := rawdb.WriteTrienodeHistory(freezer, 1, header, keySection, valueSection); err != nil { if err := rawdb.WriteTrienodeHistory(freezer, 1, header, keySection, valueSection); err != nil {
t.Fatalf("Failed to write trienode history: %v", err) t.Fatalf("Failed to write trienode history: %v", err)
} }
tr := newTrienodeHistoryReader(1, freezer)
tr, err := newTrienodeHistoryReader(1, freezer)
if err != nil {
t.Fatalf("Failed to construct history reader: %v", err)
}
// Try to read a non-existent path // Try to read a non-existent path
var found bool var (
err error
found bool
)
_, found, err = tr.read(testrand.Hash(), "nonexistent") _, found, err = tr.read(testrand.Hash(), "nonexistent")
if found || err != nil { if found || err != nil {
t.Fatal("Expected not found for non-existent trie owner") t.Fatal("Expected not found for non-existent trie owner")
@ -459,11 +455,7 @@ func TestTrienodeHistoryReaderNilValues(t *testing.T) {
if err := rawdb.WriteTrienodeHistory(freezer, 1, header, keySection, valueSection); err != nil { if err := rawdb.WriteTrienodeHistory(freezer, 1, header, keySection, valueSection); err != nil {
t.Fatalf("Failed to write trienode history: %v", err) t.Fatalf("Failed to write trienode history: %v", err)
} }
tr := newTrienodeHistoryReader(1, freezer)
tr, err := newTrienodeHistoryReader(1, freezer)
if err != nil {
t.Fatalf("Failed to construct history reader: %v", err)
}
// Test reading nil values // Test reading nil values
data1, found, err := tr.read(owner, "nil1") data1, found, err := tr.read(owner, "nil1")
@ -511,11 +503,7 @@ func TestTrienodeHistoryReaderNilKey(t *testing.T) {
if err := rawdb.WriteTrienodeHistory(freezer, 1, header, keySection, valueSection); err != nil { if err := rawdb.WriteTrienodeHistory(freezer, 1, header, keySection, valueSection); err != nil {
t.Fatalf("Failed to write trienode history: %v", err) t.Fatalf("Failed to write trienode history: %v", err)
} }
tr := newTrienodeHistoryReader(1, freezer)
tr, err := newTrienodeHistoryReader(1, freezer)
if err != nil {
t.Fatalf("Failed to construct history reader: %v", err)
}
// Test reading nil values // Test reading nil values
data1, _, err := tr.read(owner, "") data1, _, err := tr.read(owner, "")
@ -713,11 +701,7 @@ func TestSearchSingle(t *testing.T) {
if err := rawdb.WriteTrienodeHistory(freezer, 1, header, keySection, valueSection); err != nil { if err := rawdb.WriteTrienodeHistory(freezer, 1, header, keySection, valueSection); err != nil {
t.Fatalf("Failed to write trienode history: %v", err) t.Fatalf("Failed to write trienode history: %v", err)
} }
tr := newTrienodeHistoryReader(1, freezer)
tr, err := newTrienodeHistoryReader(1, freezer)
if err != nil {
t.Fatalf("Failed to construct history reader: %v", err)
}
// Test reading non-existent entry // Test reading non-existent entry
keys := []string{ keys := []string{