diff --git a/core/state/snapshot/hextrie_generator.go b/core/state/snapshot/hextrie_generator.go index abaad6ffe6..51200668b6 100644 --- a/core/state/snapshot/hextrie_generator.go +++ b/core/state/snapshot/hextrie_generator.go @@ -27,13 +27,11 @@ type leaf struct { value []byte } -// trieGenerator is a very basic hexary trie builder which uses the same Trie -// as the rest of geth, with no enhancements or optimizations -type trieGenerator struct{} +type trieGeneratorFn func(in chan (leaf), out chan (common.Hash)) -//BenchmarkTrieGeneration/4K-8 73 15309586 ns/op 6614793 B/op 55006 allocs/op -//BenchmarkTrieGeneration/10K-8 28 39538254 ns/op 16539589 B/op 137515 allocs/op -func (gen *trieGenerator) Generate3(in chan (leaf), out chan (common.Hash)) { +// PruneGenerate is a hexary trie builder which collapses old nodes, but is still +// based on more or less the ordinary trie builder +func PruneGenerate(in chan (leaf), out chan (common.Hash)) { t := trie.NewHashTrie() for leaf := range in { t.TryUpdate(leaf.key[:], leaf.value) @@ -41,9 +39,9 @@ func (gen *trieGenerator) Generate3(in chan (leaf), out chan (common.Hash)) { out <- t.Hash() } -//BenchmarkTrieGeneration/4K-6 94 12598506 ns/op 6162370 B/op 57921 allocs/op -//BenchmarkTrieGeneration/10K-6 37 33790908 ns/op 17278751 B/op 151002 allocs/op -func (gen *trieGenerator) Generate2(in chan (leaf), out chan (common.Hash)) { +// StdGenerate is a very basic hexary trie builder which uses the same Trie +// as the rest of geth, with no enhancements or optimizations +func StdGenerate(in chan (leaf), out chan (common.Hash)) { t, _ := trie.New(common.Hash{}, trie.NewDatabase(memorydb.New())) for leaf := range in { t.TryUpdate(leaf.key[:], leaf.value) @@ -51,9 +49,9 @@ func (gen *trieGenerator) Generate2(in chan (leaf), out chan (common.Hash)) { out <- t.Hash() } -//BenchmarkTrieGeneration/4K-6 115 12755614 ns/op 2303051 B/op 42678 allocs/op -//BenchmarkTrieGeneration/10K-6 46 25374595 ns/op 5754446 B/op 106676 allocs/op -func (gen *trieGenerator) Generate(in chan (leaf), out chan (common.Hash)) { +// AppendOnlyGenerate is a very basic hexary trie builder which uses the same Trie +// as the rest of geth, but does not create as many objects while expanding the trie +func AppendOnlyGenerate(in chan (leaf), out chan (common.Hash)) { t := trie.NewAppendOnlyTrie() for leaf := range in { t.TryUpdate(leaf.key[:], leaf.value) diff --git a/core/state/snapshot/iterator_test.go b/core/state/snapshot/iterator_test.go index 5468a9a589..8d61c1e07c 100644 --- a/core/state/snapshot/iterator_test.go +++ b/core/state/snapshot/iterator_test.go @@ -84,7 +84,7 @@ func (ti *testIterator) Hash() common.Hash { } func (ti *testIterator) Account() []byte { - return nil + return []byte{ti.values[0]} } func (ti *testIterator) Release() {} diff --git a/core/state/snapshot/trie_generator_test.go b/core/state/snapshot/trie_generator_test.go index a90e1045f3..c43b63eeec 100644 --- a/core/state/snapshot/trie_generator_test.go +++ b/core/state/snapshot/trie_generator_test.go @@ -18,6 +18,7 @@ package snapshot import ( "encoding/binary" + "math/rand" "sync" "testing" @@ -26,7 +27,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" ) -func generateTrie(it AccountIterator, generator *trieGenerator) common.Hash { +func generateTrie(it AccountIterator, generatorFn trieGeneratorFn) common.Hash { var ( in = make(chan leaf) // chan to pass leaves out = make(chan common.Hash) // chan to collect result @@ -34,7 +35,7 @@ func generateTrie(it AccountIterator, generator *trieGenerator) common.Hash { ) wg.Add(1) go func() { - generator.Generate3(in, out) + generatorFn(in, out) wg.Done() }() // Feed leaves @@ -60,6 +61,7 @@ func TestTrieGeneration(t *testing.T) { base.root: base, }, } + rand.Seed(1338) // Stack three diff layers on top with various overlaps snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), randomAccountSet("0x11", "0x22", "0x33"), nil) @@ -67,10 +69,9 @@ func TestTrieGeneration(t *testing.T) { // sorted accountlists are not included in the results. head := snaps.Snapshot(common.HexToHash("0x02")) it := head.(*diffLayer).AccountIterator(common.HexToHash("0x00")) - generator := &trieGenerator{} - hash := generateTrie(it, generator) - if exp, got := hash, common.HexToHash("807fbe7d4e4c62b80b1e7f682bb13ed409467df2a5903e5af44b88f6b08d0519"); exp != got { - t.Fatalf("expected %v got %v", exp, got) + hash := generateTrie(it, AppendOnlyGenerate) + if got, exp := hash, common.HexToHash("333a7c170a3d97bd53321d0f39b1a6b9a35b286ad2d3b3ced72ca339197c5dca"); exp != got { + t.Fatalf("expected %x got %x", exp, got) } } @@ -86,6 +87,7 @@ func TestTrieGenerationAppendonly(t *testing.T) { base.root: base, }, } + rand.Seed(1337) // Stack three diff layers on top with various overlaps snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), randomAccountSet("0x11", "0x22", "0x33"), nil) @@ -93,13 +95,16 @@ func TestTrieGenerationAppendonly(t *testing.T) { // sorted accountlists are not included in the results. head := snaps.Snapshot(common.HexToHash("0x02")) it := head.(*diffLayer).AccountIterator(common.HexToHash("0x00")) - generator := &trieGenerator{} - hash := generateTrie(it, generator) - if exp, got := hash, common.HexToHash("807fbe7d4e4c62b80b1e7f682bb13ed409467df2a5903e5af44b88f6b08d0519"); exp != got { - t.Fatalf("expected %v got %v", exp, got) + hash := generateTrie(it, AppendOnlyGenerate) + if got, exp := hash, common.HexToHash("c9dd8a9602446bfcce27efbb0188a78761bf5473dd363f4ae2f17975a308344a"); exp != got { + t.Fatalf("expected %x got %x", exp, got) } } +// BenchmarkTrieGeneration/4K/standard-6 98 14141790 ns/op 6164989 B/op 57929 allocs/op +// BenchmarkTrieGeneration/4K/pruning-6 72 14015967 ns/op 6604020 B/op 54962 allocs/op +// BenchmarkTrieGeneration/10K/standard-6 42 30085495 ns/op 17280084 B/op 151006 allocs/op +// BenchmarkTrieGeneration/10K/pruning-6 32 34536586 ns/op 16510731 B/op 137402 allocs/op func BenchmarkTrieGeneration(b *testing.B) { // Get a fairly large trie // Create a custom account factory to recreate the same addresses @@ -129,13 +134,22 @@ func BenchmarkTrieGeneration(b *testing.B) { head := snaps.Snapshot(common.HexToHash("0x02")) // Call it once to make it create the lists before test starts head.(*diffLayer).AccountIterator(common.HexToHash("0x00")) - b.ResetTimer() - b.ReportAllocs() - for i := 0; i < b.N; i++ { - it := head.(*diffLayer).AccountIterator(common.HexToHash("0x00")) - generator := &trieGenerator{} - generateTrie(it, generator) - } + b.Run("standard", func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + it := head.(*diffLayer).AccountIterator(common.HexToHash("0x00")) + generateTrie(it, StdGenerate) + } + }) + b.Run("pruning", func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + it := head.(*diffLayer).AccountIterator(common.HexToHash("0x00")) + generateTrie(it, PruneGenerate) + } + }) }) b.Run("10K", func(b *testing.B) { // 4K accounts @@ -143,12 +157,21 @@ func BenchmarkTrieGeneration(b *testing.B) { head := snaps.Snapshot(common.HexToHash("0x02")) // Call it once to make it create the lists before test starts head.(*diffLayer).AccountIterator(common.HexToHash("0x00")) - b.ResetTimer() - b.ReportAllocs() - for i := 0; i < b.N; i++ { - it := head.(*diffLayer).AccountIterator(common.HexToHash("0x00")) - generator := &trieGenerator{} - generateTrie(it, generator) - } + b.Run("standard", func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + it := head.(*diffLayer).AccountIterator(common.HexToHash("0x00")) + generateTrie(it, StdGenerate) + } + }) + b.Run("pruning", func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + it := head.(*diffLayer).AccountIterator(common.HexToHash("0x00")) + generateTrie(it, PruneGenerate) + } + }) }) }