From 9a81cd7637be6637c16fb7306583d6a254d009a4 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sun, 18 Nov 2007 01:00:49 +0000 Subject: [PATCH] reintroduce the "SWIFT" algorithm --- gtk/torrent-inspector.c | 5 +- libtransmission/clients.c | 4 +- libtransmission/peer-mgr-private.h | 23 ++++++++ libtransmission/peer-mgr.c | 94 +++++++++++++++++++++++++++++- libtransmission/peer-msgs.c | 27 ++++++--- libtransmission/ratecontrol.c | 4 +- libtransmission/transmission.h | 3 +- 7 files changed, 143 insertions(+), 17 deletions(-) diff --git a/gtk/torrent-inspector.c b/gtk/torrent-inspector.c index 302783e22..d4059f015 100644 --- a/gtk/torrent-inspector.c +++ b/gtk/torrent-inspector.c @@ -36,7 +36,7 @@ #include "torrent-inspector.h" #include "util.h" -#define UPDATE_INTERVAL_MSEC 1500 +#define UPDATE_INTERVAL_MSEC 2000 /**** ***** PIECES VIEW @@ -361,10 +361,11 @@ render_status( GtkTreeViewColumn * column UNUSED, case TR_PEER_STATUS_HANDSHAKE: text = _( "Handshaking" ); break; case TR_PEER_STATUS_PEER_IS_CHOKED: text = _( "Peer is Choked" ); break; case TR_PEER_STATUS_CLIENT_IS_CHOKED: text = _( "Choked" ); break; - case TR_PEER_STATUS_CLIENT_IS_INTERESTED: text = _( "Choked and Interested" ); break; + case TR_PEER_STATUS_CLIENT_IS_INTERESTED: text = _( "Choked & Interested" ); break; case TR_PEER_STATUS_READY: text = _( "Ready" ); break; case TR_PEER_STATUS_REQUEST_SENT: text = _( "Request Sent" ); break; case TR_PEER_STATUS_ACTIVE : text = _( "Active" ); break; + case TR_PEER_STATUS_ACTIVE_AND_CHOKED: text = _( "Active & Choked" ); break; default: text = "BUG"; break; } g_object_set (renderer, "text", text, NULL); diff --git a/libtransmission/clients.c b/libtransmission/clients.c index 7585a84e0..4f16784f4 100644 --- a/libtransmission/clients.c +++ b/libtransmission/clients.c @@ -408,12 +408,12 @@ char * tr_clientForId( const uint8_t * id ) isprint( id[3] ) && isprint( id[4] ) && isprint( id[5] ) && isprint( id[6] ) && isprint( id[7] ) ) { - tr_asprintf( &ret, "unknown client (%c%c%c%c%c%c%c%c)", + tr_asprintf( &ret, "%c%c%c%c%c%c%c%c", id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7] ); } else { - tr_asprintf( &ret, "unknown client (0x%02x%02x%02x%02x%02x%02x%02x%02x)", + tr_asprintf( &ret, "0x%02x%02x%02x%02x%02x%02x%02x%02x", id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7] ); } } diff --git a/libtransmission/peer-mgr-private.h b/libtransmission/peer-mgr-private.h index 71a98133e..ba3fd419f 100644 --- a/libtransmission/peer-mgr-private.h +++ b/libtransmission/peer-mgr-private.h @@ -28,6 +28,27 @@ enum ENCRYPTION_PREFERENCE_NO }; +/** +*** The "SWIFT" system is described by Karthik Tamilmani, +*** Vinay Pai, and Alexander Mohr of Stony Brook University +*** in their paper "SWIFT: A System With Incentives For Trading" +*** http://citeseer.ist.psu.edu/tamilmani04swift.html +*** +*** More SWIFT constants are defined in peer-mgr.c +**/ + +/** + * Use SWIFT? + */ +static const int SWIFT_ENABLED = 1; + +/** + * For every byte the peer uploads to us, + * allow them to download this many bytes from us + */ +static const double SWIFT_REPAYMENT_RATIO = 1.33; + + typedef struct tr_peer { unsigned int peerIsChoked : 1; @@ -66,6 +87,8 @@ typedef struct tr_peer double rateToClient; double rateToPeer; + + int64_t credit; } tr_peer; diff --git a/libtransmission/peer-mgr.c b/libtransmission/peer-mgr.c index 77aa15159..c16944279 100644 --- a/libtransmission/peer-mgr.c +++ b/libtransmission/peer-mgr.c @@ -39,6 +39,35 @@ #include "trevent.h" #include "utils.h" +/** +*** The "SWIFT" system is described by Karthik Tamilmani, +*** Vinay Pai, and Alexander Mohr of Stony Brook University +*** in their paper "SWIFT: A System With Incentives For Trading" +*** http://citeseer.ist.psu.edu/tamilmani04swift.html +*** +*** More SWIFT constants are defined in peer-mgr-private.h +**/ + +/** + * Allow new peers to download this many bytes from + * us when getting started. This can prevent gridlock + * with other peers using tit-for-tat algorithms + */ +static const int SWIFT_INITIAL_CREDIT = 64 * 1024; /* 64 KiB */ + +/** + * We expend a fraction of our torrent's total upload speed + * on largesse by uniformly distributing free credit to + * all of our peers. This too helps prevent gridlock. + */ +static const double SWIFT_LARGESSE = 0.10; /* 10% of our UL */ + +/** + * How frequently to extend largesse-based credit + */ +static const int SWIFT_PERIOD_MSEC = 5000; + + enum { /* how frequently to change which peers are choked */ @@ -114,6 +143,7 @@ typedef struct tr_timer * reconnectTimer; tr_timer * rechokeTimer; tr_timer * refillTimer; + tr_timer * swiftTimer; tr_torrent * tor; tr_bitfield * requested; @@ -314,6 +344,7 @@ peerConstructor( const struct in_addr * in_addr ) { tr_peer * p; p = tr_new0( tr_peer, 1 ); + p->credit = SWIFT_INITIAL_CREDIT; p->rcToClient = tr_rcInit( ); p->rcToPeer = tr_rcInit( ); memcpy( &p->in_addr, in_addr, sizeof(struct in_addr) ); @@ -397,6 +428,7 @@ torrentDestructor( Torrent * t ) tr_timerFree( &t->reconnectTimer ); tr_timerFree( &t->rechokeTimer ); tr_timerFree( &t->refillTimer ); + tr_timerFree( &t->swiftTimer ); tr_bitfieldFree( t->requested ); tr_ptrArrayFree( t->pool, (PtrArrayForeachFunc)tr_free ); @@ -1167,6 +1199,7 @@ tr_peerMgrGetPeers( tr_peerMgr * manager, static int reconnectPulse( void * vtorrent ); static int rechokePulse( void * vtorrent ); +static int swiftPulse( void * vtorrent ); void tr_peerMgrStartTorrent( tr_peerMgr * manager, @@ -1181,6 +1214,7 @@ tr_peerMgrStartTorrent( tr_peerMgr * manager, assert( t != NULL ); assert( ( t->isRunning != 0 ) == ( t->reconnectTimer != NULL ) ); assert( ( t->isRunning != 0 ) == ( t->rechokeTimer != NULL ) ); + assert( ( t->isRunning != 0 ) == ( t->swiftTimer != NULL ) ); if( !t->isRunning ) { @@ -1194,9 +1228,15 @@ tr_peerMgrStartTorrent( tr_peerMgr * manager, rechokePulse, t, RECHOKE_PERIOD_MSEC ); + t->swiftTimer = tr_timerNew( t->manager->handle, + swiftPulse, t, + SWIFT_PERIOD_MSEC ); + reconnectPulse( t ); rechokePulse( t ); + + swiftPulse( t ); } managerUnlock( manager ); @@ -1210,6 +1250,7 @@ stopTorrent( Torrent * t ) t->isRunning = 0; tr_timerFree( &t->rechokeTimer ); tr_timerFree( &t->reconnectTimer ); + tr_timerFree( &t->swiftTimer ); /* disconnect the peers. */ tr_ptrArrayForeach( t->peers, (PtrArrayForeachFunc)peerDestructor ); @@ -1477,8 +1518,8 @@ static double getWeightedThroughput( const tr_peer * peer ) { /* FIXME: tweak this? */ - return ( 1 * peer->rateToPeer ) - + ( 1 * peer->rateToClient ); + return /* 1 * peer->rateToPeer ) + +*/ ( 1 * peer->rateToClient ); } static void @@ -1534,6 +1575,55 @@ rechokePulse( void * vtorrent ) return TRUE; } +/*** +**** +***/ + +static int +swiftPulse( void * vtorrent ) +{ + Torrent * t = vtorrent; + torrentLock( t ); + + if( !tr_torrentIsSeed( t->tor ) ) + { + int i; + int peerCount = 0; + int deadbeatCount = 0; + tr_peer ** peers = getConnectedPeers( t, &peerCount ); + tr_peer ** deadbeats = tr_new( tr_peer*, peerCount ); + + for( i=0; icredit < 0 ) + deadbeats[deadbeatCount++] = peer; + } + + if( deadbeatCount ) + { + const double ul_KiBsec = tr_rcRate( t->tor->upload ); + const double ul_KiB = ul_KiBsec * (SWIFT_PERIOD_MSEC/1000.0); + const double ul_bytes = ul_KiB * 1024; + const double freeCreditTotal = ul_bytes * SWIFT_LARGESSE; + const int freeCreditPerPeer = (int)( freeCreditTotal / deadbeatCount ); + for( i=0; icredit = freeCreditPerPeer; + tordbg( t, "%d deadbeats, " + "who are each being granted %d bytes' credit " + "for a total of %.1f KiB, " + "%d%% of the torrent's ul speed %.1f\n", + deadbeatCount, freeCreditPerPeer, + ul_KiBsec*SWIFT_LARGESSE, (int)(SWIFT_LARGESSE*100), ul_KiBsec ); + } + + tr_free( deadbeats ); + tr_free( peers ); + } + + torrentUnlock( t ); + return TRUE; +} + /*** **** **** Life and Death diff --git a/libtransmission/peer-msgs.c b/libtransmission/peer-msgs.c index 4c4586f9c..a9641d2c7 100644 --- a/libtransmission/peer-msgs.c +++ b/libtransmission/peer-msgs.c @@ -1034,6 +1034,7 @@ clientGotBytes( tr_peermsgs * msgs, uint32_t byteCount ) tor->activityDate = tr_date( ); tor->downloadedCur += byteCount; msgs->info->pieceDataActivityDate = time( NULL ); + msgs->info->credit += (int)(byteCount * SWIFT_REPAYMENT_RATIO); tr_rcTransferred( msgs->info->rcToClient, byteCount ); tr_rcTransferred( tor->download, byteCount ); tr_rcTransferred( tor->handle->download, byteCount ); @@ -1255,6 +1256,7 @@ peerGotBytes( tr_peermsgs * msgs, uint32_t byteCount ) tor->activityDate = tr_date( ); tor->uploadedCur += byteCount; msgs->info->pieceDataActivityDate = time( NULL ); + msgs->info->credit -= byteCount; tr_rcTransferred( msgs->info->rcToPeer, byteCount ); tr_rcTransferred( tor->upload, byteCount ); tr_rcTransferred( tor->handle->upload, byteCount ); @@ -1440,7 +1442,15 @@ static int canWrite( const tr_peermsgs * msgs ) { /* don't let our outbuffer get too large */ - return tr_peerIoWriteBytesWaiting( msgs->io ) < 4096; + if( tr_peerIoWriteBytesWaiting( msgs->io ) > 4096 ) + return FALSE; + + /* SWIFT */ + if( SWIFT_ENABLED && !tr_torrentIsSeed( msgs->torrent ) + && ( msgs->info->credit < 0 ) ) + return FALSE; + + return TRUE; } static size_t @@ -1492,20 +1502,21 @@ updatePeerStatus( tr_peermsgs * msgs ) peer->status = TR_PEER_STATUS_HANDSHAKE; else if( ( time(NULL) - peer->pieceDataActivityDate ) < 3 ) - peer->status = TR_PEER_STATUS_ACTIVE; - - else if( peer->clientIsChoked ) - peer->status = TR_PEER_STATUS_CLIENT_IS_CHOKED; + peer->status = peer->clientIsChoked + ? TR_PEER_STATUS_ACTIVE_AND_CHOKED + : TR_PEER_STATUS_ACTIVE; else if( peer->peerIsChoked ) peer->status = TR_PEER_STATUS_PEER_IS_CHOKED; + else if( peer->clientIsChoked ) + peer->status = peer->clientIsInterested + ? TR_PEER_STATUS_CLIENT_IS_INTERESTED + : TR_PEER_STATUS_CLIENT_IS_CHOKED; + else if( msgs->clientAskedFor != NULL ) peer->status = TR_PEER_STATUS_REQUEST_SENT; - else if( peer->clientIsInterested ) - peer->status = TR_PEER_STATUS_CLIENT_IS_INTERESTED; - else peer->status = TR_PEER_STATUS_READY; } diff --git a/libtransmission/ratecontrol.c b/libtransmission/ratecontrol.c index e4ca70bc2..a89ee037f 100644 --- a/libtransmission/ratecontrol.c +++ b/libtransmission/ratecontrol.c @@ -29,7 +29,7 @@ #include "ratecontrol.h" #include "utils.h" -#define GRANULARITY_MSEC 100 +#define GRANULARITY_MSEC 250 #define SHORT_INTERVAL_MSEC 3000 #define LONG_INTERVAL_MSEC 6000 #define HISTORY_SIZE (LONG_INTERVAL_MSEC / GRANULARITY_MSEC) @@ -108,7 +108,7 @@ tr_rcBytesLeft( const tr_ratecontrol * r ) cur = rateForInterval( r, SHORT_INTERVAL_MSEC ); max = r->limit; kb = max>cur ? max-cur : 0; - bytes = (size_t)(kb * 1024u); + bytes = (size_t)(kb * 1024); tr_lockUnlock( (tr_lock*)r->lock ); } diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index 22e4d0d0e..f8e65fc7f 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -691,7 +691,8 @@ typedef enum TR_PEER_STATUS_CLIENT_IS_INTERESTED, TR_PEER_STATUS_READY, TR_PEER_STATUS_REQUEST_SENT, - TR_PEER_STATUS_ACTIVE + TR_PEER_STATUS_ACTIVE, + TR_PEER_STATUS_ACTIVE_AND_CHOKED } tr_peer_status;