diff --git a/trie/hasher.go b/trie/hasher.go index 54f6a9de2b6a..a98593649137 100644 --- a/trie/hasher.go +++ b/trie/hasher.go @@ -165,20 +165,20 @@ func (h *hasher) store(n node, db *Database, force bool) (node, error) { if _, isHash := n.(hashNode); n == nil || isHash { return n, nil } - // Generate the RLP encoding of the node - h.tmp.Reset() - if err := rlp.Encode(&h.tmp, n); err != nil { - panic("encode error: " + err.Error()) - } - if len(h.tmp) < 32 && !force { - return n, nil // Nodes smaller than 32 bytes are stored inside their parent - } - // Larger nodes are replaced by their hash and stored in the database. + // We might already have the hash hash, _ := n.cache() if hash == nil { + // Generate the RLP encoding of the node + h.tmp.Reset() + if err := rlp.Encode(&h.tmp, n); err != nil { + panic("encode error: " + err.Error()) + } + if len(h.tmp) < 32 && !force { + return n, nil // Nodes smaller than 32 bytes are stored inside their parent + } + // Larger nodes are replaced by their hash and stored in the database. hash = h.makeHashNode(h.tmp) } - if db != nil { // We are pooling the trie nodes into an intermediate memory cache hash := common.BytesToHash(hash) diff --git a/trie/trie_test.go b/trie/trie_test.go index e53ac568e9c3..5286c490ca0d 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -512,6 +512,42 @@ func BenchmarkHash(b *testing.B) { trie.Hash() } +// Benchmarks the trie Commit following a Hash. Since the trie caches the result of any operation, +// we cannot use b.N as the number of hashing rouns, since all rounds apart from +// the first one will be NOOP. As such, we'll use b.N as the number of account to +// insert into the trie before measuring the hashing. +func BenchmarkCommitAfterHash(b *testing.B) { + // Make the random benchmark deterministic + random := rand.New(rand.NewSource(0)) + + // Create a realistic account trie to hash + addresses := make([][20]byte, b.N) + for i := 0; i < len(addresses); i++ { + for j := 0; j < len(addresses[i]); j++ { + addresses[i][j] = byte(random.Intn(256)) + } + } + accounts := make([][]byte, len(addresses)) + for i := 0; i < len(accounts); i++ { + var ( + nonce = uint64(random.Int63()) + balance = new(big.Int).Rand(random, new(big.Int).Exp(common.Big2, common.Big256, nil)) + root = emptyRoot + code = crypto.Keccak256(nil) + ) + accounts[i], _ = rlp.EncodeToBytes([]interface{}{nonce, balance, root, code}) + } + // Insert the accounts into the trie and hash it + trie := newEmpty() + for i := 0; i < len(addresses); i++ { + trie.Update(crypto.Keccak256(addresses[i][:]), accounts[i]) + } + trie.Hash() + b.ResetTimer() + b.ReportAllocs() + trie.Commit(nil) +} + func tempDB() (string, *Database) { dir, err := ioutil.TempDir("", "trie-bench") if err != nil {