mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-27 00:46:18 +00:00
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>
73 lines
1.6 KiB
Go
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
|
|
}
|