Skip to content

Commit 97c622c

Browse files
committed
trie: make hasher parallel when number of changes are large
1 parent 7547565 commit 97c622c

File tree

5 files changed

+45
-15
lines changed

5 files changed

+45
-15
lines changed

trie/hasher.go

+28-9
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ func (b *sliceBuffer) Reset() {
4646
// hasher is a type used for the trie Hash operation. A hasher has some
4747
// internal preallocated temp space
4848
type hasher struct {
49-
sha keccakState
50-
tmp sliceBuffer
49+
sha keccakState
50+
tmp sliceBuffer
51+
parallel bool // Whether to use paralallel threads when hashing
5152
}
5253

5354
// hasherPool holds pureHashers
@@ -60,8 +61,9 @@ var hasherPool = sync.Pool{
6061
},
6162
}
6263

63-
func newHasher() *hasher {
64+
func newHasher(parallel bool) *hasher {
6465
h := hasherPool.Get().(*hasher)
66+
h.parallel = parallel
6567
return h
6668
}
6769

@@ -126,14 +128,31 @@ func (h *hasher) hashFullNodeChildren(n *fullNode) (collapsed *fullNode, cached
126128
// Hash the full node's children, caching the newly hashed subtrees
127129
cached = n.copy()
128130
collapsed = n.copy()
129-
for i := 0; i < 16; i++ {
130-
if child := n.Children[i]; child != nil {
131-
collapsed.Children[i], cached.Children[i] = h.hash(child, false)
132-
} else {
133-
collapsed.Children[i] = nilValueNode
131+
if h.parallel {
132+
var wg sync.WaitGroup
133+
wg.Add(16)
134+
for i := 0; i < 16; i++ {
135+
go func(i int) {
136+
hasher := newHasher(false)
137+
if child := n.Children[i]; child != nil {
138+
collapsed.Children[i], cached.Children[i] = hasher.hash(child, false)
139+
} else {
140+
collapsed.Children[i] = nilValueNode
141+
}
142+
wg.Done()
143+
defer returnHasherToPool(hasher)
144+
}(i)
145+
}
146+
wg.Wait()
147+
} else {
148+
for i := 0; i < 16; i++ {
149+
if child := n.Children[i]; child != nil {
150+
collapsed.Children[i], cached.Children[i] = h.hash(child, false)
151+
} else {
152+
collapsed.Children[i] = nilValueNode
153+
}
134154
}
135155
}
136-
cached.Children[16] = n.Children[16]
137156
return collapsed, cached
138157
}
139158

trie/iterator.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ func (it *nodeIterator) LeafBlob() []byte {
182182
func (it *nodeIterator) LeafProof() [][]byte {
183183
if len(it.stack) > 0 {
184184
if _, ok := it.stack[len(it.stack)-1].node.(valueNode); ok {
185-
hasher := newHasher()
185+
hasher := newHasher(false)
186186
defer returnHasherToPool(hasher)
187187
proofs := make([][]byte, 0, len(it.stack))
188188

trie/proof.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func (t *Trie) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) e
6464
panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
6565
}
6666
}
67-
hasher := newHasher()
67+
hasher := newHasher(false)
6868
defer returnHasherToPool(hasher)
6969

7070
for i, n := range nodes {

trie/secure_trie.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ func (t *SecureTrie) NodeIterator(start []byte) NodeIterator {
176176
// The caller must not hold onto the return value because it will become
177177
// invalid on the next call to hashKey or secKey.
178178
func (t *SecureTrie) hashKey(key []byte) []byte {
179-
h := newHasher()
179+
h := newHasher(false)
180180
h.sha.Reset()
181181
h.sha.Write(key)
182182
buf := h.sha.Sum(t.hashKeyBuf[:0])

trie/trie.go

+14-3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ type LeafCallback func(leaf []byte, parent common.Hash) error
4848
type Trie struct {
4949
db *Database
5050
root node
51+
// Keep a rough track of the number of leafs to commit
52+
dirtyCount int
53+
// And leafs to hash
54+
unhashedCount int
5155
}
5256

5357
// newFlag returns the cache flag value for a newly created node.
@@ -163,6 +167,8 @@ func (t *Trie) Update(key, value []byte) {
163167
//
164168
// If a node was not found in the database, a MissingNodeError is returned.
165169
func (t *Trie) TryUpdate(key, value []byte) error {
170+
t.unhashedCount++
171+
t.dirtyCount++
166172
k := keybytesToHex(key)
167173
if len(value) != 0 {
168174
_, n, err := t.insert(t.root, nil, k, valueNode(value))
@@ -259,6 +265,8 @@ func (t *Trie) Delete(key []byte) {
259265
// TryDelete removes any existing value for key from the trie.
260266
// If a node was not found in the database, a MissingNodeError is returned.
261267
func (t *Trie) TryDelete(key []byte) error {
268+
t.unhashedCount++
269+
t.dirtyCount++
262270
k := keybytesToHex(key)
263271
_, n, err := t.delete(t.root, nil, k)
264272
if err != nil {
@@ -405,7 +413,7 @@ func (t *Trie) resolveHash(n hashNode, prefix []byte) (node, error) {
405413
// Hash returns the root hash of the trie. It does not write to the
406414
// database and can be used even if the trie doesn't have one.
407415
func (t *Trie) Hash() common.Hash {
408-
hash, cached, _ := t.hashRoot(nil, nil)
416+
hash, cached, _ := t.hashRoot(nil)
409417
t.root = cached
410418
return common.BytesToHash(hash.(hashNode))
411419
}
@@ -454,17 +462,20 @@ func (t *Trie) Commit(onleaf LeafCallback) (root common.Hash, err error) {
454462
if common.BytesToHash(newRoot) != rootHash {
455463
panic(fmt.Sprintf("Committed root %x != roothash %x", newRoot, rootHash))
456464
}
465+
t.dirtyCount = 0
457466
t.root = newRoot
458467
return rootHash, nil
459468
}
460469

461470
// hashRoot calculates the root hash of the given trie
462-
func (t *Trie) hashRoot(db *Database, onleaf LeafCallback) (node, node, error) {
471+
func (t *Trie) hashRoot(db *Database) (node, node, error) {
463472
if t.root == nil {
464473
return hashNode(emptyRoot.Bytes()), nil, nil
465474
}
466-
h := newHasher()
475+
// If the number of changes is below 100, we let one thread handle it
476+
h := newHasher(t.unhashedCount >= 100)
467477
defer returnHasherToPool(h)
468478
hashed, cached := h.hash(t.root, true)
479+
t.unhashedCount = 0
469480
return hashed, cached, nil
470481
}

0 commit comments

Comments
 (0)