forked from forks/go-ethereum
all: create global hasher pool (#31769)
This PR creates a global hasher pool that can be used by all packages. It also removes a bunch of the package local pools. It also updates a few locations to use available hashers or the global hashing pool to reduce allocations all over the codebase. This change should reduce global allocation count by ~1% --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com>
This commit is contained in:
parent
485ff4bbff
commit
0eb2eeea90
11 changed files with 34 additions and 121 deletions
|
|
@ -18,7 +18,6 @@ package rawdb
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
|
|
@ -45,25 +44,6 @@ const HashScheme = "hash"
|
|||
// on extra state diffs to survive deep reorg.
|
||||
const PathScheme = "path"
|
||||
|
||||
// hasher is used to compute the sha256 hash of the provided data.
|
||||
type hasher struct{ sha crypto.KeccakState }
|
||||
|
||||
var hasherPool = sync.Pool{
|
||||
New: func() interface{} { return &hasher{sha: crypto.NewKeccakState()} },
|
||||
}
|
||||
|
||||
func newHasher() *hasher {
|
||||
return hasherPool.Get().(*hasher)
|
||||
}
|
||||
|
||||
func (h *hasher) hash(data []byte) common.Hash {
|
||||
return crypto.HashData(h.sha, data)
|
||||
}
|
||||
|
||||
func (h *hasher) release() {
|
||||
hasherPool.Put(h)
|
||||
}
|
||||
|
||||
// ReadAccountTrieNode retrieves the account trie node with the specified node path.
|
||||
func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) []byte {
|
||||
data, _ := db.Get(accountTrieNodeKey(path))
|
||||
|
|
@ -170,9 +150,7 @@ func HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash c
|
|||
if len(blob) == 0 {
|
||||
return false
|
||||
}
|
||||
h := newHasher()
|
||||
defer h.release()
|
||||
return h.hash(blob) == hash // exists but not match
|
||||
return crypto.Keccak256Hash(blob) == hash // exists but not match
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown scheme %v", scheme))
|
||||
}
|
||||
|
|
@ -194,9 +172,7 @@ func ReadTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash
|
|||
if len(blob) == 0 {
|
||||
return nil
|
||||
}
|
||||
h := newHasher()
|
||||
defer h.release()
|
||||
if h.hash(blob) != hash {
|
||||
if crypto.Keccak256Hash(blob) != hash {
|
||||
return nil // exists but not match
|
||||
}
|
||||
return blob
|
||||
|
|
|
|||
|
|
@ -33,24 +33,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/triedb/database"
|
||||
)
|
||||
|
||||
// bufferPool holds the buffers for keccak calculation.
|
||||
var bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return crypto.NewKeccakState()
|
||||
},
|
||||
}
|
||||
|
||||
// allocBuff allocates the keccak buffer from the pool
|
||||
func allocBuff() crypto.KeccakState {
|
||||
return bufferPool.Get().(crypto.KeccakState)
|
||||
}
|
||||
|
||||
// releaseBuff returns the provided keccak buffer to the pool. It's unnecessary
|
||||
// to clear the buffer, as it will be cleared before the calculation.
|
||||
func releaseBuff(buff crypto.KeccakState) {
|
||||
bufferPool.Put(buff)
|
||||
}
|
||||
|
||||
// ContractCodeReader defines the interface for accessing contract code.
|
||||
type ContractCodeReader interface {
|
||||
// Code retrieves a particular contract's code.
|
||||
|
|
@ -167,10 +149,7 @@ func newFlatReader(reader database.StateReader) *flatReader {
|
|||
//
|
||||
// The returned account might be nil if it's not existent.
|
||||
func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||
buff := allocBuff()
|
||||
defer releaseBuff(buff)
|
||||
|
||||
account, err := r.reader.Account(crypto.HashData(buff, addr.Bytes()))
|
||||
account, err := r.reader.Account(crypto.Keccak256Hash(addr.Bytes()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -200,11 +179,8 @@ func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) {
|
|||
//
|
||||
// The returned storage slot might be empty if it's not existent.
|
||||
func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||
buff := allocBuff()
|
||||
defer releaseBuff(buff)
|
||||
|
||||
addrHash := crypto.HashData(buff, addr.Bytes())
|
||||
slotHash := crypto.HashData(buff, key.Bytes())
|
||||
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||
slotHash := crypto.Keccak256Hash(key.Bytes())
|
||||
ret, err := r.reader.Storage(addrHash, slotHash)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
|
|
|
|||
|
|
@ -25,12 +25,11 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// hasherPool holds LegacyKeccak256 hashers for rlpHash.
|
||||
var hasherPool = sync.Pool{
|
||||
New: func() interface{} { return sha3.NewLegacyKeccak256() },
|
||||
New: func() interface{} { return crypto.NewKeccakState() },
|
||||
}
|
||||
|
||||
// encodeBufferPool holds temporary encoder buffers for DeriveSha and TX encoding.
|
||||
|
|
|
|||
|
|
@ -555,7 +555,8 @@ func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *ui
|
|||
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
|
||||
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
|
||||
func (evm *EVM) Create2(caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), crypto.Keccak256(code))
|
||||
inithash := crypto.HashData(evm.interpreter.hasher, code)
|
||||
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:])
|
||||
return evm.create(caller, code, gas, endowment, contractAddr, CREATE2)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
|
@ -234,11 +233,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
|
|||
offset, size := scope.Stack.pop(), scope.Stack.peek()
|
||||
data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
|
||||
|
||||
if interpreter.hasher == nil {
|
||||
interpreter.hasher = crypto.NewKeccakState()
|
||||
} else {
|
||||
interpreter.hasher.Reset()
|
||||
}
|
||||
interpreter.hasher.Reset()
|
||||
interpreter.hasher.Write(data)
|
||||
interpreter.hasher.Read(interpreter.hasherBuf[:])
|
||||
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
|
|||
}
|
||||
}
|
||||
evm.Config.ExtraEips = extraEips
|
||||
return &EVMInterpreter{evm: evm, table: table}
|
||||
return &EVMInterpreter{evm: evm, table: table, hasher: crypto.NewKeccakState()}
|
||||
}
|
||||
|
||||
// Run loops and evaluates the contract's code with the given input data and returns
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
|
|
@ -73,6 +74,12 @@ func NewKeccakState() KeccakState {
|
|||
return sha3.NewLegacyKeccak256().(KeccakState)
|
||||
}
|
||||
|
||||
var hasherPool = sync.Pool{
|
||||
New: func() any {
|
||||
return sha3.NewLegacyKeccak256().(KeccakState)
|
||||
},
|
||||
}
|
||||
|
||||
// HashData hashes the provided data using the KeccakState and returns a 32 byte hash
|
||||
func HashData(kh KeccakState, data []byte) (h common.Hash) {
|
||||
kh.Reset()
|
||||
|
|
@ -84,22 +91,26 @@ func HashData(kh KeccakState, data []byte) (h common.Hash) {
|
|||
// Keccak256 calculates and returns the Keccak256 hash of the input data.
|
||||
func Keccak256(data ...[]byte) []byte {
|
||||
b := make([]byte, 32)
|
||||
d := NewKeccakState()
|
||||
d := hasherPool.Get().(KeccakState)
|
||||
d.Reset()
|
||||
for _, b := range data {
|
||||
d.Write(b)
|
||||
}
|
||||
d.Read(b)
|
||||
hasherPool.Put(d)
|
||||
return b
|
||||
}
|
||||
|
||||
// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
|
||||
// converting it to an internal Hash data structure.
|
||||
func Keccak256Hash(data ...[]byte) (h common.Hash) {
|
||||
d := NewKeccakState()
|
||||
d := hasherPool.Get().(KeccakState)
|
||||
d.Reset()
|
||||
for _, b := range data {
|
||||
d.Write(b)
|
||||
}
|
||||
d.Read(h[:])
|
||||
hasherPool.Put(d)
|
||||
return h
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ type hasher struct {
|
|||
|
||||
// hasherPool holds pureHashers
|
||||
var hasherPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
New: func() any {
|
||||
return &hasher{
|
||||
tmp: make([]byte, 0, 550), // cap is as large as a full fullNode.
|
||||
sha: crypto.NewKeccakState(),
|
||||
|
|
|
|||
24
trie/sync.go
24
trie/sync.go
|
|
@ -729,9 +729,7 @@ func (s *Sync) hasNode(owner common.Hash, path []byte, hash common.Hash) (exists
|
|||
} else {
|
||||
blob = rawdb.ReadStorageTrieNode(s.database, owner, path)
|
||||
}
|
||||
h := newBlobHasher()
|
||||
defer h.release()
|
||||
exists = hash == h.hash(blob)
|
||||
exists = hash == crypto.Keccak256Hash(blob)
|
||||
inconsistent = !exists && len(blob) != 0
|
||||
return exists, inconsistent
|
||||
}
|
||||
|
|
@ -746,23 +744,3 @@ func ResolvePath(path []byte) (common.Hash, []byte) {
|
|||
}
|
||||
return owner, path
|
||||
}
|
||||
|
||||
// blobHasher is used to compute the sha256 hash of the provided data.
|
||||
type blobHasher struct{ state crypto.KeccakState }
|
||||
|
||||
// blobHasherPool is the pool for reusing pre-allocated hash state.
|
||||
var blobHasherPool = sync.Pool{
|
||||
New: func() interface{} { return &blobHasher{state: crypto.NewKeccakState()} },
|
||||
}
|
||||
|
||||
func newBlobHasher() *blobHasher {
|
||||
return blobHasherPool.Get().(*blobHasher)
|
||||
}
|
||||
|
||||
func (h *blobHasher) hash(data []byte) common.Hash {
|
||||
return crypto.HashData(h.state, data)
|
||||
}
|
||||
|
||||
func (h *blobHasher) release() {
|
||||
blobHasherPool.Put(h)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,15 +115,12 @@ func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co
|
|||
dirtyNodeMissMeter.Mark(1)
|
||||
|
||||
// Try to retrieve the trie node from the clean memory cache
|
||||
h := newHasher()
|
||||
defer h.release()
|
||||
|
||||
key := nodeCacheKey(owner, path)
|
||||
if dl.nodes != nil {
|
||||
if blob := dl.nodes.Get(nil, key); len(blob) > 0 {
|
||||
cleanNodeHitMeter.Mark(1)
|
||||
cleanNodeReadMeter.Mark(int64(len(blob)))
|
||||
return blob, h.hash(blob), &nodeLoc{loc: locCleanCache, depth: depth}, nil
|
||||
return blob, crypto.Keccak256Hash(blob), &nodeLoc{loc: locCleanCache, depth: depth}, nil
|
||||
}
|
||||
cleanNodeMissMeter.Mark(1)
|
||||
}
|
||||
|
|
@ -138,7 +135,7 @@ func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co
|
|||
dl.nodes.Set(key, blob)
|
||||
cleanNodeWriteMeter.Mark(int64(len(blob)))
|
||||
}
|
||||
return blob, h.hash(blob), &nodeLoc{loc: locDiskLayer, depth: depth}, nil
|
||||
return blob, crypto.Keccak256Hash(blob), &nodeLoc{loc: locDiskLayer, depth: depth}, nil
|
||||
}
|
||||
|
||||
// account directly retrieves the account RLP associated with a particular
|
||||
|
|
@ -359,22 +356,3 @@ func (dl *diskLayer) resetCache() {
|
|||
dl.nodes.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
// hasher is used to compute the sha256 hash of the provided data.
|
||||
type hasher struct{ sha crypto.KeccakState }
|
||||
|
||||
var hasherPool = sync.Pool{
|
||||
New: func() interface{} { return &hasher{sha: crypto.NewKeccakState()} },
|
||||
}
|
||||
|
||||
func newHasher() *hasher {
|
||||
return hasherPool.Get().(*hasher)
|
||||
}
|
||||
|
||||
func (h *hasher) hash(data []byte) common.Hash {
|
||||
return crypto.HashData(h.sha, data)
|
||||
}
|
||||
|
||||
func (h *hasher) release() {
|
||||
hasherPool.Put(h)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
|
|
@ -85,10 +86,9 @@ func apply(db database.NodeDatabase, prevRoot common.Hash, postRoot common.Hash,
|
|||
func updateAccount(ctx *context, db database.NodeDatabase, addr common.Address) error {
|
||||
// The account was present in prev-state, decode it from the
|
||||
// 'slim-rlp' format bytes.
|
||||
h := newHasher()
|
||||
defer h.release()
|
||||
h := crypto.NewKeccakState()
|
||||
|
||||
addrHash := h.hash(addr.Bytes())
|
||||
addrHash := crypto.HashData(h, addr.Bytes())
|
||||
prev, err := types.FullAccount(ctx.accounts[addr])
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -113,7 +113,7 @@ func updateAccount(ctx *context, db database.NodeDatabase, addr common.Address)
|
|||
for key, val := range ctx.storages[addr] {
|
||||
tkey := key
|
||||
if ctx.rawStorageKey {
|
||||
tkey = h.hash(key.Bytes())
|
||||
tkey = crypto.HashData(h, key.Bytes())
|
||||
}
|
||||
var err error
|
||||
if len(val) == 0 {
|
||||
|
|
@ -149,10 +149,9 @@ func updateAccount(ctx *context, db database.NodeDatabase, addr common.Address)
|
|||
// account and storage is wiped out correctly.
|
||||
func deleteAccount(ctx *context, db database.NodeDatabase, addr common.Address) error {
|
||||
// The account must be existent in post-state, load the account.
|
||||
h := newHasher()
|
||||
defer h.release()
|
||||
h := crypto.NewKeccakState()
|
||||
|
||||
addrHash := h.hash(addr.Bytes())
|
||||
addrHash := crypto.HashData(h, addr.Bytes())
|
||||
blob, err := ctx.accountTrie.Get(addrHash.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -174,7 +173,7 @@ func deleteAccount(ctx *context, db database.NodeDatabase, addr common.Address)
|
|||
}
|
||||
tkey := key
|
||||
if ctx.rawStorageKey {
|
||||
tkey = h.hash(key.Bytes())
|
||||
tkey = crypto.HashData(h, key.Bytes())
|
||||
}
|
||||
if err := st.Delete(tkey.Bytes()); err != nil {
|
||||
return err
|
||||
|
|
|
|||
Loading…
Reference in a new issue