From: Matt Joiner <anacrolix@gmail.com>
Date: Tue, 30 Nov 2021 04:18:38 +0000 (+1100)
Subject: Cache piece request orderings
X-Git-Tag: v1.39.0^2~18
X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=231301f5d77b55aab1a1ac23f7436e6145efed9f;p=btrtrc.git

Cache piece request orderings
---

diff --git a/request-strategy/order.go b/request-strategy/order.go
index 9fb45002..55f881db 100644
--- a/request-strategy/order.go
+++ b/request-strategy/order.go
@@ -2,10 +2,13 @@ package request_strategy
 
 import (
 	"bytes"
+	"expvar"
+	"runtime"
 	"sort"
 	"sync"
 
 	"github.com/anacrolix/multiless"
+	"github.com/anacrolix/torrent/metainfo"
 
 	"github.com/anacrolix/torrent/types"
 )
@@ -22,13 +25,26 @@ type (
 
 type ClientPieceOrder struct{}
 
-type filterTorrent struct {
-	*Torrent
-	unverifiedBytes int64
+func equalFilterPieces(l, r []filterPiece) bool {
+	if len(l) != len(r) {
+		return false
+	}
+	for i := range l {
+		lp := &l[i]
+		rp := &r[i]
+		if lp.Priority != rp.Priority ||
+			lp.Partial != rp.Partial ||
+			lp.Availability != rp.Availability ||
+			lp.index != rp.index ||
+			lp.t.InfoHash != rp.t.InfoHash {
+			return false
+		}
+	}
+	return true
 }
 
-func sortFilterPieces(pieces []filterPiece) {
-	sort.Slice(pieces, func(_i, _j int) bool {
+func sortFilterPieces(pieces []filterPiece, indices []int) {
+	sort.Slice(indices, func(_i, _j int) bool {
 		i := &pieces[_i]
 		j := &pieces[_j]
 		return multiless.New().Int(
@@ -87,11 +103,56 @@ func (p *requestablePiece) chunkIndexToRequestIndex(c ChunkIndex) RequestIndex {
 }
 
 type filterPiece struct {
-	t     *filterTorrent
+	t     *Torrent
 	index pieceIndex
 	*Piece
 }
 
+var (
+	sortsMu sync.Mutex
+	sorts   = map[*[]filterPiece][]int{}
+)
+
+func reorderedFilterPieces(pieces []filterPiece, indices []int) (ret []filterPiece) {
+	ret = make([]filterPiece, len(indices))
+	for i, j := range indices {
+		ret[i] = pieces[j]
+	}
+	return
+}
+
+var packageExpvarMap = expvar.NewMap("request-strategy")
+
+func getSortedFilterPieces(unsorted []filterPiece) []filterPiece {
+	sortsMu.Lock()
+	defer sortsMu.Unlock()
+	for key, order := range sorts {
+		if equalFilterPieces(*key, unsorted) {
+			packageExpvarMap.Add("reused filter piece ordering", 1)
+			return reorderedFilterPieces(unsorted, order)
+		}
+	}
+	sorted := append(make([]filterPiece, 0, len(unsorted)), unsorted...)
+	indices := make([]int, len(sorted))
+	for i := 0; i < len(indices); i++ {
+		indices[i] = i
+	}
+	sortFilterPieces(sorted, indices)
+	packageExpvarMap.Add("added filter piece ordering", 1)
+	sorts[&unsorted] = indices
+	runtime.SetFinalizer(&pieceOrderingFinalizer{unsorted: &unsorted}, func(me *pieceOrderingFinalizer) {
+		packageExpvarMap.Add("finalized filter piece ordering", 1)
+		sortsMu.Lock()
+		defer sortsMu.Unlock()
+		delete(sorts, me.unsorted)
+	})
+	return reorderedFilterPieces(unsorted, indices)
+}
+
+type pieceOrderingFinalizer struct {
+	unsorted *[]filterPiece
+}
+
 // Calls f with requestable pieces in order.
 func GetRequestablePieces(input Input, f func(t *Torrent, p *Piece, pieceIndex int)) {
 	maxPieces := 0
@@ -108,20 +169,18 @@ func GetRequestablePieces(input Input, f func(t *Torrent, p *Piece, pieceIndex i
 	}
 	for _t := range input.Torrents {
 		// TODO: We could do metainfo requests here.
-		t := &filterTorrent{
-			Torrent:         &input.Torrents[_t],
-			unverifiedBytes: 0,
-		}
+		t := &input.Torrents[_t]
 		for i := range t.Pieces {
 			pieces = append(pieces, filterPiece{
-				t:     t,
+				t:     &input.Torrents[_t],
 				index: i,
 				Piece: &t.Pieces[i],
 			})
 		}
 	}
-	sortFilterPieces(pieces)
+	pieces = getSortedFilterPieces(pieces)
 	var allTorrentsUnverifiedBytes int64
+	torrentUnverifiedBytes := map[metainfo.Hash]int64{}
 	for _, piece := range pieces {
 		if left := storageLeft; left != nil {
 			if *left < piece.Length {
@@ -134,15 +193,15 @@ func GetRequestablePieces(input Input, f func(t *Torrent, p *Piece, pieceIndex i
 			// considered unverified and hold up further requests.
 			continue
 		}
-		if piece.t.MaxUnverifiedBytes != 0 && piece.t.unverifiedBytes+piece.Length > piece.t.MaxUnverifiedBytes {
+		if piece.t.MaxUnverifiedBytes != 0 && torrentUnverifiedBytes[piece.t.InfoHash]+piece.Length > piece.t.MaxUnverifiedBytes {
 			continue
 		}
 		if input.MaxUnverifiedBytes != 0 && allTorrentsUnverifiedBytes+piece.Length > input.MaxUnverifiedBytes {
 			continue
 		}
-		piece.t.unverifiedBytes += piece.Length
+		torrentUnverifiedBytes[piece.t.InfoHash] += piece.Length
 		allTorrentsUnverifiedBytes += piece.Length
-		f(piece.t.Torrent, piece.Piece, piece.index)
+		f(piece.t, piece.Piece, piece.index)
 	}
 	return
 }