trie/bintrie: align hashInternal deep branch with shallow shape

Deep branch used a pooled hash.Hash: h.Write(lh[:]) passed a subslice
through the interface, forcing lh/rh to heap; h.Sum(nil) allocated
32 B per rehash; empty children allocated 32 B via make([]byte, HashSize).

Mirror the shallow branch: write left/right hashes into a stack
[64]byte via copy() and call sha256.Sum256 in one shot. No interface
writes, no pool round-trip, no Sum(nil), no empty-child make.

Benchmark delta (M4 Pro, go1.24.0, --count=5 --benchtime=5s):

  before: 9133 ns/op  6526 B/op  95 allocs/op
  after:  8783 ns/op  5623 B/op  67 allocs/op

  vs upstream/master@53ff723cc: allocs/op -50.0% (was -29.1%),
  bytes/op -85.1% (was -82.7%), time/op +18.7% (was +23.4%).
This commit is contained in:
CPerezz 2026-04-19 08:12:54 +02:00
parent ef3217c249
commit 0c0c68da6c
No known key found for this signature in database
GPG key ID: 62045F34B97177DD

View file

@ -89,21 +89,19 @@ func (s *NodeStore) hashInternal(idx uint32) common.Hash {
return node.hash
}
h := newSha256()
defer returnSha256(h)
// Deep sequential branch — mirrors the shallow branch's shape to keep
// input on the stack. Writing lh/rh through hash.Hash (interface)
// forces escape; copy into a local [64]byte and hash it in one shot.
var input [64]byte
if !node.left.IsEmpty() {
lh := s.computeHash(node.left)
h.Write(lh[:])
} else {
h.Write(make([]byte, HashSize))
copy(input[:HashSize], lh[:])
}
if !node.right.IsEmpty() {
rh := s.computeHash(node.right)
h.Write(rh[:])
} else {
h.Write(make([]byte, HashSize))
copy(input[HashSize:], rh[:])
}
node.hash = common.BytesToHash(h.Sum(nil))
node.hash = sha256.Sum256(input[:])
node.mustRecompute = false
return node.hash
}