From cad1e64d70145b72c18351b60d23aeb8a85d8652 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 10 Nov 2017 16:13:39 +0800 Subject: [PATCH] trie: clean up full node children hash calculation --- trie/hasher.go | 74 ++++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/trie/hasher.go b/trie/hasher.go index d063ef7b22..f8c1bee88c 100644 --- a/trie/hasher.go +++ b/trie/hasher.go @@ -26,6 +26,20 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) +// Calculator calculator is a utility used by the hasher, used to calculate the hash value of the tree node. +type calculator struct { + sha hash.Hash + buffer *bytes.Buffer +} + +// calculatorPool is a set of temporary calculators that may be individually saved and retrieved. +var calculatorPool = sync.Pool{ + New: func() interface{} { + return &calculator{buffer: new(bytes.Buffer), sha: sha3.NewKeccak256()} + }, +} + +// hasher hasher is used to calculate the hash value of the whole tree. type hasher struct { cachegen, cachelimit uint16 threaded bool @@ -40,18 +54,7 @@ func newHasher(cachegen, cachelimit uint16) *hasher { return h } -type calculator struct { - sha hash.Hash - buffer *bytes.Buffer -} - -// calculatorPool is a set of temporary calculators that may be individually saved and retrieved. -var calculatorPool = sync.Pool{ - New: func() interface{} { - return &calculator{buffer: new(bytes.Buffer), sha: sha3.NewKeccak256()} - }, -} - +// newCalculator retrieves a cleaned calculator from calculator pool. func (h *hasher) newCalculator() *calculator { calculator := calculatorPool.Get().(*calculator) calculator.buffer.Reset() @@ -135,40 +138,39 @@ func (h *hasher) hashChildren(original node, db DatabaseWriter) (node, node, err case *fullNode: // Hash the full node's children, caching the newly hashed subtrees - var wg sync.WaitGroup collapsed, cached := n.copy(), n.copy() + + hashChild := func(index int, wg *sync.WaitGroup) { + var herr error + if collapsed.Children[index] != nil { + collapsed.Children[index], cached.Children[index], herr = h.hash(n.Children[index], db, false) + if herr != nil { + h.mu.Lock() + err = herr + h.mu.Unlock() + } + } else { + collapsed.Children[index] = valueNode(nil) // Ensure that nil children are encoded as empty strings. + } + if wg != nil { + wg.Done() + } + } + if !h.threaded { // Calculate children hash concurrently. // Note. Avoid creating too much goroutines(which may hits GC), // we only thread out on the topmost full node. + var wg sync.WaitGroup for i := 0; i < 16; i++ { - if n.Children[i] != nil { - wg.Add(1) - go func(i int) { - var herr error - collapsed.Children[i], cached.Children[i], herr = h.hash(n.Children[i], db, false) - if herr != nil { - err = herr - } - wg.Done() - }(i) - } else { - collapsed.Children[i] = valueNode(nil) // Ensure that nil children are encoded as empty strings. - } + wg.Add(1) + go hashChild(i, &wg) } wg.Wait() h.threaded = true } else { for i := 0; i < 16; i++ { - if n.Children[i] != nil { - var herr error - collapsed.Children[i], cached.Children[i], herr = h.hash(n.Children[i], db, false) - if herr != nil { - err = herr - } - } else { - collapsed.Children[i] = valueNode(nil) // Ensure that nil children are encoded as empty strings. - } + hashChild(i, nil) } } if err != nil { @@ -191,9 +193,9 @@ func (h *hasher) store(n node, db DatabaseWriter, force bool) (node, error) { if _, isHash := n.(hashNode); n == nil || isHash { return n, nil } - // Generate the RLP encoding of the node calculator := h.newCalculator() defer h.returnCalculator(calculator) + // Generate the RLP encoding of the node if err := rlp.Encode(calculator.buffer, n); err != nil { panic("encode error: " + err.Error()) }