triedb/pathdb: use channel based slice pool

This commit is contained in:
Gary Rong 2025-02-10 11:38:21 +08:00
parent 4b56cf6107
commit a07beeb2c8
1 changed files with 40 additions and 20 deletions

View File

@ -25,24 +25,42 @@ import (
"golang.org/x/sync/errgroup"
)
// slicePool is a shared pool of hash slice, for reducing the GC pressure.
var slicePool = sync.Pool{
New: func() interface{} {
slice := make([]common.Hash, 0, 16) // Pre-allocate a slice with a reasonable capacity.
return &slice
},
// slicePool is a pool for hash slice. It is safe for concurrent use.
type slicePool struct {
c chan []common.Hash
w int
}
// getSlice obtains the hash slice from the shared pool.
func getSlice() []common.Hash {
slice := *slicePool.Get().(*[]common.Hash)
slice = slice[:0]
return slice
// newSlicePool creates a new pool for shared hash slice. The sliceCap sets the
// capacity of newly allocated slices, and the nitems determines how many items
// the pool will hold, at maximum.
func newSlicePool(sliceCap, nitems int) *slicePool {
return &slicePool{
c: make(chan []common.Hash, nitems),
w: sliceCap,
}
}
// returnSlice returns the hash slice back to the shared pool for following usage.
func returnSlice(slice []common.Hash) {
slicePool.Put(&slice)
// get returns a slice. Safe for concurrent use.
func (p *slicePool) get() []common.Hash {
select {
case b := <-p.c:
return b[:0]
default:
return make([]common.Hash, 0, p.w)
}
}
// put returns a slice to the pool. Safe for concurrent use. This method
// will ignore slices that are too large (>3x the cap)
func (p *slicePool) put(b []common.Hash) {
if len(b) > 3*p.w {
return
}
select {
case p.c <- b:
default:
}
}
// lookup is an internal structure used to efficiently determine the layer in
@ -51,6 +69,7 @@ type lookup struct {
accounts map[common.Hash][]common.Hash
storages map[common.Hash]map[common.Hash][]common.Hash
descendant func(state common.Hash, ancestor common.Hash) bool
pool *slicePool
}
// newLookup initializes the lookup structure.
@ -67,6 +86,7 @@ func newLookup(head layer, descendant func(state common.Hash, ancestor common.Ha
accounts: make(map[common.Hash][]common.Hash),
storages: make(map[common.Hash]map[common.Hash][]common.Hash),
descendant: descendant,
pool: newSlicePool(16, 4096),
}
// Apply the diff layers from bottom to top
for i := len(layers) - 1; i >= 0; i-- {
@ -159,7 +179,7 @@ func (l *lookup) addLayer(diff *diffLayer) {
for accountHash := range diff.states.accountData {
list, exists := l.accounts[accountHash]
if !exists {
list = getSlice()
list = l.pool.get()
}
list = append(list, state)
l.accounts[accountHash] = list
@ -178,7 +198,7 @@ func (l *lookup) addLayer(diff *diffLayer) {
for slotHash := range slots {
list, exists := subset[slotHash]
if !exists {
list = getSlice()
list = l.pool.get()
}
list = append(list, state)
subset[slotHash] = list
@ -212,7 +232,7 @@ func (l *lookup) removeLayer(diff *diffLayer) error {
if i == 0 {
list = list[1:]
if cap(list) > 1024 {
list = append(getSlice(), list...)
list = append(l.pool.get(), list...)
}
} else {
list = append(list[:i], list[i+1:]...)
@ -227,7 +247,7 @@ func (l *lookup) removeLayer(diff *diffLayer) error {
if len(list) != 0 {
l.accounts[accountHash] = list
} else {
returnSlice(list)
l.pool.put(list)
delete(l.accounts, accountHash)
}
}
@ -252,7 +272,7 @@ func (l *lookup) removeLayer(diff *diffLayer) error {
if i == 0 {
list = list[1:]
if cap(list) > 1024 {
list = append(getSlice(), list...)
list = append(l.pool.get(), list...)
}
} else {
list = append(list[:i], list[i+1:]...)
@ -267,7 +287,7 @@ func (l *lookup) removeLayer(diff *diffLayer) error {
if len(list) != 0 {
subset[slotHash] = list
} else {
returnSlice(subset[slotHash])
l.pool.put(subset[slotHash])
delete(subset, slotHash)
}
}