refactored EffectiveAmount for performance

This commit is contained in:
Brannon King 2021-07-10 15:35:52 -04:00
parent f829fb6206
commit a0469820a2
6 changed files with 103 additions and 43 deletions

View file

@ -23,6 +23,8 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"runtime"
"sort"
) )
// ClaimTrie implements a Merkle Trie supporting linear history of commits. // ClaimTrie implements a Merkle Trie supporting linear history of commits.
@ -110,18 +112,6 @@ func New(cfg config.Config) (*ClaimTrie, error) {
return nil, fmt.Errorf("load blocks: %w", err) return nil, fmt.Errorf("load blocks: %w", err)
} }
if previousHeight > 0 {
hash, err := blockRepo.Get(previousHeight)
if err != nil {
return nil, fmt.Errorf("get hash: %w", err)
}
_, err = nodeManager.IncrementHeightTo(previousHeight)
if err != nil {
return nil, fmt.Errorf("node manager init: %w", err)
}
trie.SetRoot(hash, nil) // keep this after IncrementHeightTo
}
ct := &ClaimTrie{ ct := &ClaimTrie{
blockRepo: blockRepo, blockRepo: blockRepo,
temporalRepo: temporalRepo, temporalRepo: temporalRepo,
@ -149,6 +139,25 @@ func New(cfg config.Config) (*ClaimTrie, error) {
} }
ct.cleanups = cleanups ct.cleanups = cleanups
if previousHeight > 0 {
hash, err := blockRepo.Get(previousHeight)
if err != nil {
ct.Close() // TODO: the cleanups aren't run when we exit with an err above here (but should be)
return nil, fmt.Errorf("get hash: %w", err)
}
_, err = nodeManager.IncrementHeightTo(previousHeight)
if err != nil {
ct.Close()
return nil, fmt.Errorf("node manager init: %w", err)
}
trie.SetRoot(hash, nil) // keep this after IncrementHeightTo
if !ct.MerkleHash().IsEqual(hash) {
ct.Close()
return nil, fmt.Errorf("unable to restore the claim hash to %s at height %d", hash.String(), previousHeight)
}
}
return ct, nil return ct, nil
} }

View file

