factored out choke comparison functions into choker.cpp

This commit is contained in:
Arvid Norberg 2014-10-22 22:30:04 +00:00
parent 02a9ea18b7
commit e09a2b770d
3 changed files with 181 additions and 183 deletions

View File

@ -554,13 +554,10 @@ namespace libtorrent
return (m_endgame_mode || m_snubbed) ? 1 : m_desired_queue_size; return (m_endgame_mode || m_snubbed) ? 1 : m_desired_queue_size;
} }
bool bittyrant_unchoke_compare(
peer_connection const* p) const;
// compares this connection against the given connection // compares this connection against the given connection
// for which one is more eligible for an unchoke. // for which one is more eligible for an unchoke.
// returns true if this is more eligible // returns true if this is more eligible
bool unchoke_compare(peer_connection const* p) const;
bool upload_rate_compare(peer_connection const* p) const;
int download_payload_rate() const { return m_statistics.download_payload_rate(); } int download_payload_rate() const { return m_statistics.download_payload_rate(); }
// resets the byte counters that are used to measure // resets the byte counters that are used to measure
@ -750,6 +747,10 @@ namespace libtorrent
size_type uploaded_since_unchoked() const size_type uploaded_since_unchoked() const
{ return m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke; } { return m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke; }
// the time we last unchoked this peer
ptime time_of_last_unchoke() const
{ return m_last_unchoke; }
// called when the disk write buffer is drained again, and we can // called when the disk write buffer is drained again, and we can
// start downloading payload again // start downloading payload again
void on_disk(); void on_disk();
@ -769,6 +770,8 @@ namespace libtorrent
counters& stats_counters() const { return m_counters; } counters& stats_counters() const { return m_counters; }
int get_priority(int channel) const;
protected: protected:
size_t try_read(sync_t s, error_code& ec); size_t try_read(sync_t s, error_code& ec);
@ -878,8 +881,6 @@ namespace libtorrent
int wanted_transfer(int channel); int wanted_transfer(int channel);
int request_bandwidth(int channel, int bytes = 0); int request_bandwidth(int channel, int bytes = 0);
int get_priority(int channel) const;
boost::shared_ptr<socket_type> m_socket; boost::shared_ptr<socket_type> m_socket;
// the queue of blocks we have requested // the queue of blocks we have requested
@ -981,6 +982,7 @@ namespace libtorrent
// the time we received the last // the time we received the last
// piece request from the peer // piece request from the peer
ptime m_last_incoming_request; ptime m_last_incoming_request;
// the time when we unchoked this peer // the time when we unchoked this peer
ptime m_last_unchoke; ptime m_last_unchoke;

View File

