]> granicus.if.org Git - transmission/commitdiff
(trunk libT) #3656 "endgame could be faster" -- fixed. Patch by harrydb.
authorJordan Lee <jordan@transmissionbt.com>
Tue, 15 Feb 2011 16:04:56 +0000 (16:04 +0000)
committerJordan Lee <jordan@transmissionbt.com>
Tue, 15 Feb 2011 16:04:56 +0000 (16:04 +0000)
libtransmission/completion.c
libtransmission/completion.h
libtransmission/peer-mgr.c

index c8194065be609f0ab70a8178d9cb0876817ec8f3..d9081596d8c6a84d07d7d0d032732a6c9c23b450 100644 (file)
@@ -27,6 +27,7 @@ tr_cpReset( tr_completion * cp )
     memset( cp->completeBlocks, 0, sizeof( uint16_t ) * cp->tor->info.pieceCount );
     cp->sizeNow = 0;
     cp->sizeWhenDoneIsDirty = 1;
+    cp->blocksWantedIsDirty = 1;
     cp->haveValidIsDirty = 1;
 }
 
@@ -54,6 +55,36 @@ void
 tr_cpInvalidateDND( tr_completion * cp )
 {
     cp->sizeWhenDoneIsDirty = 1;
+    cp->blocksWantedIsDirty = 1;
+}
+
+tr_block_index_t
+tr_cpBlocksMissing( const tr_completion * ccp )
+{
+    if( ccp->blocksWantedIsDirty )
+    {
+        tr_completion *    cp = (tr_completion *) ccp; /* mutable */
+        const tr_torrent * tor = cp->tor;
+        const tr_info *    info = &tor->info;
+        tr_piece_index_t   i;
+        tr_block_index_t   wanted = 0;
+        tr_block_index_t   complete = 0;
+
+        for( i = 0; i < info->pieceCount; ++i )
+        {
+            if( info->pieces[i].dnd )
+                continue;
+
+            wanted += tr_torPieceCountBlocks( tor, i );
+            complete += cp->completeBlocks[i];
+        }
+
+        cp->blocksWantedLazy = wanted;
+        cp->blocksWantedCompleteLazy = complete;
+        cp->blocksWantedIsDirty = FALSE;
+    }
+
+    return ccp->blocksWantedLazy - ccp->blocksWantedCompleteLazy;
 }
 
 uint64_t
@@ -132,6 +163,9 @@ tr_cpPieceRem( tr_completion *  cp,
         if( tr_cpBlockIsCompleteFast( cp, block ) )
             cp->sizeNow -= tr_torBlockCountBytes( tor, block );
 
+    if( !tor->info.pieces[piece].dnd )
+        cp->blocksWantedCompleteLazy -= cp->completeBlocks[piece];
+
     cp->sizeWhenDoneIsDirty = 1;
     cp->haveValidIsDirty = 1;
     cp->completeBlocks[piece] = 0;
@@ -158,6 +192,8 @@ tr_cpBlockAdd( tr_completion * cp, tr_block_index_t block )
         tr_bitfieldAdd( &cp->blockBitfield, block );
 
         cp->sizeNow += blockSize;
+        if( !tor->info.pieces[piece].dnd )
+            cp->blocksWantedCompleteLazy++;
 
         cp->haveValidIsDirty = 1;
         cp->sizeWhenDoneIsDirty = 1;
@@ -179,6 +215,7 @@ tr_cpSetHaveAll( tr_completion * cp )
     for( i=0; i<tor->info.pieceCount; ++i )
         cp->completeBlocks[i] = tr_torPieceCountBlocks( tor, i );
     cp->sizeWhenDoneIsDirty = 1;
+    cp->blocksWantedIsDirty = 1;
     cp->haveValidIsDirty = 1;
 }
 
@@ -210,6 +247,7 @@ tr_cpBlockBitfieldSet( tr_completion * cp, tr_bitfield * blockBitfield )
 
         /* invalidate the fields that are lazy-evaluated */
         cp->sizeWhenDoneIsDirty = TRUE;
