From 1c9ddee16f925989cc6bda3df39a98cd46c8b1f1 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 12 Mar 2026 10:20:12 +0100 Subject: [PATCH] trie/bintrie: use a sync.Pool when hashing binary tree nodes (#33989) Binary tree hashing is quite slow, owing to many factors. One of them is the GC pressure that is the consequence of allocating many hashers, as a binary tree has 4x the size of an MPT. This PR introduces an optimization that already exists for the MPT: keep a pool of hashers, in order to reduce the amount of allocations. --- trie/bintrie/hasher.go | 39 +++++++++++++++++++++++++++++++++++ trie/bintrie/internal_node.go | 4 ++-- trie/bintrie/key_encoding.go | 4 ++-- trie/bintrie/stem_node.go | 10 +++++---- 4 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 trie/bintrie/hasher.go diff --git a/trie/bintrie/hasher.go b/trie/bintrie/hasher.go new file mode 100644 index 0000000000..b81c145723 --- /dev/null +++ b/trie/bintrie/hasher.go @@ -0,0 +1,39 @@ +// Copyright 2026 go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bintrie + +import ( + "crypto/sha256" + "hash" + "sync" +) + +var sha256Pool = sync.Pool{ + New: func() any { + return sha256.New() + }, +} + +func newSha256() hash.Hash { + h := sha256Pool.Get().(hash.Hash) + h.Reset() + return h +} + +func returnSha256(h hash.Hash) { + sha256Pool.Put(h) +} diff --git a/trie/bintrie/internal_node.go b/trie/bintrie/internal_node.go index 2d02e240be..7ad76aa9db 100644 --- a/trie/bintrie/internal_node.go +++ b/trie/bintrie/internal_node.go @@ -17,7 +17,6 @@ package bintrie import ( - "crypto/sha256" "errors" "fmt" @@ -125,7 +124,8 @@ func (bt *InternalNode) Hash() common.Hash { return bt.hash } - h := sha256.New() + h := newSha256() + defer returnSha256(h) if bt.left != nil { h.Write(bt.left.Hash().Bytes()) } else { diff --git a/trie/bintrie/key_encoding.go b/trie/bintrie/key_encoding.go index 9b98bee491..c009f1529f 100644 --- a/trie/bintrie/key_encoding.go +++ b/trie/bintrie/key_encoding.go @@ -18,7 +18,6 @@ package bintrie import ( "bytes" - "crypto/sha256" "github.com/ethereum/go-ethereum/common" "github.com/holiman/uint256" @@ -51,7 +50,8 @@ func GetBinaryTreeKey(addr common.Address, key []byte) []byte { } func getBinaryTreeKey(addr common.Address, offset []byte, overflow bool) []byte { - hasher := sha256.New() + hasher := newSha256() + defer returnSha256(hasher) hasher.Write(zeroHash[:12]) hasher.Write(addr[:]) var buf [32]byte diff --git a/trie/bintrie/stem_node.go b/trie/bintrie/stem_node.go index f1ae2361ff..3f69261d62 100644 --- a/trie/bintrie/stem_node.go +++ b/trie/bintrie/stem_node.go @@ -18,7 +18,6 @@ package bintrie import ( "bytes" - "crypto/sha256" "errors" "fmt" "slices" @@ -114,14 +113,17 @@ func (bt *StemNode) Hash() common.Hash { } var data [StemNodeWidth]common.Hash + h := newSha256() + defer returnSha256(h) for i, v := range bt.Values { if v != nil { - h := sha256.Sum256(v) - data[i] = common.BytesToHash(h[:]) + h.Reset() + h.Write(v) + h.Sum(data[i][:0]) } } + h.Reset() - h := sha256.New() for level := 1; level <= 8; level++ { for i := range StemNodeWidth / (1 << level) { h.Reset()