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 (
lastID uint32
descList []*indexBlockDesc
descList = make([]*indexBlockDesc, 0, len(blob)/size)
)
for i := 0; i < len(blob)/size; i++ {
var desc indexBlockDesc

View file

@ -65,13 +65,13 @@ func (d *indexBlockDesc) encode() []byte {
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.
func (d *indexBlockDesc) decode(blob []byte) {
d.max = binary.BigEndian.Uint64(blob[:8])
d.entries = binary.BigEndian.Uint16(blob[8:10])
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.

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.
func (r *trienodeReader) readTrienode(addrHash common.Hash, path string, historyID uint64) ([]byte, bool, error) {
tr, err := newTrienodeHistoryReader(historyID, r.freezer)
if err != nil {
return nil, false, err
}
tr := newTrienodeHistoryReader(historyID, r.freezer)
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
// I/O overhead.
type trienodeHistoryReader struct {
id uint64 // ID of the associated trienode history
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
id uint64 // ID of the associated trienode history
reader ethdb.AncientReader // Database reader of ancient store
}
// newTrienodeHistoryReader constructs the reader for specific trienode history.
func newTrienodeHistoryReader(id uint64, reader ethdb.AncientReader) (*trienodeHistoryReader, error) {
r := &trienodeHistoryReader{
id: id,
reader: reader,
iReaders: make(map[common.Hash]*singleTrienodeHistoryReader),
func newTrienodeHistoryReader(id uint64, reader ethdb.AncientReader) *trienodeHistoryReader {
return &trienodeHistoryReader{
id: id,
reader: reader,
}
if err := r.decodeHeader(); err != nil {
return nil, err
}
return r, nil
}
// 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)
if err != nil {
return err
return iRange{}, iRange{}, false, err
}
_, owners, keyOffsets, valOffsets, err := decodeHeader(header)
if err != nil {
return err
return iRange{}, iRange{}, false, err
}
r.keyRanges = make(map[common.Hash]iRange, len(owners))
r.valRanges = make(map[common.Hash]iRange, len(owners))
pos := sort.Search(len(owners), func(i int) bool {
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 {
// Decode the key range for this trie chunk
var keyStart uint32
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],
}
var valRange iRange
if pos != 0 {
valRange.start = valOffsets[pos-1]
}
return nil
valRange.limit = valOffsets[pos]
return keyRange, valRange, true, nil
}
// 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) {
ir, ok := r.iReaders[owner]
if !ok {
keyRange, exists := r.keyRanges[owner]
if !exists {
return nil, false, nil // not found
}
valRange, exists := r.valRanges[owner]
if !exists {
return nil, false, nil // not found
}
var err error
ir, err = newSingleTrienodeHistoryReader(r.id, r.reader, keyRange, valRange)
if err != nil {
return nil, false, err
}
r.iReaders[owner] = ir
keyRange, valRange, found, err := r.decodeHeader(owner)
if err != nil {
return nil, false, err
}
if !found {
return nil, false, nil
}
ir, err := newSingleTrienodeHistoryReader(r.id, r.reader, keyRange, valRange)
if err != nil {
return nil, false, err
}
return ir.read([]byte(path))
}

View file

@ -138,10 +138,7 @@ func TestTrienodeHistoryReader(t *testing.T) {
}
}
for i, h := range hs {
tr, err := newTrienodeHistoryReader(uint64(i+1), freezer)
if err != nil {
t.Fatalf("Failed to construct the history reader: %v", err)
}
tr := newTrienodeHistoryReader(uint64(i+1), freezer)
for _, owner := range h.owners {
nodes := h.nodes[owner]
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 {
t.Fatalf("Failed to write trienode history: %v", err)
}
tr, err := newTrienodeHistoryReader(1, freezer)
if err != nil {
t.Fatalf("Failed to construct history reader: %v", err)
}
tr := newTrienodeHistoryReader(1, freezer)
// Try to read a non-existent path
var found bool
var (
err error
found bool
)
_, found, err = tr.read(testrand.Hash(), "nonexistent")
if found || err != nil {
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 {
t.Fatalf("Failed to write trienode history: %v", err)
}
tr, err := newTrienodeHistoryReader(1, freezer)
if err != nil {
t.Fatalf("Failed to construct history reader: %v", err)
}
tr := newTrienodeHistoryReader(1, freezer)
// Test reading nil values
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 {
t.Fatalf("Failed to write trienode history: %v", err)
}
tr, err := newTrienodeHistoryReader(1, freezer)
if err != nil {
t.Fatalf("Failed to construct history reader: %v", err)
}
tr := newTrienodeHistoryReader(1, freezer)
// Test reading nil values
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 {
t.Fatalf("Failed to write trienode history: %v", err)
}
tr, err := newTrienodeHistoryReader(1, freezer)
if err != nil {
t.Fatalf("Failed to construct history reader: %v", err)
}
tr := newTrienodeHistoryReader(1, freezer)
// Test reading non-existent entry
keys := []string{