merged unchoke algorithm fix from RC_0_16

This commit is contained in:
Arvid Norberg 2013-06-01 22:33:11 +00:00
parent c464dc2383
commit 93993002ab
5 changed files with 138 additions and 32 deletions

View File

@ -21,6 +21,7 @@
* fix uTP edge case where udp socket buffer fills up
* fix nagle implementation in uTP
* fix round-robin seed-unchoke algorithm
* add bootstrap.sh to generage configure script and run configure
* fix bug in SOCK5 UDP support
* fix issue where torrents added by URL would not be started immediately

View File

@ -623,11 +623,14 @@ namespace libtorrent
// enum from peer_info::bw_state
char m_channel_state[2];
size_type uploaded_since_unchoke() const
{ return m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke; }
size_type uploaded_in_last_round() const
{ return m_statistics.total_payload_upload() - m_uploaded_at_last_round; }
size_type downloaded_since_unchoke() const
{ return m_statistics.total_payload_download() - m_downloaded_at_last_unchoke; }
size_type downloaded_in_last_round() const
{ return m_statistics.total_payload_download() - m_downloaded_at_last_round; }
size_type uploaded_since_unchoked() const
{ return m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke; }
// called when the disk write buffer is drained again, and we can
// start downloading payload again
@ -847,11 +850,19 @@ namespace libtorrent
size_type m_free_upload;
// the total payload download bytes
// at the last unchoke cycle. This is used to
// at the last unchoke round. This is used to
// measure the number of bytes transferred during
// an unchoke cycle, to unchoke peers the more bytes
// they sent us
size_type m_downloaded_at_last_unchoke;
size_type m_downloaded_at_last_round;
size_type m_uploaded_at_last_round;
// this is the number of bytes we had uploaded the
// last time this peer was unchoked. This does not
// reset each unchoke interval/round. This is used to
// track upload across rounds, for the full duration of
// the peer being unchoked. Specifically, it's used
// for the round-robin unchoke algorithm.
size_type m_uploaded_at_last_unchoke;
#ifndef TORRENT_DISABLE_GEO_IP

71
parse_peer_log.py Normal file
View File

@ -0,0 +1,71 @@
#!/usr/bin/env python
import glob
import os
import sys
# usage: parse_peer_log <path-to-libtorrent-peer-logs>
log_files = []
for p in glob.iglob(os.path.join(sys.argv[1], '*.log')):
name = os.path.split(p)[1]
if name == 'main_session.log': continue
print name
f = open(p, 'r')
out_file = p + '.dat'
log_files.append(out_file)
out = open(out_file, 'w+')
uploaded_blocks = 0;
downloaded_blocks = 0;
for l in f:
t = l.split(': ')[0].split('.')[0]
log_line = False
if ' ==> PIECE' in l:
uploaded_blocks+= 1
log_line = True
if ' <== PIECE' in l:
downloaded_blocks+= 1
log_line = True
if log_line:
print >>out, '%s\t%d\t%d' % (t, uploaded_blocks, downloaded_blocks)
out.close()
f.close()
out = open('peers.gnuplot', 'wb')
print >>out, "set term png size 1200,700"
print >>out, 'set xrange [0:*]'
print >>out, 'set xlabel "time"'
print >>out, 'set ylabel "blocks"'
print >>out, 'set key box'
print >>out, 'set xdata time'
print >>out, 'set timefmt "%H:%M:%S"'
print >>out, 'set title "uploaded blocks"'
print >>out, 'set output "peers_upload.png"'
print >>out, 'plot',
first = True
for n in log_files:
if not first:
print >>out, ',',
first = False
print >>out, ' "%s" using 1:2 title "%s" with steps' % (n, os.path.split(n)[1].split('.log')[0]),
print >>out, ''
print >>out, 'set title "downloaded blocks"'
print >>out, 'set output "peers_download.png"'
print >>out, 'plot',
first = True
for n in log_files:
if not first:
print >>out, ',',
first = False
print >>out, ' "%s" using 1:3 title "%s" with steps' % (n, os.path.split(n)[1].split('.log')[0]),
print >>out, ''
out.close()
os.system('gnuplot peers.gnuplot');

View File

