From: Matt Joiner Date: Tue, 16 Feb 2016 13:00:55 +0000 (+1100) Subject: Add torrent.completedPieces bitmap X-Git-Tag: v1.0.0~890 X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=0f9f7ba01b928ba450448e50b2ea7c11c4dc7126;p=btrtrc.git Add torrent.completedPieces bitmap Reduce load on data.PieceComplete for torrents with lots of pieces, when reader position changes. Not sure of the improvement yet. --- diff --git a/client.go b/client.go index 5dfa4b05..7b9797e7 100644 --- a/client.go +++ b/client.go @@ -1783,34 +1783,9 @@ func (cl *Client) saveTorrentFile(t *torrent) error { return nil } -func (cl *Client) startTorrent(t *torrent) { - if t.Info == nil || t.data == nil { - panic("nope") - } - // If the client intends to upload, it needs to know what state pieces are - // in. - if !cl.config.NoUpload { - // Queue all pieces for hashing. This is done sequentially to avoid - // spamming goroutines. - for i := range t.Pieces { - t.Pieces[i].QueuedForHash = true - } - go func() { - for i := range t.Pieces { - cl.verifyPiece(t, i) - } - }() - } -} - -// Storage cannot be changed once it's set. func (cl *Client) setStorage(t *torrent, td Data) (err error) { - err = t.setStorage(td) + t.setStorage(td) cl.event.Broadcast() - if err != nil { - return - } - cl.startTorrent(t) return } @@ -2450,6 +2425,7 @@ func (me *Client) pieceHashed(t *torrent, piece int, correct bool) { log.Printf("%T: error completing piece %d: %s", t.data, piece, err) correct = false } + t.updatePieceCompletion(piece) } else if len(touchers) != 0 { log.Printf("dropping %d conns that touched piece", len(touchers)) for _, c := range touchers { diff --git a/client_test.go b/client_test.go index 6e1c9b40..9e8f1c50 100644 --- a/client_test.go +++ b/client_test.go @@ -434,7 +434,11 @@ func (me badData) WriteAt(b []byte, off int64) (int, error) { } func (me badData) WriteSectionTo(w io.Writer, off, n int64) (int64, error) { - return 0, nil + written, err := w.Write([]byte("hello")) + if err == nil { + err = io.ErrUnexpectedEOF + } + return int64(written), err } func (me badData) PieceComplete(piece int) bool { diff --git a/torrent.go b/torrent.go index c8e5e5ba..53b5788f 100644 --- a/torrent.go +++ b/torrent.go @@ -17,7 +17,6 @@ import ( "github.com/anacrolix/missinggo/itertools" "github.com/anacrolix/missinggo/perf" "github.com/anacrolix/missinggo/pubsub" - "github.com/bradfitz/iter" "github.com/anacrolix/torrent/bencode" "github.com/anacrolix/torrent/metainfo" @@ -104,7 +103,8 @@ type torrent struct { readers map[*Reader]struct{} - pendingPieces bitmap.Bitmap + pendingPieces bitmap.Bitmap + completedPieces bitmap.Bitmap connPieceInclinationPool sync.Pool } @@ -120,6 +120,10 @@ func (t *torrent) setDisplayName(dn string) { } func (t *torrent) pieceComplete(piece int) bool { + return t.completedPieces.Get(piece) +} + +func (t *torrent) pieceCompleteUncached(piece int) bool { // TODO: This is called when setting metadata, and before storage is // assigned, which doesn't seem right. return t.data != nil && t.data.PieceComplete(piece) @@ -267,12 +271,24 @@ func (t *torrent) setMetadata(md *metainfo.Info, infoBytes []byte) (err error) { return } -func (t *torrent) setStorage(td Data) (err error) { +func (t *torrent) setStorage(td Data) { if t.data != nil { t.data.Close() } t.data = td - return + t.completedPieces.Clear() + for i := range t.Pieces { + t.Pieces[i].QueuedForHash = true + } + go func() { + for i := range t.Pieces { + t.verifyPiece(i) + } + }() +} + +func (t *torrent) verifyPiece(piece int) { + t.cl.verifyPiece(t, piece) } func (t *torrent) haveAllMetadataPieces() bool { @@ -532,12 +548,7 @@ func (t *torrent) numPieces() int { } func (t *torrent) numPiecesCompleted() (num int) { - for i := range iter.N(t.Info.NumPieces()) { - if t.pieceComplete(i) { - num++ - } - } - return + return t.completedPieces.Len() } func (t *torrent) isClosed() bool { @@ -588,9 +599,11 @@ func (t *torrent) writeChunk(piece int, begin int64, data []byte) (err error) { } func (t *torrent) bitfield() (bf []bool) { - for i := range t.Pieces { - bf = append(bf, t.havePiece(i)) - } + bf = make([]bool, t.numPieces()) + t.completedPieces.IterTyped(func(piece int) (again bool) { + bf[piece] = true + return true + }) return } @@ -678,12 +691,7 @@ func (t *torrent) haveAllPieces() bool { if !t.haveInfo() { return false } - for i := range t.Pieces { - if !t.pieceComplete(i) { - return false - } - } - return true + return t.completedPieces.Len() == t.numPieces() } func (me *torrent) haveAnyPieces() bool { @@ -877,10 +885,11 @@ func (t *torrent) updatePiecePriorities() { } return true }) + t.completedPieces.IterTyped(func(piece int) (more bool) { + newPrios[piece] = PiecePriorityNone + return true + }) for i, prio := range newPrios { - if t.pieceComplete(i) { - prio = PiecePriorityNone - } if prio != t.Pieces[i].priority { t.Pieces[i].priority = prio t.piecePriorityChanged(i) @@ -970,12 +979,7 @@ func (t *torrent) pendPiece(piece int) { } func (t *torrent) getCompletedPieces() (ret bitmap.Bitmap) { - for i := range iter.N(t.numPieces()) { - if t.pieceComplete(i) { - ret.Add(i) - } - } - return + return t.completedPieces.Copy() } func (t *torrent) unpendPieces(unpend *bitmap.Bitmap) { @@ -1033,3 +1037,7 @@ func (t *torrent) putPieceInclination(pi []int) { t.connPieceInclinationPool.Put(pi) pieceInclinationsPut.Add(1) } + +func (t *torrent) updatePieceCompletion(piece int) { + t.completedPieces.Set(piece, t.pieceCompleteUncached(piece)) +}