+        cp->blocksWantedIsDirty = TRUE;
         cp->haveValidIsDirty = TRUE;
 
         /* to set the remaining fields, we walk through every block... */
index 738247f662d4c6de4cbf75dc00b54c68f36b3fb3..ea0ad5e27f1e57963806c87dc40257966793aa2f 100644 (file)
@@ -25,6 +25,7 @@
 typedef struct tr_completion
 {
     tr_bool  sizeWhenDoneIsDirty;
+    tr_bool  blocksWantedIsDirty;
     tr_bool  haveValidIsDirty;
 
     tr_torrent *    tor;
@@ -38,6 +39,16 @@ typedef struct tr_completion
     /* a block is complete if and only if we have it */
     uint16_t *  completeBlocks;
 
+    /* total number of blocks that we want downloaded
+       DON'T access this directly; it's a lazy field.
+       Used by tr_cpBlocksMissing(). */
+    tr_block_index_t    blocksWantedLazy;
+
+    /* total number of completed blocks that we want downloaded
+       DON'T access this directly; it's a lazy field.
+       Used by tr_cpBlocksMissing(). */
+    tr_block_index_t    blocksWantedCompleteLazy;
+
     /* number of bytes we'll have when done downloading. [0..info.totalSize]
        DON'T access this directly; it's a lazy field.
        use tr_cpSizeWhenDone() instead! */
@@ -69,6 +80,8 @@ tr_completeness            tr_cpGetStatus( const tr_completion * );
 
 uint64_t                   tr_cpHaveValid( const tr_completion * );
 
+tr_block_index_t           tr_cpBlocksMissing( const tr_completion * );
+
 uint64_t                   tr_cpSizeWhenDone( const tr_completion * );
 
 void                       tr_cpInvalidateDND( tr_completion * );
index 9b72af6fde6853888040d1027ac807eb1e5eb24e..bf9ec429e92f425052ffaa3efb5e19ab3da62cff 100644 (file)
@@ -204,6 +204,12 @@ typedef struct tr_torrent_peers
     int                        interestedCount;
     int                        maxPeers;
     time_t                     lastCancel;
+
+    /* Before the endgame this should be 0. In endgame, is contains the average
+     * number of pending requests per peer. Only peers which have more pending
+     * requests are considered 'fast' are allowed to request a block that's
+     * already been requested from another (slower?) peer. */
+    int                        endgame;
 }
 Torrent;
 
@@ -720,12 +726,16 @@ requestListLookup( Torrent * t, tr_block_index_t block, const tr_peer * peer )
                     compareReqByBlock );
 }
 
-/* how many peers are we currently requesting this block from... */
-static int
-countBlockRequests( Torrent * t, tr_block_index_t block )
+/**
+ * Find the peers are we currently requesting the block
+ * with index @a block from and append them to @a peerArr.
+ */
+static void
+getBlockRequestPeers( Torrent * t, tr_block_index_t block,
+                      tr_ptrArray * peerArr )
 {
     tr_bool exact;
-    int i, n, pos;
+    int i, pos;
     struct block_request key;
 
     key.block = block;
@@ -736,15 +746,12 @@ countBlockRequests( Torrent * t, tr_block_index_t block )
 
     assert( !exact ); /* shouldn't have a request with .peer == NULL */
 
-    n = 0;
-    for( i=pos; i<t->requestCount; ++i ) {
-        if( t->requests[i].block == block )
-            ++n;
-        else
+    for( i = pos; i < t->requestCount; ++i )
+    {
+        if( t->requests[i].block != block )
             break;
+        tr_ptrArrayAppend( peerArr, t->requests[i].peer );
     }
-
-    return n;
 }
 
 static void
@@ -848,21 +855,50 @@ pieceListSort( Torrent * t, int mode )
         qsort( t->pieces, t->pieceCount, sizeof( struct weighted_piece ), comparePieceByIndex );
 }
 
