(trunk libT) #3656 "endgame could be faster" -- fixed. Patch by harrydb.
This commit is contained in:
parent
dede299e04
commit
d591767941
|
@ -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... */
|
||||
|
|
|
@ -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 * );
|
||||
|
|
|
@ -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 );
|
||||
|
||||
if( ( t->pieces != NULL ) && ( t->pieceCount > 0 ) )
|
||||
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( (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 );
|
||||
|
||||
/*if( endgame ) fprintf( stderr, "ENDGAME reached\n" );*/
|
||||
return endgame;
|
||||
/* add the active bittorrent peers... */
|
||||
for( ; p!=pend; ++p )
|
||||
if( (*p)->pendingReqsToPeer > 0 )
|
||||
++numDownloading;
|
||||
|
||||
/* add the active webseeds... */
|
||||
numDownloading += countActiveWebseeds( t );
|
||||
|
||||
/* 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;
|
||||
/* 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 send the same request to any peer too many times */
|
||||
if( countBlockRequests( t, b ) >= maxDuplicatesPerBlock )
|
||||
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;
|
||||
|
||||
requestListRemove( t, block, peer );
|
||||
pieceListRemoveRequest( t, block );
|
||||
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 );
|
||||
}
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue