From e8b2910b369d2848904a2147d9751f05d1d8c25e Mon Sep 17 00:00:00 2001 From: Brannon King Date: Wed, 28 Jul 2021 14:40:21 -0400 Subject: [PATCH] optimize collectSpentChildren, add tests for it --- claimtrie/claimtrie.go | 1 + claimtrie/claimtrie_test.go | 93 +++++++++++++++++++++++++++++++++ claimtrie/merkletrie/ramtrie.go | 2 +- claimtrie/node/manager.go | 42 +++++++++++---- claimtrie/node/manager_test.go | 23 ++++++++ 5 files changed, 150 insertions(+), 11 deletions(-) diff --git a/claimtrie/claimtrie.go b/claimtrie/claimtrie.go index 4ba666d2..6e862d90 100644 --- a/claimtrie/claimtrie.go +++ b/claimtrie/claimtrie.go @@ -354,6 +354,7 @@ func (ct *ClaimTrie) Close() { node.LogOnce("On cleanup: " + err.Error()) } } + ct.cleanups = nil } func (ct *ClaimTrie) forwardNodeChange(chg change.Change) error { diff --git a/claimtrie/claimtrie_test.go b/claimtrie/claimtrie_test.go index 2bac3e24..cb5d7d60 100644 --- a/claimtrie/claimtrie_test.go +++ b/claimtrie/claimtrie_test.go @@ -1,7 +1,9 @@ package claimtrie import ( + "math/rand" "testing" + "time" "github.com/btcsuite/btcd/claimtrie/change" "github.com/btcsuite/btcd/claimtrie/config" @@ -252,3 +254,94 @@ func TestRebuild(t *testing.T) { r.NotNil(m2) r.Equal(*m, *m2) } + +func BenchmarkClaimTrie_AppendBlock(b *testing.B) { + + rand.Seed(42) + names := make([][]byte, 0, b.N) + + for i := 0; i < b.N; i++ { + names = append(names, randomName()) + } + + param.SetNetwork(wire.TestNet) + param.OriginalClaimExpirationTime = 1000000 + param.ExtendedClaimExpirationTime = 1000000 + cfg.DataDir = b.TempDir() + + r := require.New(b) + ct, err := New(cfg) + r.NoError(err) + defer ct.Close() + h1 := chainhash.Hash{100, 200} + + start := time.Now() + b.ResetTimer() + + c := 0 + for i := 0; i < b.N; i++ { + op := wire.OutPoint{Hash: h1, Index: uint32(i)} + id := change.NewClaimID(op) + err = ct.AddClaim(names[i], op, id, 500) + r.NoError(err) + if c++; (c & 0xff) == 0xff { + err = ct.AppendBlock() + r.NoError(err) + } + } + + for i := 0; i < b.N; i++ { + op := wire.OutPoint{Hash: h1, Index: uint32(i)} + id := change.NewClaimID(op) + op.Hash[0] = 1 + err = ct.UpdateClaim(names[i], op, 400, id) + r.NoError(err) + if c++; (c & 0xff) == 0xff { + err = ct.AppendBlock() + r.NoError(err) + } + } + + for i := 0; i < b.N; i++ { + op := wire.OutPoint{Hash: h1, Index: uint32(i)} + id := change.NewClaimID(op) + op.Hash[0] = 2 + err = ct.UpdateClaim(names[i], op, 300, id) + r.NoError(err) + if c++; (c & 0xff) == 0xff { + err = ct.AppendBlock() + r.NoError(err) + } + } + + for i := 0; i < b.N; i++ { + op := wire.OutPoint{Hash: h1, Index: uint32(i)} + id := change.NewClaimID(op) + op.Hash[0] = 3 + err = ct.SpendClaim(names[i], op, id) + r.NoError(err) + if c++; (c & 0xff) == 0xff { + err = ct.AppendBlock() + r.NoError(err) + } + } + err = ct.AppendBlock() + r.NoError(err) + + b.StopTimer() + ht := ct.height + h1 = *ct.MerkleHash() + ct.Close() + b.Logf("Running AppendBlock bench with %d names in %f sec. Height: %d, Hash: %s", + b.N, time.Since(start).Seconds(), ht, h1.String()) +} + +func randomName() []byte { + name := make([]byte, rand.Intn(30)+10) + rand.Read(name) + for i := range name { + name[i] %= 56 + name[i] += 65 + } + return name +} diff --git a/claimtrie/merkletrie/ramtrie.go b/claimtrie/merkletrie/ramtrie.go index b1ef6e94..72b21ac0 100644 --- a/claimtrie/merkletrie/ramtrie.go +++ b/claimtrie/merkletrie/ramtrie.go @@ -145,7 +145,7 @@ func (rt *RamTrie) merkleHashAllClaims(v *collapsedVertex) *chainhash.Hash { if v.claimHash != nil { claimHash = v.claimHash } else if len(childHashes) == 0 { - return v.merkleHash + return nil } childHash := NoChildrenHash diff --git a/claimtrie/node/manager.go b/claimtrie/node/manager.go index 4de622e8..d02ef4fe 100644 --- a/claimtrie/node/manager.go +++ b/claimtrie/node/manager.go @@ -1,11 +1,11 @@ package node import ( - "bytes" "container/list" "crypto/sha256" "encoding/binary" "fmt" + "sort" "strconv" "github.com/pkg/errors" @@ -199,22 +199,44 @@ func collectChildNames(changes []change.Change) { // For each non-spend change // Loop through all the spends before you and add them to your child list if they are your child + type pair struct { + name string + order int + } + + spends := make([]pair, 0, len(changes)) + for i := range changes { + t := changes[i].Type + if t != change.SpendClaim { + continue + } + spends = append(spends, pair{string(changes[i].Name), i}) + } + sort.Slice(spends, func(i, j int) bool { + if spends[i].name == spends[j].name { + return spends[i].order < spends[j].order + } + return spends[i].name < spends[j].name + }) + for i := range changes { t := changes[i].Type if t == change.SpendClaim || t == change.SpendSupport { continue } - a := changes[i].Name + a := string(changes[i].Name) sc := map[string]bool{} - for j := 0; j < i; j++ { - t = changes[j].Type - if t != change.SpendClaim { - continue - } - b := changes[j].Name - if len(b) >= len(a) && bytes.Equal(a, b[:len(a)]) { - sc[string(b)] = true + idx := sort.Search(len(spends), func(i int) bool { + return spends[i].name >= a + }) + for idx < len(spends) { + b := spends[idx].name + if len(b) >= len(a) && spends[idx].order < i && a == b[:len(a)] { + sc[b] = true + } else { + break } + idx++ } changes[i].SpentChildren = sc } diff --git a/claimtrie/node/manager_test.go b/claimtrie/node/manager_test.go index 7c9dcee0..48ae1379 100644 --- a/claimtrie/node/manager_test.go +++ b/claimtrie/node/manager_test.go @@ -226,3 +226,26 @@ func TestHasChildren(t *testing.T) { r.NoError(err) r.True(m.hasChildren([]byte("a"), 4, nil, 2)) } + +func TestCollectChildren(t *testing.T) { + r := require.New(t) + + c1 := change.Change{Name: []byte("ba"), Type: change.SpendClaim} + c2 := change.Change{Name: []byte("ba"), Type: change.UpdateClaim} + c3 := change.Change{Name: []byte("ac"), Type: change.SpendClaim} + c4 := change.Change{Name: []byte("ac"), Type: change.UpdateClaim} + c5 := change.Change{Name: []byte("a"), Type: change.SpendClaim} + c6 := change.Change{Name: []byte("a"), Type: change.UpdateClaim} + c := []change.Change{c1, c2, c3, c4, c5, c6} + + collectChildNames(c) + + r.Empty(c[0].SpentChildren) + r.Empty(c[2].SpentChildren) + r.Empty(c[4].SpentChildren) + + r.Len(c[1].SpentChildren, 1) + r.Len(c[3].SpentChildren, 1) + r.Len(c[5].SpentChildren, 2) + r.True(c[5].SpentChildren["ac"]) +}