-static tr_bool
-isInEndgame( Torrent * t )
+static int
+countActiveWebseeds( const Torrent * t )
 {
-    tr_bool endgame = FALSE;
+    int activeCount = 0;
+    const tr_webseed ** w = (const tr_webseed **) tr_ptrArrayBase( &t->webseeds );
+    const tr_webseed ** const wend = w + tr_ptrArraySize( &t->webseeds );
+
+    for( ; w!=wend; ++w )
+        if( tr_webseedIsActive( *w ) )
+            ++activeCount;
+
+    return activeCount;
+}
+
+static void
+updateEndgame( Torrent * t )
+{
+    const tr_torrent * tor = t->tor;
+    const tr_block_index_t missing = tr_cpBlocksMissing( &tor->completion );
+
+    assert( t->requestCount >= 0 );
 
-    if( ( t->pieces != NULL ) && ( t->pieceCount > 0 ) )
+    if( (tr_block_index_t) t->requestCount < missing )
     {
-        const struct weighted_piece * p = t->pieces;
-        const int pending = p->requestCount;
-        const int missing = tr_cpMissingBlocksInPiece( &t->tor->completion, p->index );
-        endgame = pending >= missing;
+        /* not in endgame */
+        t->endgame = 0;
     }
+    else if( !t->endgame ) /* only recalculate when endgame first begins */
+    {
+        int numDownloading = 0;
+        const tr_peer ** p = (const tr_peer **) tr_ptrArrayBase( &t->peers );
+        const tr_peer ** const pend = p + tr_ptrArraySize( &t->peers );
+
+        /* add the active bittorrent peers... */
+        for( ; p!=pend; ++p )
+            if( (*p)->pendingReqsToPeer > 0 )
+                ++numDownloading;
+
+        /* add the active webseeds... */
+        numDownloading += countActiveWebseeds( t );
 
-    /*if( endgame ) fprintf( stderr, "ENDGAME reached\n" );*/
-    return endgame;
+        /* average number of pending requests per downloading peer */
+        t->endgame = t->requestCount / MAX( numDownloading, 1 );
+    }
 }
 
 /**
@@ -874,7 +910,7 @@ isInEndgame( Torrent * t )
 static void
 assertWeightedPiecesAreSorted( Torrent * t )
 {
-    if( !isInEndgame( t ) )
+    if( !t->endgame )
     {
         int i;
         weightTorrent = t->tor;
@@ -1068,7 +1104,6 @@ tr_peerMgrGetNextRequests( tr_torrent           * tor,
     int i;
     int got;
     Torrent * t;
-    tr_bool endgame;
     struct weighted_piece * pieces;
     const tr_bitset * have = &peer->have;
 
@@ -1087,37 +1122,52 @@ tr_peerMgrGetNextRequests( tr_torrent           * tor,
     if( t->pieces == NULL )
         pieceListRebuild( t );
 
-    endgame = isInEndgame( t );
-
+    updateEndgame( t );
     pieces = t->pieces;
     for( i=0; i<t->pieceCount && got<numwant; ++i )
     {
         struct weighted_piece * p = pieces + i;
-        const int missing = tr_cpMissingBlocksInPiece( &tor->completion, p->index );
-        const int maxDuplicatesPerBlock = endgame ? 3 : 1;
-
-        if( p->requestCount > ( missing * maxDuplicatesPerBlock ) )
-            continue;
 
         /* if the peer has this piece that we want... */
         if( tr_bitsetHasFast( have, p->index ) )
         {
             tr_block_index_t b = tr_torPieceFirstBlock( tor, p->index );
             const tr_block_index_t e = b + tr_torPieceCountBlocks( tor, p->index );
+            tr_ptrArray peerArr = TR_PTR_ARRAY_INIT;
 
             for( ; b!=e && got<numwant; ++b )
             {
+                int peerCount;
+                tr_peer ** peers;
+
                 /* don't request blocks we've already got */
                 if( tr_cpBlockIsCompleteFast( &tor->completion, b ) )
                     continue;
 
-                /* don't send the same request to the same peer twice */
-                if( tr_peerMgrDidPeerRequest( tor, peer, b ) )
-                    continue;
-
-                /* don't send the same request to any peer too many times */
-                if( countBlockRequests( t, b ) >= maxDuplicatesPerBlock )
-                    continue;
+                /* always add peer if this block has no peers yet */
+                tr_ptrArrayClear( &peerArr );
+                getBlockRequestPeers( t, b, &peerArr );
+                peers = (tr_peer **) tr_ptrArrayPeek( &peerArr, &peerCount );
+                if( peerCount != 0 )
+                {
+                    /* don't make a second block request until the endgame */
+                    if( !t->endgame )
+                        continue;
+
+                    /* don't have more than two peers requesting this block */
+                    if( peerCount > 1 )
+                        continue;
+
+                    /* don't send the same request to the same peer twice */
+                    if( peer == peers[0] )
+                        continue;
+
+                    /* in the endgame allow an additional peer to download a
+                       block but only if the peer seems to be handling requests
+                       relatively fast */
+                    if( peer->pendingReqsToPeer + numwant - got < t->endgame )
+                        continue;
+                }
 
                 /* update the caller's table */
                 setme[got++] = b;
@@ -1126,6 +1176,8 @@ tr_peerMgrGetNextRequests( tr_torrent           * tor,
                 requestListAdd( t, b, peer );
                 ++p->requestCount;
             }
+
+            tr_ptrArrayDestruct( &peerArr, NULL );
         }
     }
 
