Skip to content
This repository has been archived by the owner on Feb 1, 2023. It is now read-only.

feat: cache the materialized wantlist #530

Merged
merged 2 commits into from
Oct 26, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions internal/decision/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,6 @@ func (e *Engine) WantlistForPeer(p peer.ID) []wl.Entry {
entries := partner.wantList.Entries()
partner.lk.Unlock()

wl.SortEntries(entries)

return entries
}

Expand Down
12 changes: 0 additions & 12 deletions internal/messagequeue/messagequeue.go
Original file line number Diff line number Diff line change
Expand Up @@ -740,13 +740,6 @@ func (mq *MessageQueue) extractOutgoingMessage(supportsHave bool) (bsmsg.BitSwap

// Next, add the wants. If we have too many entries to fit into a single
// message, sort by priority and include the high priority ones first.
// However, avoid sorting till we really need to as this code is a
// called frequently.

// Add each regular want-have / want-block to the message.
if msgSize+(len(peerEntries)*bsmsg.MaxEntrySize) > mq.maxMessageSize {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're now eagerly sorting which could affect performance. However, I believe the real perf issue was that we were repeatedly sorting the same broadcast list over-and-over, which we're now doing at most once per change.

bswl.SortEntries(peerEntries)
}

for _, e := range peerEntries {
msgSize += mq.msg.AddEntry(e.Cid, e.Priority, e.WantType, true)
Expand All @@ -757,11 +750,6 @@ func (mq *MessageQueue) extractOutgoingMessage(supportsHave bool) (bsmsg.BitSwap
}
}

// Add each broadcast want-have to the message.
if msgSize+(len(bcstEntries)*bsmsg.MaxEntrySize) > mq.maxMessageSize {
bswl.SortEntries(bcstEntries)
}

// Add each broadcast want-have to the message
for _, e := range bcstEntries {
// Broadcast wants are sent as want-have
Expand Down
40 changes: 29 additions & 11 deletions wantlist/wantlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import (
// Wantlist is a raw list of wanted blocks and their priorities
type Wantlist struct {
set map[cid.Cid]Entry

// Re-computing this can get expensive so we memoize it.
cached []Entry
}

// Entry is an entry in a want list, consisting of a cid and its priority
Expand Down Expand Up @@ -58,11 +61,11 @@ func (w *Wantlist) Add(c cid.Cid, priority int32, wantType pb.Message_Wantlist_W
return false
}

w.set[c] = Entry{
w.put(c, Entry{
Cid: c,
Priority: priority,
WantType: wantType,
}
})

return true
}
Expand All @@ -74,7 +77,7 @@ func (w *Wantlist) Remove(c cid.Cid) bool {
return false
}

delete(w.set, c)
w.delete(c)
return true
}

Expand All @@ -91,34 +94,49 @@ func (w *Wantlist) RemoveType(c cid.Cid, wantType pb.Message_Wantlist_WantType)
return false
}

delete(w.set, c)
w.delete(c)
return true
}

func (w *Wantlist) delete(c cid.Cid) {
delete(w.set, c)
w.cached = nil
}

func (w *Wantlist) put(c cid.Cid, e Entry) {
w.cached = nil
w.set[c] = e
}

// Contains returns the entry, if present, for the given CID, plus whether it
// was present.
func (w *Wantlist) Contains(c cid.Cid) (Entry, bool) {
e, ok := w.set[c]
return e, ok
}

// Entries returns all wantlist entries for a want list.
// Entries returns all wantlist entries for a want list, sorted by priority.
//
// DO NOT MODIFY. The returned list is cached.
func (w *Wantlist) Entries() []Entry {
if w.cached != nil {
return w.cached
}
es := make([]Entry, 0, len(w.set))
for _, e := range w.set {
es = append(es, e)
}
return es
sort.Sort(entrySlice(es))
w.cached = es
return es[0:len(es):len(es)]
}

// Absorb all the entries in other into this want list
func (w *Wantlist) Absorb(other *Wantlist) {
// Invalidate the cache up-front to avoid doing any work trying to keep it up-to-date.
w.cached = nil

for _, e := range other.Entries() {
w.Add(e.Cid, e.Priority, e.WantType)
}
}

// SortEntries sorts the list of entries by priority.
func SortEntries(es []Entry) {
sort.Sort(entrySlice(es))
}
2 changes: 0 additions & 2 deletions wantlist/wantlist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,6 @@ func TestSortEntries(t *testing.T) {
wl.Add(testcids[2], 4, pb.Message_Wantlist_Have)

entries := wl.Entries()
SortEntries(entries)

if !entries[0].Cid.Equals(testcids[1]) ||
!entries[1].Cid.Equals(testcids[2]) ||
!entries[2].Cid.Equals(testcids[0]) {
Expand Down