factored out choke comparison functions into choker.cpp
This commit is contained in:
parent
02a9ea18b7
commit
e09a2b770d
|
@ -554,13 +554,10 @@ namespace libtorrent
|
|||
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
|
||||
// for which one is more eligible for an unchoke.
|
||||
// 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(); }
|
||||
|
||||
// resets the byte counters that are used to measure
|
||||
|
@ -750,6 +747,10 @@ namespace libtorrent
|
|||
size_type uploaded_since_unchoked() const
|
||||
{ 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
|
||||
// start downloading payload again
|
||||
void on_disk();
|
||||
|
@ -769,6 +770,8 @@ namespace libtorrent
|
|||
|
||||
counters& stats_counters() const { return m_counters; }
|
||||
|
||||
int get_priority(int channel) const;
|
||||
|
||||
protected:
|
||||
|
||||
size_t try_read(sync_t s, error_code& ec);
|
||||
|
@ -878,8 +881,6 @@ namespace libtorrent
|
|||
int wanted_transfer(int channel);
|
||||
int request_bandwidth(int channel, int bytes = 0);
|
||||
|
||||
int get_priority(int channel) const;
|
||||
|
||||
boost::shared_ptr<socket_type> m_socket;
|
||||
|
||||
// the queue of blocks we have requested
|
||||
|
@ -981,6 +982,7 @@ namespace libtorrent
|
|||
// the time we received the last
|
||||
// piece request from the peer
|
||||
ptime m_last_incoming_request;
|
||||
|
||||
// the time when we unchoked this peer
|
||||
ptime m_last_unchoke;
|
||||
|
||||
|
|
187
src/choker.cpp
187
src/choker.cpp
|
@ -33,11 +33,173 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/choker.hpp"
|
||||
#include "libtorrent/peer_connection.hpp"
|
||||
#include "libtorrent/aux_/session_settings.hpp"
|
||||
#include "libtorrent/torrent.hpp"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
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 max_upload_rate
|
||||
, time_duration unchoke_interval
|
||||
|
@ -78,11 +240,8 @@ namespace libtorrent
|
|||
|
||||
// if we're using the bittyrant choker, sort peers by their return
|
||||
// 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()
|
||||
, boost::bind(&peer_connection::bittyrant_unchoke_compare, _1, _2));
|
||||
, boost::bind(&bittyrant_unchoke_compare, _1, _2));
|
||||
|
||||
int upload_capacity_left = max_upload_rate;
|
||||
|
||||
|
@ -129,7 +288,7 @@ namespace libtorrent
|
|||
// TODO: make the comparison function a free function and move it
|
||||
// into this cpp file
|
||||
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
|
||||
int rate_threshold = 1024;
|
||||
|
@ -151,17 +310,17 @@ namespace libtorrent
|
|||
++upload_slots;
|
||||
}
|
||||
|
||||
// sorts the peers that are eligible for unchoke by download rate and secondary
|
||||
// by total upload. The reason for this is, if all torrents are being seeded,
|
||||
// the download rate will be 0, and the peers we have sent the least to should
|
||||
// be unchoked
|
||||
// sorts the peers that are eligible for unchoke by download rate and
|
||||
// secondary by total upload. The reason for this is, if all torrents are
|
||||
// being seeded, the download rate will be 0, and the peers we have sent
|
||||
// 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
|
||||
// this cpp file
|
||||
std::sort(peers.begin(), peers.end()
|
||||
, boost::bind(&peer_connection::unchoke_compare, _1, _2));
|
||||
std::partial_sort(peers.begin(), peers.begin()
|
||||
+ (std::min)(upload_slots, int(peers.size())), peers.end()
|
||||
, boost::bind(&unchoke_compare, _1, _2, boost::cref(sett)));
|
||||
|
||||
return upload_slots;
|
||||
}
|
||||
|
|
|
@ -294,151 +294,6 @@ namespace libtorrent
|
|||
* 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
|
||||
{
|
||||
TORRENT_ASSERT(is_single_thread());
|
||||
|
@ -463,22 +318,6 @@ namespace libtorrent
|
|||
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()
|
||||
{
|
||||
TORRENT_ASSERT(is_single_thread());
|
||||
|
@ -3909,8 +3748,6 @@ namespace libtorrent
|
|||
|
||||
bool empty_download_queue = m_download_queue.empty();
|
||||
|
||||
ptime now = time_now_hires();
|
||||
|
||||
while (!m_request_queue.empty()
|
||||
&& ((int)m_download_queue.size() < m_desired_queue_size
|
||||
|| m_queued_time_critical > 0))
|
||||
|
|
Loading…
Reference in New Issue