From b066812373c40b12f7a21676a6ce2f5636c65e05 Mon Sep 17 00:00:00 2001 From: Gary Rong Date: Thu, 29 Jan 2026 10:48:01 +0800 Subject: [PATCH] triedb/pathdb: allocate hash slice from sync pool --- triedb/pathdb/layertree_test.go | 14 ++++++++++++++ triedb/pathdb/lookup.go | 31 ++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/triedb/pathdb/layertree_test.go b/triedb/pathdb/layertree_test.go index a74c6eb045..b53d99c283 100644 --- a/triedb/pathdb/layertree_test.go +++ b/triedb/pathdb/layertree_test.go @@ -882,3 +882,17 @@ func TestStorageLookup(t *testing.T) { } } } + +// goos: darwin +// goarch: arm64 +// pkg: github.com/ethereum/go-ethereum/triedb/pathdb +// cpu: Apple M1 Pro +// BenchmarkHashList +// BenchmarkHashList-8 52540634 22.61 ns/op 24 B/op 1 allocs/op +func BenchmarkHashList(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + l := getHashList() + putHashList(l) + } +} diff --git a/triedb/pathdb/lookup.go b/triedb/pathdb/lookup.go index 719546f410..540e6e4cdc 100644 --- a/triedb/pathdb/lookup.go +++ b/triedb/pathdb/lookup.go @@ -189,7 +189,7 @@ func (l *lookup) addLayer(diff *diffLayer) { for accountHash := range diff.states.accountData { list, exists := l.accounts[accountHash] if !exists { - list = make([]common.Hash, 0, 16) // TODO(rjl493456442) use sync pool + list = getHashList() } list = append(list, state) l.accounts[accountHash] = list @@ -204,7 +204,7 @@ func (l *lookup) addLayer(diff *diffLayer) { key := storageKey(accountHash, slotHash) list, exists := l.storages[key] if !exists { - list = make([]common.Hash, 0, 16) // TODO(rjl493456442) use sync pool + list = getHashList() } list = append(list, state) l.storages[key] = list @@ -229,7 +229,9 @@ func removeFromList(list []common.Hash, element common.Hash) (bool, []common.Has // Mitigation: release the array if capacity exceeds threshold. list = list[1:] if cap(list) > 1024 { - list = append(make([]common.Hash, 0, len(list)), list...) + newList := append(make([]common.Hash, 0, len(list)), list...) + putHashList(list) + list = newList } } return true, list @@ -258,6 +260,7 @@ func (l *lookup) removeLayer(diff *diffLayer) error { if len(list) != 0 { l.accounts[accountHash] = list } else { + putHashList(list) delete(l.accounts, accountHash) } } @@ -275,6 +278,7 @@ func (l *lookup) removeLayer(diff *diffLayer) error { if len(list) != 0 { l.storages[key] = list } else { + putHashList(list) delete(l.storages, key) } } @@ -283,3 +287,24 @@ func (l *lookup) removeLayer(diff *diffLayer) error { }) return eg.Wait() } + +// hashListPool is not allocation-free. Since a slice header is 24 bytes and +// cannot be stored directly in an interface, interface boxing is involved, +// which typically incurs a 24-byte allocation. This design strikes a balance +// between allocation efficiency and code simplicity. Note that it is possible +// to achieve zero allocations by wrapping the []common.Hash in a struct and +// storing a pointer to that struct in the sync.Pool. +var hashListPool = sync.Pool{ + New: func() any { + return make([]common.Hash, 0, 16) + }, +} + +func getHashList() []common.Hash { + l := hashListPool.Get().([]common.Hash) + return l[:0] +} + +func putHashList(l []common.Hash) { + hashListPool.Put(l) +}