@@ -1430,9 +1482,26 @@ peerCallbackFunc( tr_peer * peer, const tr_peer_event * e, void * vt )
         {
             tr_torrent * tor = t->tor;
             tr_block_index_t block = _tr_block( tor, e->pieceIndex, e->offset );
+            int i, peerCount;
+            tr_peer ** peers;
+            tr_ptrArray peerArr = TR_PTR_ARRAY_INIT;
+
+            removeRequestFromTables( t, block, peer );
+            getBlockRequestPeers( t, block, &peerArr );
+            peers = (tr_peer **) tr_ptrArrayPeek( &peerArr, &peerCount );
+
+            /* remove additional block requests and send cancel to peers */
+            for( i=0; i<peerCount; i++ ) {
+                tr_peer * p = peers[i];
+                assert( p != peer );
+                if( p->msgs ) {
+                    tr_historyAdd( p->cancelsSentToPeer, tr_time( ), 1 );
+                    tr_peerMsgsCancel( p->msgs, block );
+                }
+                removeRequestFromTables( t, block, p );
+            }
 
-            requestListRemove( t, block, peer );
-            pieceListRemoveRequest( t, block );
+            tr_ptrArrayDestruct( &peerArr, FALSE );
 
             if( peer && peer->blocksSentToClient )
                 tr_historyAdd( peer->blocksSentToClient, tr_time( ), 1 );
@@ -2188,7 +2257,6 @@ tr_peerMgrTorrentStats( tr_torrent  * tor,
     int i, size;
     const Torrent * t = tor->torrentPeers;
     const tr_peer ** peers;
-    const tr_webseed ** webseeds;
 
     assert( tr_torrentIsLocked( tor ) );
 
@@ -2227,11 +2295,7 @@ tr_peerMgrTorrentStats( tr_torrent  * tor,
             ++*setmeSeedsConnected;
     }
 
-    webseeds = (const tr_webseed**) tr_ptrArrayBase( &t->webseeds );
-    size = tr_ptrArraySize( &t->webseeds );
-    for( i=0; i<size; ++i )
-        if( tr_webseedIsActive( webseeds[i] ) )
-            ++*setmeWebseedsSendingToUs;
+    *setmeWebseedsSendingToUs = countActiveWebseeds( t );
 }
 
 int