go-ethereum/nomt/core/hasher.go
weiihann 036e37809e nomt: optimize Hash() pipeline — pool hashers, eliminate redundant sorts, in-place merge
Performance optimizations to the NOMT storage engine while preserving
correctness (all triecompare cross-validation tests pass at 10K+ scale):

- Pool SHA256 hashers via sync.Pool in HashInternal and HashStem
- Replace allStems map with sorted slice + O(N+M) merge (in-place fast
  path for incremental updates avoids allocation entirely)
- Add UpdateSorted to db.DB, skipping redundant sort of pre-sorted ops
- Simplify canonicalRoot to use pre-sorted allStems directly
- Optimize StemSharedBits with byte-level XOR + bits.LeadingZeros8
- Replace stemLess loops with bytes.Compare in all locations
- Eliminate per-stem map alloc in groupAndHashStems (use [256]bool dirty)
- Use stack-allocated [248]bool for downBits in BuildInternalTree
- Remove unused stemPathCmp function

BenchmarkHash/10000/nomt: 9.8ms → 8.2ms (-16%)
BenchmarkBlockWorkload/nomt: 7.7ms → 6.6ms (-14%)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 01:11:59 +08:00

73 lines
1.6 KiB
Go

package core
import (
"crypto/sha256"
"hash"
"sync"
)
const (
// StemSize is the number of bytes in a stem path (248 bits).
StemSize = 31
// StemNodeWidth is the number of value slots per stem node.
StemNodeWidth = 256
// HashSize is the size of a SHA256 hash in bytes.
HashSize = 32
)
var sha256Pool = sync.Pool{
New: func() any { return sha256.New() },
}
// HashInternal computes SHA256(left || right) matching EIP-7864's InternalNode.Hash().
func HashInternal(data *InternalData) Node {
h := sha256Pool.Get().(hash.Hash)
h.Reset()
h.Write(data.Left[:])
h.Write(data.Right[:])
var out Node
h.Sum(out[:0])
sha256Pool.Put(h)
return out
}
// HashStem computes the stem node hash matching EIP-7864's StemNode.Hash().
//
// Algorithm:
// 1. SHA256 each non-nil value to get 256 leaf hashes (nil → zero hash)
// 2. Build an 8-level binary SHA256 tree (256 → 128 → ... → 1 root)
// Skip pairs where both children are zero (produce zero parent)
// 3. Final hash: SHA256(stem || 0x00 || subtreeRoot)
func HashStem(stem [StemSize]byte, values [StemNodeWidth][]byte) Node {
var data [StemNodeWidth]Node
for i, v := range values {
if v != nil {
data[i] = sha256.Sum256(v)
}
}
h := sha256Pool.Get().(hash.Hash)
for level := 1; level <= 8; level++ {
for i := range StemNodeWidth / (1 << level) {
if data[i*2] == (Node{}) && data[i*2+1] == (Node{}) {
data[i] = Node{}
continue
}
h.Reset()
h.Write(data[i*2][:])
h.Write(data[i*2+1][:])
h.Sum(data[i][:0])
}
}
h.Reset()
h.Write(stem[:])
h.Write([]byte{0x00})
h.Write(data[0][:])
var out Node
h.Sum(out[:0])
sha256Pool.Put(h)
return out
}