@ -33,11 +33,173 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/choker.hpp" #include "libtorrent/choker.hpp"
#include "libtorrent/peer_connection.hpp" #include "libtorrent/peer_connection.hpp"
#include "libtorrent/aux_/session_settings.hpp" #include "libtorrent/aux_/session_settings.hpp"
#include "libtorrent/torrent.hpp"
#include <boost/bind.hpp> #include <boost/bind.hpp>
namespace libtorrent namespace libtorrent
{ {
// return true if 'lhs' peer should be preferred to be unchoke over 'rhs'
// TODO: 3 split this funcion up into multiple functions, one for each
// seed_choking_algorithm, and pick which one to use when calling
// the sort function
bool unchoke_compare(peer_connection const* lhs
, peer_connection const* rhs, aux::session_settings const& sett)
{
// if one peer belongs to a higher priority torrent than the other one
// that one should be unchoked.
boost::shared_ptr<torrent> t1 = lhs->associated_torrent().lock();
TORRENT_ASSERT(t1);
boost::shared_ptr<torrent> t2 = rhs->associated_torrent().lock();
TORRENT_ASSERT(t2);
int prio1 = lhs->get_priority(peer_connection::upload_channel);
int prio2 = rhs->get_priority(peer_connection::upload_channel);
if (prio1 != prio2)
return prio1 > prio2;
// compare how many bytes they've sent us
size_type c1;
size_type c2;
c1 = lhs->downloaded_in_last_round();
c2 = rhs->downloaded_in_last_round();
if (c1 != c2) return c1 > c2;
if (sett.get_int(settings_pack::seed_choking_algorithm)
== settings_pack::round_robin)
{
// the amount uploaded since unchoked (not just in the last round)
c1 = lhs->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.
int pieces = sett.get_int(settings_pack::seeding_piece_quota);
// 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 = !lhs->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 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 (lhs->is_choked() < rhs->is_choked()) return true;
if (lhs->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 (sett.get_int(settings_pack::seed_choking_algorithm)
== settings_pack::fastest_upload)
{
c1 = lhs->uploaded_in_last_round();
c2 = rhs->uploaded_in_last_round();
// take torrent priority into account
c1 *= prio1;
c2 *= prio2;
if (c1 > c2) return true;
if (c2 > c1) return false;
}
else if (sett.get_int(settings_pack::seed_choking_algorithm)
== settings_pack::anti_leech)
{
// the anti-leech seeding algorithm is based on the paper "Improving
// BitTorrent: A Simple Approach" from Chow et. al. and ranks peers based
// on how many pieces they have, prefering to unchoke peers that just
// started and peers that are close to completing. Like this:
// ^
// | \ / |
// | \ / |
// | \ / |
// s | \ / |
// c | \ / |
// o | \ / |
// r | \ / |
// e | \ / |
// | \ / |
// | \ / |
// | \ / |
// | \ / |
// | V |
// +---------------------------+
// 0% num have pieces 100%
int t1_total = t1->torrent_file().num_pieces();
int t2_total = t2->torrent_file().num_pieces();
int score1 = (lhs->num_have_pieces() < t1_total / 2
? t1_total - lhs->num_have_pieces() : lhs->num_have_pieces()) * 1000 / t1_total;
int score2 = (rhs->num_have_pieces() < t2_total / 2
? t2_total - rhs->num_have_pieces() : rhs->num_have_pieces()) * 1000 / t2_total;
if (score1 > score2) return true;
if (score2 > score1) return false;
}
// 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 lhs->time_of_last_unchoke() < rhs->time_of_last_unchoke();
}
bool upload_rate_compare(peer_connection const* lhs
, peer_connection const* rhs)
{
size_type c1;
size_type c2;
c1 = lhs->uploaded_in_last_round();
c2 = rhs->uploaded_in_last_round();
// take torrent priority into account
c1 *= lhs->get_priority(peer_connection::upload_channel);
c2 *= rhs->get_priority(peer_connection::upload_channel);
return c1 > c2;
}
bool bittyrant_unchoke_compare(peer_connection const* lhs
, peer_connection const* rhs)
{
size_type d1, d2, u1, u2;
// first compare how many bytes they've sent us
d1 = lhs->downloaded_in_last_round();
d2 = rhs->downloaded_in_last_round();
// divided by the number of bytes we've sent them
u1 = lhs->uploaded_in_last_round();
u2 = rhs->uploaded_in_last_round();
// take torrent priority into account
d1 *= lhs->get_priority(peer_connection::upload_channel);
d2 *= rhs->get_priority(peer_connection::upload_channel);
d1 = d1 * 1000 / (std::max)(size_type(1), u1);
d2 = d2 * 1000 / (std::max)(size_type(1), u2);
if (d1 > d2) return true;
if (d1 < d2) return false;
// if both peers are still in their send quota or not in their send quota
// prioritize the one that has waited the longest to be unchoked
return lhs->time_of_last_unchoke() < rhs->time_of_last_unchoke();
}
int unchoke_sort(std::vector<peer_connection*>& peers int unchoke_sort(std::vector<peer_connection*>& peers
, int max_upload_rate , int max_upload_rate
, time_duration unchoke_interval , time_duration unchoke_interval
@ -78,11 +240,8 @@ namespace libtorrent
// if we're using the bittyrant choker, sort peers by their return // if we're using the bittyrant choker, sort peers by their return
// on investment. i.e. download rate / upload rate // on investment. i.e. download rate / upload rate
// TODO: make the comparison function a free function and move it
// into this source file
std::sort(peers.begin(), peers.end() std::sort(peers.begin(), peers.end()
, boost::bind(&peer_connection::bittyrant_unchoke_compare, _1, _2)); , boost::bind(&bittyrant_unchoke_compare, _1, _2));
int upload_capacity_left = max_upload_rate; int upload_capacity_left = max_upload_rate;
@ -129,7 +288,7 @@ namespace libtorrent
// TODO: make the comparison function a free function and move it // TODO: make the comparison function a free function and move it
// into this cpp file // into this cpp file
std::sort(peers.begin(), peers.end() std::sort(peers.begin(), peers.end()
, boost::bind(&peer_connection::upload_rate_compare, _1, _2)); , boost::bind(&upload_rate_compare, _1, _2));
// TODO: make configurable // TODO: make configurable
int rate_threshold = 1024; int rate_threshold = 1024;
@ -151,17 +310,17 @@ namespace libtorrent
++upload_slots; ++upload_slots;
} }
// sorts the peers that are eligible for unchoke by download rate and secondary // sorts the peers that are eligible for unchoke by download rate and
// by total upload. The reason for this is, if all torrents are being seeded, // secondary by total upload. The reason for this is, if all torrents are
// the download rate will be 0, and the peers we have sent the least to should // being seeded, the download rate will be 0, and the peers we have sent
// be unchoked // the least to should be unchoked
// TODO: use partial_sort // we use partial sort here, because we only care about the top
// upload_slots peers.
// TODO: make the comparison function a free function and move it into std::partial_sort(peers.begin(), peers.begin()
// this cpp file + (std::min)(upload_slots, int(peers.size())), peers.end()
std::sort(peers.begin(), peers.end() , boost::bind(&unchoke_compare, _1, _2, boost::cref(sett)));
, boost::bind(&peer_connection::unchoke_compare, _1, _2));
return upload_slots; return upload_slots;
} }

View File

@ -294,151 +294,6 @@ namespace libtorrent
* m_settings.get_int(settings_pack::decrease_est_reciprocation_rate) / 100; * m_settings.get_int(settings_pack::decrease_est_reciprocation_rate) / 100;
} }
bool peer_connection::bittyrant_unchoke_compare(
peer_connection const* p) const
{
TORRENT_ASSERT(is_single_thread());
TORRENT_ASSERT(p);
peer_connection const& rhs = *p;
size_type d1, d2, u1, u2;
// first compare how many bytes they've sent us
d1 = downloaded_in_last_round();
d2 = rhs.downloaded_in_last_round();
// divided by the number of bytes we've sent them
u1 = uploaded_in_last_round();
u2 = rhs.uploaded_in_last_round();
// take torrent priority into account
d1 *= get_priority(upload_channel);
d2 *= rhs.get_priority(upload_channel);
d1 = d1 * 1000 / (std::max)(size_type(1), u1);
d2 = d2 * 1000 / (std::max)(size_type(1), u2);
if (d1 > d2) return true;
if (d1 < d2) return false;
// if both peers are still in their send quota or not in their send quota
// prioritize the one that has waited the longest to be unchoked
return m_last_unchoke < rhs.m_last_unchoke;
}
// return true if 'this' peer should be preferred to be unchoke over p
bool peer_connection::unchoke_compare(peer_connection const* p) const
{
TORRENT_ASSERT(is_single_thread());
TORRENT_ASSERT(p);
peer_connection const& rhs = *p;
// if one peer belongs to a higher priority torrent than the other one
// that one should be unchoked.
boost::shared_ptr<torrent> t1 = m_torrent.lock();
TORRENT_ASSERT(t1);
boost::shared_ptr<torrent> t2 = rhs.associated_torrent().lock();
TORRENT_ASSERT(t2);
int prio1 = get_priority(upload_channel);
int prio2 = rhs.get_priority(upload_channel);
if (prio1 != prio2)
return prio1 > prio2;
// compare how many bytes they've sent us
size_type c1;
size_type c2;
c1 = downloaded_in_last_round();
c2 = rhs.downloaded_in_last_round();
if (c1 != c2) return c1 > c2;
if (m_settings.get_int(settings_pack::seed_choking_algorithm)
== settings_pack::round_robin)
{
// 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.
int pieces = m_settings.get_int(settings_pack::seeding_piece_quota);
// 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 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_settings.get_int(settings_pack::seed_choking_algorithm)
== settings_pack::fastest_upload)
{
c1 = uploaded_in_last_round();
c2 = rhs.uploaded_in_last_round();
// take torrent priority into account
c1 *= prio1;
c2 *= prio2;
if (c1 > c2) return true;
if (c2 > c1) return false;
}
else if (m_settings.get_int(settings_pack::seed_choking_algorithm)
== settings_pack::anti_leech)
{
// the anti-leech seeding algorithm is based on the paper "Improving
// BitTorrent: A Simple Approach" from Chow et. al. and ranks peers based
// on how many pieces they have, prefering to unchoke peers that just
// started and peers that are close to completing. Like this:
// ^
// | \ / |
// | \ / |
// | \ / |
// s | \ / |
// c | \ / |
// o | \ / |
// r | \ / |
// e | \ / |
// | \ / |
// | \ / |
// | \ / |
// | \ / |
// | V |
// +---------------------------+
// 0% num have pieces 100%
int t1_total = t1->torrent_file().num_pieces();
int t2_total = t2->torrent_file().num_pieces();
int score1 = (num_have_pieces() < t1_total / 2
? t1_total - num_have_pieces() : num_have_pieces()) * 1000 / t1_total;
int score2 = (rhs.num_have_pieces() < t2_total / 2
? t2_total - rhs.num_have_pieces() : rhs.num_have_pieces()) * 1000 / t2_total;
if (score1 > score2) return true;
if (score2 > score1) return false;
}
// 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;
}
int peer_connection::get_priority(int channel) const int peer_connection::get_priority(int channel) const
{ {
TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(is_single_thread());
@ -463,22 +318,6 @@ namespace libtorrent
return prio; return prio;
} }
bool peer_connection::upload_rate_compare(peer_connection const* p) const
{
TORRENT_ASSERT(is_single_thread());
size_type c1;
size_type c2;
c1 = uploaded_in_last_round();
c2 = p->uploaded_in_last_round();
// take torrent priority into account
c1 *= get_priority(upload_channel);
c2 *= p->get_priority(upload_channel);
return c1 > c2;
}
void peer_connection::reset_choke_counters() void peer_connection::reset_choke_counters()
{ {
TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(is_single_thread());
@ -3909,8 +3748,6 @@ namespace libtorrent
bool empty_download_queue = m_download_queue.empty(); bool empty_download_queue = m_download_queue.empty();
ptime now = time_now_hires();
while (!m_request_queue.empty() while (!m_request_queue.empty()
&& ((int)m_download_queue.size() < m_desired_queue_size && ((int)m_download_queue.size() < m_desired_queue_size
|| m_queued_time_critical > 0)) || m_queued_time_critical > 0))