Repository: ipfs/go-bitswap
Pull request #304 (2020-03-17 20:16:00)

feat: micro-optimize priority
We send them as fixed-sized 32bit ints on the wire, so we don't need to store them locally as full ints.

This should bring each Entry down from 32 bytes to 24 bytes because the Priority and WantType should now fit into 8 bytes with no padding.

Is this super important? Probably not. But it is showing up in pprof (although most of the space is probably going to the underlying CIDs).


Patch:
diff --git a/internal/decision/engine.go b/internal/decision/engine.go
index 4b2dea4..6fe8875 100644
--- a/internal/decision/engine.go
+++ b/internal/decision/engine.go
@@ -596,7 +596,7 @@ func (e *Engine) MessageReceived(ctx context.Context, p peer.ID, m bsmsg.BitSwap
 
 				activeEntries = append(activeEntries, peertask.Task{
 					Topic:    c,
-					Priority: entry.Priority,
+					Priority: int(entry.Priority),
 					Work:     bsmsg.BlockPresenceSize(c),
 					Data: &taskData{
 						BlockSize:    0,
@@ -624,7 +624,7 @@ func (e *Engine) MessageReceived(ctx context.Context, p peer.ID, m bsmsg.BitSwap
 			}
 			activeEntries = append(activeEntries, peertask.Task{
 				Topic:    c,
-				Priority: entry.Priority,
+				Priority: int(entry.Priority),
 				Work:     entrySize,
 				Data: &taskData{
 					BlockSize:    blockSize,
@@ -692,7 +692,7 @@ func (e *Engine) ReceiveFrom(from peer.ID, blks []blocks.Block, haves []cid.Cid)
 
 				e.peerRequestQueue.PushTasks(l.Partner, peertask.Task{
 					Topic:    entry.Cid,
-					Priority: entry.Priority,
+					Priority: int(entry.Priority),
 					Work:     entrySize,
 					Data: &taskData{
 						BlockSize:    blockSize,
diff --git a/internal/decision/ledger.go b/internal/decision/ledger.go
index a607834..8f103bd 100644
--- a/internal/decision/ledger.go
+++ b/internal/decision/ledger.go
@@ -91,7 +91,7 @@ func (l *ledger) ReceivedBytes(n int) {
 	l.Accounting.BytesRecv += uint64(n)
 }
 
-func (l *ledger) Wants(k cid.Cid, priority int, wantType pb.Message_Wantlist_WantType) {
+func (l *ledger) Wants(k cid.Cid, priority int32, wantType pb.Message_Wantlist_WantType) {
 	log.Debugf("peer %s wants %s", l.Partner, k)
 	l.wantList.Add(k, priority, wantType)
 }
diff --git a/internal/messagequeue/messagequeue.go b/internal/messagequeue/messagequeue.go
index 8fccc0b..aed5fbf 100644
--- a/internal/messagequeue/messagequeue.go
+++ b/internal/messagequeue/messagequeue.go
@@ -68,7 +68,7 @@ type MessageQueue struct {
 	bcstWants recallWantlist
 	peerWants recallWantlist
 	cancels   *cid.Set
-	priority  int
+	priority  int32
 
 	// Dont touch any of these variables outside of run loop
 	sender                bsnet.MessageSender
@@ -95,7 +95,7 @@ func newRecallWantList() recallWantlist {
 }
 
 // Add want to both the pending list and the list of all wants
-func (r *recallWantlist) Add(c cid.Cid, priority int, wtype pb.Message_Wantlist_WantType) {
+func (r *recallWantlist) Add(c cid.Cid, priority int32, wtype pb.Message_Wantlist_WantType) {
 	r.allWants.Add(c, priority, wtype)
 	r.pending.Add(c, priority, wtype)
 }
diff --git a/message/message.go b/message/message.go
index c4ea0fd..6668e7c 100644
--- a/message/message.go
+++ b/message/message.go
@@ -37,7 +37,7 @@ type BitSwapMessage interface {
 	PendingBytes() int32
 
 	// AddEntry adds an entry to the Wantlist.
-	AddEntry(key cid.Cid, priority int, wantType pb.Message_Wantlist_WantType, sendDontHave bool) int
+	AddEntry(key cid.Cid, priority int32, wantType pb.Message_Wantlist_WantType, sendDontHave bool) int
 
 	// Cancel adds a CANCEL for the given CID to the message
 	// Returns the size of the CANCEL entry in the protobuf
@@ -124,7 +124,7 @@ func newMessageFromProto(pbm pb.Message) (BitSwapMessage, error) {
 		if err != nil {
 			return nil, fmt.Errorf("incorrectly formatted cid in wantlist: %s", err)
 		}
-		m.addEntry(c, int(e.Priority), e.Cancel, e.WantType, e.SendDontHave)
+		m.addEntry(c, e.Priority, e.Cancel, e.WantType, e.SendDontHave)
 	}
 
 	// deprecated
@@ -231,11 +231,11 @@ func (m *impl) Cancel(k cid.Cid) int {
 	return m.addEntry(k, 0, true, pb.Message_Wantlist_Block, false)
 }
 
-func (m *impl) AddEntry(k cid.Cid, priority int, wantType pb.Message_Wantlist_WantType, sendDontHave bool) int {
+func (m *impl) AddEntry(k cid.Cid, priority int32, wantType pb.Message_Wantlist_WantType, sendDontHave bool) int {
 	return m.addEntry(k, priority, false, wantType, sendDontHave)
 }
 
-func (m *impl) addEntry(c cid.Cid, priority int, cancel bool, wantType pb.Message_Wantlist_WantType, sendDontHave bool) int {
+func (m *impl) addEntry(c cid.Cid, priority int32, cancel bool, wantType pb.Message_Wantlist_WantType, sendDontHave bool) int {
 	e, exists := m.wantlist[c]
 	if exists {
 		// Only change priority if want is of the same type
diff --git a/network/ipfs_impl.go b/network/ipfs_impl.go
index 67159d5..b566140 100644
--- a/network/ipfs_impl.go
+++ b/network/ipfs_impl.go
@@ -67,6 +67,10 @@ func processSettings(opts ...NetOpt) Settings {
 // impl transforms the ipfs network interface, which sends and receives
 // NetMessage objects, into the bitswap network interface.
 type impl struct {
+	// NOTE: Stats must be at the top of the heap allocation to ensure 64bit
+	// alignment.
+	stats Stats
+
 	host    host.Host
 	routing routing.ContentRouting
 
@@ -79,8 +83,6 @@ type impl struct {
 
 	// inbound messages from the network are forwarded to the receiver
 	receiver Receiver
-
-	stats Stats
 }
 
 type streamMessageSender struct {
diff --git a/wantlist/wantlist.go b/wantlist/wantlist.go
index d891ad0..e18567d 100644
--- a/wantlist/wantlist.go
+++ b/wantlist/wantlist.go
@@ -18,12 +18,12 @@ type Wantlist struct {
 // Entry is an entry in a want list, consisting of a cid and its priority
 type Entry struct {
 	Cid      cid.Cid
-	Priority int
+	Priority int32
 	WantType pb.Message_Wantlist_WantType
 }
 
 // NewRefEntry creates a new reference tracked wantlist entry.
-func NewRefEntry(c cid.Cid, p int) Entry {
+func NewRefEntry(c cid.Cid, p int32) Entry {
 	return Entry{
 		Cid:      c,
 		Priority: p,
@@ -50,7 +50,7 @@ func (w *Wantlist) Len() int {
 }
 
 // Add adds an entry in a wantlist from CID & Priority, if not already present.
-func (w *Wantlist) Add(c cid.Cid, priority int, wantType pb.Message_Wantlist_WantType) bool {
+func (w *Wantlist) Add(c cid.Cid, priority int32, wantType pb.Message_Wantlist_WantType) bool {
 	e, ok := w.set[c]
 
 	// Adding want-have should not override want-block


Test patch:
diff --git a/internal/decision/engine_test.go b/internal/decision/engine_test.go
index 6f5a193..7dac950 100644
--- a/internal/decision/engine_test.go
+++ b/internal/decision/engine_test.go
@@ -1068,14 +1068,14 @@ func partnerWantBlocks(e *Engine, keys []string, partner peer.ID) {
 	add := message.New(false)
 	for i, letter := range keys {
 		block := blocks.NewBlock([]byte(letter))
-		add.AddEntry(block.Cid(), len(keys)-i, pb.Message_Wantlist_Block, true)
+		add.AddEntry(block.Cid(), int32(len(keys)-i), pb.Message_Wantlist_Block, true)
 	}
 	e.MessageReceived(context.Background(), partner, add)
 }
 
 func partnerWantBlocksHaves(e *Engine, keys []string, wantHaves []string, sendDontHave bool, partner peer.ID) {
 	add := message.New(false)
-	priority := len(wantHaves) + len(keys)
+	priority := int32(len(wantHaves) + len(keys))
 	for _, letter := range wantHaves {
 		block := blocks.NewBlock([]byte(letter))
 		add.AddEntry(block.Cid(), priority, pb.Message_Wantlist_Have, sendDontHave)
diff --git a/internal/testutil/testutil.go b/internal/testutil/testutil.go
index 54706dc..086035a 100644
--- a/internal/testutil/testutil.go
+++ b/internal/testutil/testutil.go
@@ -13,7 +13,7 @@ import (
 )
 
 var blockGenerator = blocksutil.NewBlockGenerator()
-var prioritySeq int
+var prioritySeq int32
 
 // GenerateBlocksOfSize generates a series of blocks of the given byte size
 func GenerateBlocksOfSize(n int, size int64) []blocks.Block {