1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2024-12-26 09:37:56 +00:00

(libT) #1468: speed display is very jumpy

This commit is contained in:
Charles Kerr 2008-11-23 16:31:28 +00:00
parent 0bcff74d52
commit 9571f3b714
3 changed files with 104 additions and 136 deletions

View file

@ -30,7 +30,6 @@
#include "list.h" #include "list.h"
#include "net.h" #include "net.h"
#include "peer-io.h" #include "peer-io.h"
#include "ratecontrol.h"
#include "trevent.h" #include "trevent.h"
#include "utils.h" #include "utils.h"
@ -72,9 +71,8 @@ addPacketOverhead( size_t d )
struct tr_bandwidth struct tr_bandwidth
{ {
unsigned int isUnlimited : 1; unsigned int isUnlimited : 1;
size_t bytesUsed; size_t bytesLeft;
size_t bytesLeft;
}; };
struct tr_datatype struct tr_datatype
@ -220,7 +218,6 @@ didWriteWrapper( struct bufferevent * e,
{ {
struct tr_bandwidth * b = &io->bandwidth[TR_UP]; struct tr_bandwidth * b = &io->bandwidth[TR_UP];
b->bytesLeft -= MIN( b->bytesLeft, n ); b->bytesLeft -= MIN( b->bytesLeft, n );
b->bytesUsed += n;
} }
if( io->didWrite ) if( io->didWrite )
@ -256,8 +253,7 @@ canReadWrapper( struct bufferevent * e,
const size_t n = addPacketOverhead( payload ); const size_t n = addPacketOverhead( payload );
struct tr_bandwidth * b = io->bandwidth + TR_DOWN; struct tr_bandwidth * b = io->bandwidth + TR_DOWN;
b->bytesLeft -= MIN( b->bytesLeft, (size_t)n ); b->bytesLeft -= MIN( b->bytesLeft, (size_t)n );
b->bytesUsed += n; dbgmsg( io, "%zu new input bytes. bytesLeft is %zu", n, b->bytesLeft );
dbgmsg( io, "%zu new input bytes. bytesUsed is %zu, bytesLeft is %zu", n, b->bytesUsed, b->bytesLeft );
adjustInputBuffer( io ); adjustInputBuffer( io );
} }
@ -615,15 +611,6 @@ tr_peerIoSupportsFEXT( const tr_peerIo * io )
*** ***
**/ **/
size_t
tr_peerIoGetBandwidthUsed( const tr_peerIo * io,
tr_direction direction )
{
assert( io );
assert( direction == TR_UP || direction == TR_DOWN );
return io->bandwidth[direction].bytesUsed;
}
size_t size_t
tr_peerIoGetWriteBufferSpace( const tr_peerIo * io ) tr_peerIoGetWriteBufferSpace( const tr_peerIo * io )
{ {
@ -649,9 +636,9 @@ tr_peerIoGetWriteBufferSpace( const tr_peerIo * io )
} }
void void
tr_peerIoSetBandwidth( tr_peerIo * io, tr_peerIoAllocateBandwidth( tr_peerIo * io,
tr_direction direction, tr_direction direction,
size_t bytesLeft ) size_t bytesLeft )
{ {
struct tr_bandwidth * b; struct tr_bandwidth * b;
@ -660,7 +647,6 @@ tr_peerIoSetBandwidth( tr_peerIo * io,
b = io->bandwidth + direction; b = io->bandwidth + direction;
b->isUnlimited = 0; b->isUnlimited = 0;
b->bytesUsed = 0;
b->bytesLeft = bytesLeft; b->bytesLeft = bytesLeft;
adjustOutputBuffer( io ); adjustOutputBuffer( io );
@ -678,7 +664,6 @@ tr_peerIoSetBandwidthUnlimited( tr_peerIo * io,
b = io->bandwidth + direction; b = io->bandwidth + direction;
b->isUnlimited = 1; b->isUnlimited = 1;
b->bytesUsed = 0;
b->bytesLeft = 0; b->bytesLeft = 0;
adjustInputBuffer( io ); adjustInputBuffer( io );
@ -882,4 +867,3 @@ tr_peerIoGetAge( const tr_peerIo * io )
{ {
return time( NULL ) - io->timeCreated; return time( NULL ) - io->timeCreated;
} }

View file

@ -198,14 +198,11 @@ void tr_peerIoDrain( tr_peerIo * io,
*** ***
**/ **/
size_t tr_peerIoGetBandwidthUsed( const tr_peerIo * io,
tr_direction direction );
size_t tr_peerIoGetWriteBufferSpace( const tr_peerIo * io ); size_t tr_peerIoGetWriteBufferSpace( const tr_peerIo * io );
void tr_peerIoSetBandwidth( tr_peerIo * io, void tr_peerIoAllocateBandwidth( tr_peerIo * io,
tr_direction direction, tr_direction direction,
size_t bytesLeft ); size_t bytesLeft );
void tr_peerIoSetBandwidthUnlimited( tr_peerIo * io, void tr_peerIoSetBandwidthUnlimited( tr_peerIo * io,
tr_direction direction ); tr_direction direction );

View file

@ -67,7 +67,7 @@ enum
/* number of unchoked peers per torrent. /* number of unchoked peers per torrent.
* FIXME: this probably ought to be configurable */ * FIXME: this probably ought to be configurable */
MAX_UNCHOKED_PEERS = 12, MAX_UNCHOKED_PEERS = 14,
/* number of bad pieces a peer is allowed to send before we ban them */ /* number of bad pieces a peer is allowed to send before we ban them */
MAX_BAD_PIECES_PER_PEER = 3, MAX_BAD_PIECES_PER_PEER = 3,
@ -130,7 +130,6 @@ struct tr_peerMgr
tr_ptrArray * torrents; /* Torrent */ tr_ptrArray * torrents; /* Torrent */
tr_ptrArray * incomingHandshakes; /* tr_handshake */ tr_ptrArray * incomingHandshakes; /* tr_handshake */
tr_timer * bandwidthTimer; tr_timer * bandwidthTimer;
tr_ratecontrol * globalPoolRawSpeed[2];
}; };
#define tordbg( t, ... ) \ #define tordbg( t, ... ) \
@ -520,8 +519,6 @@ tr_peerMgrNew( tr_session * session )
m->session = session; m->session = session;
m->torrents = tr_ptrArrayNew( ); m->torrents = tr_ptrArrayNew( );
m->incomingHandshakes = tr_ptrArrayNew( ); m->incomingHandshakes = tr_ptrArrayNew( );
m->globalPoolRawSpeed[TR_CLIENT_TO_PEER] = tr_rcInit( );
m->globalPoolRawSpeed[TR_PEER_TO_CLIENT] = tr_rcInit( );
m->bandwidthTimer = tr_timerNew( session, bandwidthPulse, m, BANDWIDTH_PERIOD_MSEC ); m->bandwidthTimer = tr_timerNew( session, bandwidthPulse, m, BANDWIDTH_PERIOD_MSEC );
return m; return m;
} }
@ -532,8 +529,6 @@ tr_peerMgrFree( tr_peerMgr * manager )
managerLock( manager ); managerLock( manager );
tr_timerFree( &manager->bandwidthTimer ); tr_timerFree( &manager->bandwidthTimer );
tr_rcClose( manager->globalPoolRawSpeed[TR_CLIENT_TO_PEER] );
tr_rcClose( manager->globalPoolRawSpeed[TR_PEER_TO_CLIENT] );
/* free the handshakes. Abort invokes handshakeDoneCB(), which removes /* free the handshakes. Abort invokes handshakeDoneCB(), which removes
* the item from manager->handshakes, so this is a little roundabout... */ * the item from manager->handshakes, so this is a little roundabout... */
@ -2362,17 +2357,29 @@ reconnectPulse( void * vtorrent )
***** *****
****/ ****/
#if 0
#define DEBUG_DIRECTION TR_UP
#endif
static double static double
allocateHowMuch( double desired_average_kb_per_sec, bytesPerPulse( double KiB_per_second )
const tr_ratecontrol * ratecontrol ) {
return KiB_per_second * ( 1024.0 * BANDWIDTH_PERIOD_MSEC / 1000.0 );
}
/*
* @param currentSpeed current speed in KiB/s
* @param desiredSpeed desired speed in KiB/s
*/
static double
allocateHowMuch( tr_direction dir UNUSED, double currentSpeed, double desiredSpeed )
{ {
const int pulses_per_history = TR_RATECONTROL_HISTORY_MSEC / BANDWIDTH_PERIOD_MSEC; const int pulses_per_history = TR_RATECONTROL_HISTORY_MSEC / BANDWIDTH_PERIOD_MSEC;
const double seconds_per_pulse = BANDWIDTH_PERIOD_MSEC / 1000.0; const double current_bytes_per_pulse = bytesPerPulse( currentSpeed );
const double baseline_bytes_per_pulse = desired_average_kb_per_sec * 1024.0 * seconds_per_pulse; const double desired_bytes_per_pulse = bytesPerPulse( desiredSpeed );
const double min = baseline_bytes_per_pulse * 0.80; const double min = desired_bytes_per_pulse * 0.90;
const double max = baseline_bytes_per_pulse * 1.20; const double max = desired_bytes_per_pulse * 1.25;
const double current_bytes_per_pulse = tr_rcRate( ratecontrol ) * 1024.0 * seconds_per_pulse; const double next_pulse_bytes = desired_bytes_per_pulse * ( pulses_per_history + 1 )
const double next_pulse_bytes = baseline_bytes_per_pulse * ( pulses_per_history + 1 )
- ( current_bytes_per_pulse * pulses_per_history ); - ( current_bytes_per_pulse * pulses_per_history );
double clamped; double clamped;
@ -2381,91 +2388,81 @@ allocateHowMuch( double desired_average_kb_per_sec,
clamped = MAX( clamped, min ); clamped = MAX( clamped, min );
clamped = MIN( clamped, max ); clamped = MIN( clamped, max );
#if 0 #ifdef DEBUG_DIRECTION
fprintf( stderr, "desiredAvgKB is %5.2f, rate is %5.2f, allocating %5.2f (%5.2f)\n", if( dir == DEBUG_DIRECTION )
desired_average_kb_per_sec, fprintf( stderr, "currentSpeed(%5.2f) desiredSpeed(%5.2f), allocating %5.2f (%5.2f)\n",
tr_rcRate( ratecontrol ), currentSpeed,
desiredSpeed,
clamped/1024.0, clamped/1024.0,
next_pulse_bytes/1024.0 ); next_pulse_bytes/1024.0 );
#endif #endif
return clamped; return clamped;
} }
/** /**
* Distributes a fixed amount of bandwidth among a set of peers. * Distributes a fixed amount of bandwidth among a set of peers.
* *
* @param peerArray peers whose client-to-peer bandwidth will be set * @param peerArray peers whose client-to-peer bandwidth will be set
* @param direction whether to allocate upload or download bandwidth * @param direction whether to allocate upload or download bandwidth
* @param history recent bandwidth history for these peers * @param currentSpeed current speed in KiB/s for this set of peers
* @param desiredAvgKB overall bandwidth goal for this set of peers * @param desiredSpeed desired speed in KiB/s for this set of peers
*/ */
static void static void
setPeerBandwidth( tr_ptrArray * peerArray, setPeerBandwidth( tr_ptrArray * peerArray,
const tr_direction direction, const tr_direction direction,
const tr_ratecontrol * ratecontrol, double currentSpeed,
double desiredAvgKB ) double desiredSpeed )
{ {
const int peerCount = tr_ptrArraySize( peerArray ); const int MINIMUM_WELFARE_BYTES = bytesPerPulse( 5 );
const double bytes = allocateHowMuch( desiredAvgKB, ratecontrol );
const double welfareBytes = MIN( 2048, bytes * 0.2 );
const double meritBytes = MAX( 0, bytes - welfareBytes );
tr_peer ** peers = (tr_peer**) tr_ptrArrayBase( peerArray );
int i; int i;
double welfare; double welfare;
size_t bytesUsed; double meritBytes;
double meritMultiplier;
double welfareBytes;
const int peerCount = tr_ptrArraySize( peerArray );
const double bytes = allocateHowMuch( direction, currentSpeed, desiredSpeed );
tr_peer ** peers = (tr_peer**) tr_ptrArrayBase( peerArray );
assert( meritBytes >= 0.0 ); /* how many bytes we'll allocate based on merit.
assert( welfareBytes >= 0.0 ); *
assert( direction == TR_UP || direction == TR_DOWN ); * 1. When just getting started we want to give all the peers a lot
* of `welfare' allocation because we don't know which ones will
* turn out to be productive for us.
*
* 2. When we've reached steady state and are near our bandwidth limit,
* the bandwidth spent on `welfare' is going to come from peers that
* we already know are productive... which is probably a waste.
*
* 3. So we tie the merit/welfare allocations to the current speed.
* the closer the current speed gets to the maximum speed, the less
* welfare we allocate.
*
* 4. We always need to allocate /some/ welfare bytes, otherwise
* the other peers will starve.
*/
meritBytes = bytesPerPulse( MIN( currentSpeed * 1.2, desiredSpeed ) );
welfareBytes = bytes > meritBytes ? bytes - meritBytes : 0;
if( welfareBytes < MINIMUM_WELFARE_BYTES )
welfareBytes = MINIMUM_WELFARE_BYTES;
meritBytes = bytes - welfareBytes;
meritMultiplier = currentSpeed > 0.01 ? meritBytes / currentSpeed : 0.0;
for( i=bytesUsed=0; i<peerCount; ++i ) #ifdef DEBUG_DIRECTION
bytesUsed += tr_peerIoGetBandwidthUsed( peers[i]->io, direction ); if( direction == DEBUG_DIRECTION )
fprintf( stderr, "currentSpeed(%5.2f) desiredSpeed(%5.2f) - k[%.1f] merit [%.1f] welfare [%.1f]\n", currentSpeed, desiredSpeed, bytes/1024.0, meritBytes/1024.0, welfareBytes/1024.0 );
#endif
welfare = welfareBytes / peerCount; /* how much welfare each peer gets */
welfare = welfareBytes / peerCount;
for( i=0; i<peerCount; ++i ) for( i=0; i<peerCount; ++i )
{ {
tr_peer * peer = peers[i]; tr_peer * peer = peers[i];
const double merit = bytesUsed const size_t merit = tr_rcRate( peers[i]->pieceSpeed[direction] ) * meritMultiplier;
? ( meritBytes * tr_peerIoGetBandwidthUsed( peer->io, direction ) ) / bytesUsed tr_peerIoAllocateBandwidth( peer->io, direction, merit + welfare );
: ( meritBytes / peerCount );
tr_peerIoSetBandwidth( peer->io, direction, merit + welfare );
} }
} }
static size_t
countHandshakeBandwidth( tr_ptrArray * handshakes,
tr_direction direction )
{
const int n = tr_ptrArraySize( handshakes );
int i;
size_t total;
for( i = total = 0; i < n; ++i )
{
tr_peerIo * io = tr_handshakeGetIO( tr_ptrArrayNth( handshakes, i ) );
total += tr_peerIoGetBandwidthUsed( io, direction );
}
return total;
}
static size_t
countPeerBandwidth( tr_ptrArray * peers,
tr_direction direction )
{
const int n = tr_ptrArraySize( peers );
int i;
size_t total;
for( i = total = 0; i < n; ++i )
{
tr_peer * peer = tr_ptrArrayNth( peers, i );
total += tr_peerIoGetBandwidthUsed( peer->io, direction );
}
return total;
}
static void static void
givePeersUnlimitedBandwidth( tr_ptrArray * peers, givePeersUnlimitedBandwidth( tr_ptrArray * peers,
tr_direction direction ) tr_direction direction )
@ -2499,21 +2496,34 @@ pumpAllPeers( tr_peerMgr * mgr )
static void static void
getBandwidthPeers( Torrent * t, getBandwidthPeers( Torrent * t,
tr_direction direction, tr_direction dir,
tr_ptrArray * appendme ) tr_ptrArray * appendme,
double * speed )
{ {
int i, peerCount; int i, peerCount;
tr_peer ** peers; tr_peer ** peers;
assert( t );
assert( torrentIsLocked( t ) ); assert( torrentIsLocked( t ) );
assert( dir == TR_UP || dir == TR_DOWN );
assert( appendme );
assert( speed );
peers = (tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount ); peers = (tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount );
for( i=0; i<peerCount; ++i ) for( i=0; i<peerCount; ++i )
if( peers[i]->msgs ) {
if( ( ( direction == TR_PEER_TO_CLIENT ) && clientIsDownloadingFrom( peers[i] ) ) tr_peer * p = peers[i];
|| ( ( direction == TR_CLIENT_TO_PEER ) && clientIsUploadingTo( peers[i] ) ) )
tr_ptrArrayAppend( appendme, peers[i] ); if( p->msgs )
{
if( ( ( dir == TR_DOWN ) && clientIsDownloadingFrom( p ) ) || ( ( dir == TR_UP ) && clientIsUploadingTo( p ) ) )
{
tr_ptrArrayAppend( appendme, p );
*speed += tr_rcRate( p->pieceSpeed[dir] );
}
}
}
} }
/** /**
@ -2523,7 +2533,7 @@ getBandwidthPeers( Torrent * t,
* @param direction whether to allocate upload or download bandwidth * @param direction whether to allocate upload or download bandwidth
* @return the amount of directional bandwidth used since the last pulse. * @return the amount of directional bandwidth used since the last pulse.
*/ */
static double static void
allocateBandwidth( tr_peerMgr * mgr, allocateBandwidth( tr_peerMgr * mgr,
tr_direction direction ) tr_direction direction )
{ {
@ -2531,8 +2541,7 @@ allocateBandwidth( tr_peerMgr * mgr,
const int torrentCount = tr_ptrArraySize( mgr->torrents ); const int torrentCount = tr_ptrArraySize( mgr->torrents );
Torrent ** torrents = (Torrent **) tr_ptrArrayBase( mgr->torrents ); Torrent ** torrents = (Torrent **) tr_ptrArrayBase( mgr->torrents );
tr_ptrArray * globalPool = tr_ptrArrayNew( ); tr_ptrArray * globalPool = tr_ptrArrayNew( );
double allBytesUsed = 0; double globalPoolSpeed = 0;
size_t poolBytesUsed = 0;
int i; int i;
assert( mgr ); assert( mgr );
@ -2544,22 +2553,12 @@ allocateBandwidth( tr_peerMgr * mgr,
for( i=0; i<torrentCount; ++i ) for( i=0; i<torrentCount; ++i )
{ {
Torrent * t = torrents[i]; Torrent * t = torrents[i];
size_t used;
tr_speedlimit speedMode; tr_speedlimit speedMode;
/* no point in allocating bandwidth for stopped torrents */ /* no point in allocating bandwidth for stopped torrents */
if( tr_torrentGetActivity( t->tor ) == TR_STATUS_STOPPED ) if( tr_torrentGetActivity( t->tor ) == TR_STATUS_STOPPED )
continue; continue;
used = countPeerBandwidth( t->peers, direction );
countHandshakeBandwidth( t->outgoingHandshakes, direction );
/* remember this torrent's bytes used */
tr_rcTransferred( t->tor->rawSpeed[direction], used );
/* add this torrent's bandwidth use to allBytesUsed */
allBytesUsed += used;
/* if piece data is disallowed, don't bother limiting bandwidth -- /* if piece data is disallowed, don't bother limiting bandwidth --
* we won't be asking for, or sending out, any pieces */ * we won't be asking for, or sending out, any pieces */
if( !tr_torrentIsPieceTransferAllowed( t->tor, direction ) ) if( !tr_torrentIsPieceTransferAllowed( t->tor, direction ) )
@ -2574,37 +2573,26 @@ allocateBandwidth( tr_peerMgr * mgr,
givePeersUnlimitedBandwidth( t->peers, direction ); givePeersUnlimitedBandwidth( t->peers, direction );
break; break;
case TR_SPEEDLIMIT_SINGLE: case TR_SPEEDLIMIT_SINGLE: {
{
tr_ptrArray * peers = tr_ptrArrayNew( ); tr_ptrArray * peers = tr_ptrArrayNew( );
getBandwidthPeers( t, direction, peers ); double speed = 0;
setPeerBandwidth( peers, direction, getBandwidthPeers( t, direction, peers, &speed );
t->tor->rawSpeed[direction], setPeerBandwidth( peers, direction, speed, tr_torrentGetSpeedLimit( t->tor, direction ) );
tr_torrentGetSpeedLimit( t->tor, direction ) );
tr_ptrArrayFree( peers, NULL ); tr_ptrArrayFree( peers, NULL );
break; break;
} }
case TR_SPEEDLIMIT_GLOBAL: case TR_SPEEDLIMIT_GLOBAL:
getBandwidthPeers( t, direction, globalPool ); getBandwidthPeers( t, direction, globalPool, &globalPoolSpeed );
poolBytesUsed += used;
break; break;
} }
} }
/* add incoming handshakes to the global pool */
i = countHandshakeBandwidth( mgr->incomingHandshakes, direction );
allBytesUsed += i;
poolBytesUsed += i;
tr_rcTransferred( mgr->globalPoolRawSpeed[direction], poolBytesUsed );
/* handle the global pool's connections */ /* handle the global pool's connections */
if( !tr_sessionIsSpeedLimitEnabled( session, direction ) ) if( !tr_sessionIsSpeedLimitEnabled( session, direction ) )
givePeersUnlimitedBandwidth( globalPool, direction ); givePeersUnlimitedBandwidth( globalPool, direction );
else else
setPeerBandwidth( globalPool, direction, setPeerBandwidth( globalPool, direction, globalPoolSpeed,
mgr->globalPoolRawSpeed[direction],
tr_sessionGetSpeedLimit( session, direction ) ); tr_sessionGetSpeedLimit( session, direction ) );
/* now that we've allocated bandwidth, pump all the connected peers */ /* now that we've allocated bandwidth, pump all the connected peers */
@ -2612,7 +2600,6 @@ allocateBandwidth( tr_peerMgr * mgr,
/* cleanup */ /* cleanup */
tr_ptrArrayFree( globalPool, NULL ); tr_ptrArrayFree( globalPool, NULL );
return allBytesUsed;
} }
static int static int