@ -129,7 +129,8 @@ namespace libtorrent
, m_became_uninterested(time_now())
, m_became_uninteresting(time_now())
, m_free_upload(0)
, m_downloaded_at_last_unchoke(0)
, m_downloaded_at_last_round(0)
, m_uploaded_at_last_round(0)
, m_uploaded_at_last_unchoke(0)
, m_disk_recv_buffer(ses, 0)
, m_socket(s)
@ -294,11 +295,11 @@ namespace libtorrent
size_type d1, d2, u1, u2;
// first compare how many bytes they've sent us
d1 = m_statistics.total_payload_download() - m_downloaded_at_last_unchoke;
d2 = rhs.m_statistics.total_payload_download() - rhs.m_downloaded_at_last_unchoke;
d1 = downloaded_in_last_round();
d2 = rhs.downloaded_in_last_round();
// divided by the number of bytes we've sent them
u1 = m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke;
u2 = rhs.m_statistics.total_payload_upload() - rhs.m_uploaded_at_last_unchoke;
u1 = uploaded_in_last_round();
u2 = rhs.uploaded_in_last_round();
boost::shared_ptr<torrent> t1 = m_torrent.lock();
TORRENT_ASSERT(t1);
@ -338,31 +339,49 @@ namespace libtorrent
// compare how many bytes they've sent us
size_type c1;
size_type c2;
c1 = m_statistics.total_payload_download() - m_downloaded_at_last_unchoke;
c2 = rhs.m_statistics.total_payload_download() - rhs.m_downloaded_at_last_unchoke;
c1 = downloaded_in_last_round();
c2 = rhs.downloaded_in_last_round();
if (c1 != c2) return c1 > c2;
if (m_ses.settings().seed_choking_algorithm == session_settings::round_robin)
{
// if they are equal, compare how much we have uploaded
c1 = m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke;
c2 = rhs.m_statistics.total_payload_upload() - rhs.m_uploaded_at_last_unchoke;
// the amount uploaded since unchoked (not just in the last round)
c1 = uploaded_since_unchoked();
c2 = rhs.uploaded_since_unchoked();
// the way the round-robin unchoker works is that it,
// by default, prioritizes any peer that is already unchoked.
// this maintain the status quo across unchoke rounds. However,
// peers that are unchoked, but have sent more than one quota
// since they were unchoked, they get de-prioritized.
// in order to not switch back and forth too often,
// unchoked peers must be at least one piece ahead
// of a choked peer to be sorted at a lower unchoke-priority
int pieces = m_ses.settings().seeding_piece_quota;
bool c1_done = is_choked() || c1 > (std::max)(t1->torrent_file().piece_length() * pieces, 256 * 1024);
bool c2_done = rhs.is_choked() || c2 > (std::max)(t2->torrent_file().piece_length() * pieces, 256 * 1024);
// if a peer is already unchoked, and the number of bytes sent since it was unchoked
// is greater than the send quanta, then it's done with it' upload slot, and we
// can de-prioritize it
bool c1_quota_complete = !is_choked() && c1 > (std::max)(t1->torrent_file().piece_length() * pieces, 256 * 1024);
bool c2_quota_complete = !rhs.is_choked() && c2 > (std::max)(t2->torrent_file().piece_length() * pieces, 256 * 1024);
if (!c1_done && c2_done) return true;
if (c1_done && !c2_done) return false;
// if c2 has completed a quanta, it shuold be de-prioritized
// and vice versa
if (c1_quota_complete < c2_quota_complete) return true;
if (c1_quota_complete > c2_quota_complete) return false;
// if both peers have either completed a quanta, or not.
// keep unchoked peers prioritized over choked ones, to let
// peers keep working on uploading a full quanta
if (is_choked() < rhs.is_choked()) return true;
if (is_choked() > rhs.is_choked()) return false;
// if the peers are still identical (say, they're both waiting to be unchoked)
// fall through and rely on the logic to prioritize peers who have waited
// the longest to be unchoked
}
else if (m_ses.settings().seed_choking_algorithm == session_settings::fastest_upload)
{
c1 = m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke;
c2 = rhs.m_statistics.total_payload_upload() - rhs.m_uploaded_at_last_unchoke;
c1 = uploaded_in_last_round();
c2 = rhs.uploaded_in_last_round();
// take torrent priority into account
c1 *= 1 + t1->priority();
@ -403,6 +422,8 @@ namespace libtorrent
}
// prioritize the one that has waited the longest to be unchoked
// the round-robin unchoker relies on this logic. Don't change it
// without moving this into that unchoker logic
return m_last_unchoke < rhs.m_last_unchoke;
}
@ -416,8 +437,8 @@ namespace libtorrent
boost::shared_ptr<torrent> t2 = p->associated_torrent().lock();
TORRENT_ASSERT(t2);
c1 = m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke;
c2 = p->m_statistics.total_payload_upload() - p->m_uploaded_at_last_unchoke;
c1 = uploaded_in_last_round();
c2 = p->uploaded_in_last_round();
// take torrent priority into account
c1 *= 1 + t1->priority();
@ -428,8 +449,8 @@ namespace libtorrent
void peer_connection::reset_choke_counters()
{
m_downloaded_at_last_unchoke = m_statistics.total_payload_download();
m_uploaded_at_last_unchoke = m_statistics.total_payload_upload();
m_downloaded_at_last_round= m_statistics.total_payload_download();
m_uploaded_at_last_round = m_statistics.total_payload_upload();
}
void peer_connection::start()
@ -3096,6 +3117,8 @@ namespace libtorrent
write_unchoke();
m_choked = false;
m_uploaded_at_last_unchoke = m_statistics.total_payload_upload();
#ifdef TORRENT_VERBOSE_LOGGING
peer_log("==> UNCHOKE");
#endif

View File

@ -4599,9 +4599,9 @@ retry:
TORRENT_ASSERT(t1);
boost::shared_ptr<torrent> t2 = (*i)->associated_torrent().lock();
TORRENT_ASSERT(t2);
TORRENT_ASSERT((*prev)->uploaded_since_unchoke() * 1000
TORRENT_ASSERT((*prev)->uploaded_in_last_round() * 1000
* (1 + t1->priority()) / total_milliseconds(unchoke_interval)
>= (*i)->uploaded_since_unchoke() * 1000
>= (*i)->uploaded_in_last_round() * 1000
* (1 + t2->priority()) / total_milliseconds(unchoke_interval));
}
prev = i;
@ -4615,7 +4615,7 @@ retry:
, end(peers.end()); i != end; ++i)
{
peer_connection const& p = **i;
int rate = int(p.uploaded_since_unchoke()
int rate = int(p.uploaded_in_last_round()
* 1000 / total_milliseconds(unchoke_interval));
if (rate < rate_threshold) break;