@ -41,8 +41,8 @@ func showClaim(c *node.Claim, n *node.Node) {
mark = "*" mark = "*"
} }
fmt.Printf("%s C ID: %s, TXO: %s\n %5d/%-5d, Status: %9s, Amount: %15d, Effective Amount: %15d\n", fmt.Printf("%s C ID: %s, TXO: %s\n %5d/%-5d, Status: %9s, Amount: %15d, Support Amount: %15d\n",
mark, c.ClaimID, c.OutPoint, c.AcceptedAt, c.ActiveAt, status[c.Status], c.Amount, c.EffectiveAmount(n.Supports)) mark, c.ClaimID, c.OutPoint, c.AcceptedAt, c.ActiveAt, status[c.Status], c.Amount, n.SupportSums[c.ClaimID])
} }
func showSupport(c *node.Claim) { func showSupport(c *node.Claim) {

View file

@ -101,23 +101,6 @@ func (c *Claim) setStatus(status Status) *Claim {
return c return c
} }
func (c *Claim) EffectiveAmount(supports ClaimList) int64 {
if c.Status != Activated {
return 0
}
amt := c.Amount
for _, s := range supports {
if s.Status == Activated && s.ClaimID == c.ClaimID { // TODO: this comparison is hit a lot; byte comparison instead of hex would be faster
amt += s.Amount
}
}
return amt
}
func (c *Claim) ExpireAt() int32 { func (c *Claim) ExpireAt() int32 {
if c.AcceptedAt+param.OriginalClaimExpirationTime > param.ExtendedClaimExpirationForkHeight { if c.AcceptedAt+param.OriginalClaimExpirationTime > param.ExtendedClaimExpirationForkHeight {

View file

@ -16,6 +16,7 @@ var (
out1 = NewOutPointFromString("0000000000000000000000000000000000000000000000000000000000000000:1") out1 = NewOutPointFromString("0000000000000000000000000000000000000000000000000000000000000000:1")
out2 = NewOutPointFromString("0000000000000000000000000000000000000000000000000000000000000000:2") out2 = NewOutPointFromString("0000000000000000000000000000000000000000000000000000000000000000:2")
out3 = NewOutPointFromString("0100000000000000000000000000000000000000000000000000000000000000:1") out3 = NewOutPointFromString("0100000000000000000000000000000000000000000000000000000000000000:1")
out4 = NewOutPointFromString("0100000000000000000000000000000000000000000000000000000000000000:2")
name1 = []byte("name1") name1 = []byte("name1")
name2 = []byte("name2") name2 = []byte("name2")
) )
@ -51,6 +52,7 @@ func TestSimpleAddClaim(t *testing.T) {
m, err := NewBaseManager(repo) m, err := NewBaseManager(repo)
r.NoError(err) r.NoError(err)
defer m.Close()
_, err = m.IncrementHeightTo(10) _, err = m.IncrementHeightTo(10)
r.NoError(err) r.NoError(err)
@ -90,6 +92,58 @@ func TestSimpleAddClaim(t *testing.T) {
r.Nil(n2) r.Nil(n2)
} }
func TestSupportAmounts(t *testing.T) {
r := require.New(t)
param.SetNetwork(wire.TestNet, "")
repo, err := noderepo.NewPebble(t.TempDir())
r.NoError(err)
m, err := NewBaseManager(repo)
r.NoError(err)
defer m.Close()
_, err = m.IncrementHeightTo(10)
r.NoError(err)
chg := change.New(change.AddClaim).SetName(name1).SetOutPoint(out1.String()).SetHeight(11).SetAmount(3)
chg.ClaimID = NewClaimID(*out1).String()
err = m.AppendChange(chg)
r.NoError(err)
chg = change.New(change.AddClaim).SetName(name1).SetOutPoint(out2.String()).SetHeight(11).SetAmount(4)
chg.ClaimID = NewClaimID(*out2).String()
err = m.AppendChange(chg)
r.NoError(err)
_, err = m.IncrementHeightTo(11)
r.NoError(err)
chg = change.New(change.AddSupport).SetName(name1).SetOutPoint(out3.String()).SetHeight(12).SetAmount(2)
chg.ClaimID = NewClaimID(*out1).String()
err = m.AppendChange(chg)
r.NoError(err)
chg = change.New(change.AddSupport).SetName(name1).SetOutPoint(out4.String()).SetHeight(12).SetAmount(2)
chg.ClaimID = NewClaimID(*out2).String()
err = m.AppendChange(chg)
r.NoError(err)
chg = change.New(change.SpendSupport).SetName(name1).SetOutPoint(out4.String()).SetHeight(12).SetAmount(2)
chg.ClaimID = NewClaimID(*out2).String()
err = m.AppendChange(chg)
r.NoError(err)
_, err = m.IncrementHeightTo(20)
r.NoError(err)
n1, err := m.Node(name1)
r.NoError(err)
r.Equal(2, len(n1.Claims))
r.Equal(int64(5), n1.BestClaim.Amount+n1.SupportSums[n1.BestClaim.ClaimID])
}
func TestNodeSort(t *testing.T) { func TestNodeSort(t *testing.T) {
r := require.New(t) r := require.New(t)

View file

@ -17,11 +17,12 @@ type Node struct {
TakenOverAt int32 // The height at when the current BestClaim took over. TakenOverAt int32 // The height at when the current BestClaim took over.
Claims ClaimList // List of all Claims. Claims ClaimList // List of all Claims.
Supports ClaimList // List of all Supports, including orphaned ones. Supports ClaimList // List of all Supports, including orphaned ones.
SupportSums map[string]int64
} }
// New returns a new node. // New returns a new node.
func New() *Node { func New() *Node {
return &Node{} return &Node{SupportSums: map[string]int64{}}
} }
func (n *Node) ApplyChange(chg change.Change, delay int32) error { func (n *Node) ApplyChange(chg change.Change, delay int32) error {
@ -94,6 +95,12 @@ func (n *Node) ApplyChange(chg change.Change, delay int32) error {
case change.SpendSupport: case change.SpendSupport:
s := n.Supports.find(byOut(*out)) s := n.Supports.find(byOut(*out))
if s != nil { if s != nil {
if s.Status == Activated {
n.SupportSums[s.ClaimID] -= s.Amount
}
// TODO: we could do without this Deactivated flag if we set expiration instead
// That would eliminate the above Sum update.
// We would also need to track the update situation, though, but that could be done locally.
s.setStatus(Deactivated) s.setStatus(Deactivated)
} else { } else {
fmt.Printf("Spending support but missing existing support with TXO %s\n "+ fmt.Printf("Spending support but missing existing support with TXO %s\n "+
@ -151,12 +158,15 @@ func (n *Node) updateTakeoverHeight(height int32, name []byte, refindBest bool)
func (n *Node) handleExpiredAndActivated(height int32) int { func (n *Node) handleExpiredAndActivated(height int32) int {
changes := 0 changes := 0
update := func(items ClaimList) ClaimList { update := func(items ClaimList, sums map[string]int64) ClaimList {
for i := 0; i < len(items); i++ { for i := 0; i < len(items); i++ {
c := items[i] c := items[i]
if c.Status == Accepted && c.ActiveAt <= height && c.VisibleAt <= height { if c.Status == Accepted && c.ActiveAt <= height && c.VisibleAt <= height {
c.setStatus(Activated) c.setStatus(Activated)
changes++ changes++
if sums != nil {
sums[c.ClaimID] += c.Amount
}
} }
if c.ExpireAt() <= height || c.Status == Deactivated { if c.ExpireAt() <= height || c.Status == Deactivated {
if i < len(items)-1 { if i < len(items)-1 {
@ -165,12 +175,15 @@ func (n *Node) handleExpiredAndActivated(height int32) int {
} }
items = items[:len(items)-1] items = items[:len(items)-1]
changes++ changes++
if sums != nil && c.Status != Deactivated {
sums[c.ClaimID] -= c.Amount
}
} }
} }
return items return items
} }
n.Claims = update(n.Claims) n.Claims = update(n.Claims, nil)
n.Supports = update(n.Supports) n.Supports = update(n.Supports, n.SupportSums)
return changes return changes
} }
@ -234,9 +247,9 @@ func (n Node) findBestClaim() *Claim {
continue continue
} }
candidateAmount := candidate.EffectiveAmount(n.Supports) candidateAmount := candidate.Amount + n.SupportSums[candidate.ClaimID]
if bestAmount <= 0 { // trying to reduce calls to EffectiveAmount if bestAmount <= 0 {
bestAmount = best.EffectiveAmount(n.Supports) bestAmount = best.Amount + n.SupportSums[best.ClaimID]
} }
switch { switch {
@ -263,7 +276,7 @@ func (n *Node) activateAllClaims(height int32) int {
count := 0 count := 0
for _, c := range n.Claims { for _, c := range n.Claims {
if c.Status == Accepted && c.ActiveAt > height && c.VisibleAt <= height { if c.Status == Accepted && c.ActiveAt > height && c.VisibleAt <= height {
c.setActiveAt(height) // don't necessary need to change this number c.setActiveAt(height) // don't necessarily need to change this number?
c.setStatus(Activated) c.setStatus(Activated)
count++ count++
} }
@ -271,9 +284,10 @@ func (n *Node) activateAllClaims(height int32) int {
for _, s := range n.Supports { for _, s := range n.Supports {
if s.Status == Accepted && s.ActiveAt > height && s.VisibleAt <= height { if s.Status == Accepted && s.ActiveAt > height && s.VisibleAt <= height {
s.setActiveAt(height) // don't necessary need to change this number s.setActiveAt(height) // don't necessarily need to change this number?
s.setStatus(Activated) s.setStatus(Activated)
count++ count++
n.SupportSums[s.ClaimID] += s.Amount
} }
} }
return count return count
@ -283,8 +297,8 @@ func (n *Node) SortClaims() {
// purposefully sorting by descent // purposefully sorting by descent
sort.Slice(n.Claims, func(j, i int) bool { sort.Slice(n.Claims, func(j, i int) bool {
iAmount := n.Claims[i].EffectiveAmount(n.Supports) iAmount := n.Claims[i].Amount + n.SupportSums[n.Claims[i].ClaimID]
jAmount := n.Claims[j].EffectiveAmount(n.Supports) jAmount := n.Claims[j].Amount + n.SupportSums[n.Claims[j].ClaimID]
switch { switch {
case iAmount < jAmount: case iAmount < jAmount:
return true return true

View file

@ -15,7 +15,7 @@ type Pebble struct {
func NewPebble(path string) (*Pebble, error) { func NewPebble(path string) (*Pebble, error) {
db, err := pebble.Open(path, &pebble.Options{Cache: pebble.NewCache(128 << 20), BytesPerSync: 16 << 20}) db, err := pebble.Open(path, &pebble.Options{Cache: pebble.NewCache(256 << 20), BytesPerSync: 16 << 20})
if err != nil { if err != nil {
return nil, fmt.Errorf("pebble open %s, %w", path, err) return nil, fmt.Errorf("pebble open %s, %w", path, err)
} }