experimental support for the BitTyrant choking algorithm

This commit is contained in:
Arvid Norberg 2010-02-09 03:04:41 +00:00
parent 1a97405189
commit fb47469834
16 changed files with 396 additions and 72 deletions

View File

@ -13,6 +13,7 @@
* optimized disk cache to work with large caches
* support variable number of optimistic unchoke slots and to dynamically
adjust based on the total number of unchoke slots
* support for BitTyrant choker algorithm
0.15 release

View File

@ -49,8 +49,11 @@ void bind_session_settings()
.def_readwrite("free_torrent_hashes", &session_settings::free_torrent_hashes)
.def_readwrite("upnp_ignore_nonrouters", &session_settings::upnp_ignore_nonrouters)
.def_readwrite("send_buffer_watermark", &session_settings::send_buffer_watermark)
#ifndef TORRENT_NO_DEPRECATE
.def_readwrite("auto_upload_slots", &session_settings::auto_upload_slots)
.def_readwrite("auto_upload_slots_rate_based", &session_settings::auto_upload_slots_rate_based)
#endif
.def_readwrite("choking_algorithm", &session_settings::choking_algorithm)
.def_readwrite("use_parole_mode", &session_settings::use_parole_mode)
.def_readwrite("cache_size", &session_settings::cache_size)
.def_readwrite("cache_buffer_chunk_size", &session_settings::cache_buffer_chunk_size)
@ -126,6 +129,13 @@ void bind_session_settings()
.value("largest_contiguous", session_settings::largest_contiguous)
;
enum_<session_settings::disk_cache_algo_t>("choking_algorithm_t")
.value("fixed_slots_choker", session_settings::fixed_slots_choker)
.value("auto_expand_choker", session_settings::auto_expand_choker)
.value("rate_based_choker", session_settings::rate_based_choker)
.value("bittyrant_choker", session_settings::bittyrant_choker)
;
enum_<session_settings::io_buffer_mode_t>("io_buffer_mode_t")
.value("enable_os_cache", session_settings::enable_os_cache)
.value("disable_os_cache_for_aligned_files", session_settings::disable_os_cache_for_aligned_files)

View File

@ -547,26 +547,37 @@ quite unthrottled.
A rate limit of 0 means infinite.
set_max_uploads() set_max_connections() max_uploads() max_connections()
-----------------------------------------------------------------------
set_max_uploads() max_uploads()
-------------------------------
::
void set_max_uploads(int limit);
void set_max_connections(int limit);
int max_uploads() const;
``set_max_uploads`` sets a global limit on the number of unchoked peers (uploads).
The number of uploads is at least one per torrent.
``max_uploads()`` returns the current settings.
The number of unchoke slots may be ignored depending on what
``session_settings::choking_algorithm`` is set to.
set_max_connections() max_connections()
---------------------------------------
::
void set_max_connections(int limit);
int max_connections() const;
These functions will set a global limit on the number of unchoked peers (uploads)
and the number of connections opened. The number of connections is set to a hard
minimum of at least two connections per torrent, so if you set a too low
connections limit, and open too many torrents, the limit will not be met. The
number of uploads is at least one per torrent.
``set_max_connections`` sets a global limit on the number of connections
opened. The number of connections is set to a hard minimum of at least two per
torrent, so if you set a too low connections limit, and open too many torrents,
the limit will not be met.
``max_uploads()`` and ``max_connections()`` returns the current settings.
The number of unchoke slots may be ignored. In order to make this setting
take effect, disable ``session_settings::auto_upload_slots_rate_based``.
``max_connections()`` returns the current settings.
num_uploads() num_connections()
@ -2640,6 +2651,9 @@ consume, even if there's is more quota. Other peers will still be weighed in whe
bandwidth is being distributed. With other words, bandwidth is not distributed strictly
in order of priority, but the priority is used as a weight.
Torrents with higher priority are also more likely to have its peers unchoked, to
distribute more upload capacity to them.
use_interface()
---------------
@ -2663,25 +2677,34 @@ info_hash()
``info_hash()`` returns the info-hash for the torrent.
set_max_uploads() max_uploads() set_max_connections() max_connections()
-----------------------------------------------------------------------
set_max_uploads() max_uploads()
-------------------------------
::
void set_max_uploads(int max_uploads) const;
int max_uploads() const;
void set_max_connections(int max_connections) const;
int max_connections() const;
``set_max_uploads()`` sets the maximum number of peers that's unchoked at the same time on this
torrent. If you set this to -1, there will be no limit.
``max_uploads()`` returns the current settings.
set_max_connections() max_connections()
---------------------------------------
::
void set_max_connections(int max_connections) const;
int max_connections() const;
``set_max_connections()`` sets the maximum number of connection this torrent will open. If all
connections are used up, incoming connections may be refused or poor connections may be closed.
This must be at least 2. The default is unlimited number of connections. If -1 is given to the
function, it means unlimited.
``max_uploads()`` and ``max_connections()`` returns the current settings.
``max_connections()`` returns the current settings.
save_resume_data()
@ -3648,8 +3671,22 @@ session_settings
bool free_torrent_hashes;
bool upnp_ignore_nonrouters;
int send_buffer_watermark;
#ifndef TORRENT_NO_DEPRECATE
bool auto_upload_slots;
bool auto_upload_slots_rate_based;
#endif
enum choking_algorithm_t
{
fixed_slots_choker,
auto_expand_choker,
rate_based_choker,
bittyrant_choker
};
int choking_algorithm;
bool use_parole_mode;
int cache_size;
int cache_buffer_chunk_size;
@ -3928,6 +3965,34 @@ the max upload slots setting is used as a minimum number of unchoked slots.
This algorithm is designed to prevent the peer from spreading its upload
capacity too thin, but still open more slots in order to utilize the full capacity.
``choking_algorithm`` specifies which algorithm to use to determine which peers
to unchoke. This setting replaces the deprecated settings ``auto_upload_slots``
and ``auto_upload_slots_rate_based``.
The options for choking algorithms are:
* ``fixed_slots_choker`` is the traditional choker with a fixed number of unchoke
slots (as specified by ``session::set_max_uploads()``).
* ``auto_expand_choker`` opens at least the number of slots as specified by
``session::set_max_uploads()`` but opens up more slots if the upload capacity
is not saturated. This unchoker will work just like the ``fixed_slot_choker``
if there's no global upload rate limit set.
* ``rate_based_choker`` opens up unchoke slots based on the upload rate
achieved to peers. The more slots that are opened, the marginal upload
rate required to open up another slot increases.
* ``bittyrant_choker`` attempts to optimize download rate by finding the
reciprocation rate of each peer individually and prefers peers that gives
the highest *return on investment*. It still allocates all upload capacity,
but shuffles it around to the best peers first. For this choker to be
efficient, you need to set a global upload rate limit
(``session::set_upload_rate_limit()``). For more information about this
choker, see the paper_.
.. _paper: http://bittyrant.cs.washington.edu/#papers
``use_parole_mode`` specifies if parole mode should be used. Parole mode means
that peers that participate in pieces that fail the hash check are put in a mode
where they are only allowed to download whole pieces. If the whole piece a peer

View File

@ -342,7 +342,7 @@ void print_peer_info(std::string& out, std::vector<libtorrent::peer_info> const&
#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
out += "country ";
#endif
if (print_peer_rate) out += "peer-rate ";
if (print_peer_rate) out += "peer-rate est.rec.rate ";
out += "client \n";
char str[500];
@ -462,7 +462,11 @@ void print_peer_info(std::string& out, std::vector<libtorrent::peer_info> const&
#endif
if (print_peer_rate)
{
snprintf(str, sizeof(str), " %s", add_suffix(i->remote_dl_rate, "/s").c_str());
bool unchoked = (i->flags & peer_info::choked) == 0;
snprintf(str, sizeof(str), " %s %s %s"
, add_suffix(i->remote_dl_rate, "/s").c_str()
, unchoked ? add_suffix(i->estimated_reciprocation_rate, "/s").c_str() : " ");
out += str;
}
out += " ";
@ -771,7 +775,7 @@ int main(int argc, char* argv[])
session_settings settings;
settings.user_agent = "client_test/" LIBTORRENT_VERSION;
settings.auto_upload_slots_rate_based = true;
settings.choking_algorithm = session_settings::auto_expand_choker;
//settings.announce_to_all_trackers = true;
settings.optimize_hashing_for_speed = false;
settings.disk_cache_algorithm = session_settings::largest_contiguous;

View File

@ -188,8 +188,8 @@ namespace libtorrent
upload_limit_too_low,
download_limit_too_low,
send_buffer_watermark_too_low,
too_many_optimistic_unchoke_slots
too_many_optimistic_unchoke_slots,
bittyrant_with_no_uplimit

View File

@ -643,6 +643,9 @@ namespace libtorrent
// statistics gathered from all torrents.
stat m_stat;
int m_peak_up_rate;
int m_peak_down_rate;
// is false by default and set to true when
// the first incoming connection is established
// this is used to know if the client is behind

View File

@ -385,6 +385,8 @@ namespace libtorrent
int desired_queue_size() const { return m_desired_queue_size; }
bool peer_connection::bittyrant_unchoke_compare(
boost::intrusive_ptr<peer_connection const> 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
@ -399,6 +401,10 @@ namespace libtorrent
// interested in the other), disconnect it
void disconnect_if_redundant();
void increase_est_reciprocation_rate();
void decrease_est_reciprocation_rate();
int est_reciprocation_rate() const { return m_est_reciprocation_rate; }
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING
boost::shared_ptr<logger> m_logger;
#endif
@ -692,6 +698,10 @@ namespace libtorrent
// the time when we unchoked this peer
ptime m_last_unchoke;
// if we're unchoked by this peer, this
// was the time
ptime m_last_unchoked;
// timeouts
ptime m_last_receive;
ptime m_last_sent;
@ -702,11 +712,6 @@ namespace libtorrent
// download queue. Used for request timeout
ptime m_requested;
// if the timeout is extended for the outstanding
// requests, this is the number of seconds it was
// extended.
int m_timeout_extend;
// a timestamp when the remote download rate
// was last updated
ptime m_remote_dl_update;
@ -804,6 +809,11 @@ namespace libtorrent
// (-1, -1) if we're not receiving one
piece_block m_receiving_block;
// if the timeout is extended for the outstanding
// requests, this is the number of seconds it was
// extended.
int m_timeout_extend;
// the number of bytes that the other
// end has to send us in order to respond
// to all outstanding piece requests we
@ -905,6 +915,12 @@ namespace libtorrent
int m_download_rate_peak;
int m_upload_rate_peak;
// when using the BitTyrant choker, this is our
// estimated reciprocation rate. i.e. the rate
// we need to send to this peer for it to unchoke
// us
int m_est_reciprocation_rate;
// estimated round trip time to this peer
// based on the time from when async_connect
// was called to when on_connection_complete

View File

@ -214,6 +214,8 @@ namespace libtorrent
// the peers progress
float progress; // [0, 1]
int progress_ppm; // [0, 1000000]
int estimated_reciprocation_rate;
};
struct TORRENT_EXPORT peer_list_entry

View File

@ -123,8 +123,12 @@ namespace libtorrent
, free_torrent_hashes(true)
, upnp_ignore_nonrouters(false)
, send_buffer_watermark(100 * 1024)
#ifndef TORRENT_NO_DEPRECATE
// deprecated in 0.16
, auto_upload_slots(true)
, auto_upload_slots_rate_based(true)
#endif
, choking_algorithm(rate_based_choker)
, use_parole_mode(true)
, cache_size(1024)
, cache_buffer_chunk_size(16)
@ -193,6 +197,9 @@ namespace libtorrent
, default_cache_min_age(1)
, num_optimistic_unchoke_slots(0)
, no_atime_storage(true)
, default_est_reciprocation_rate(16000)
, increase_est_reciprocation_rate(20)
, decrease_est_reciprocation_rate(3)
{}
// this is the user agent that will be sent to the tracker
@ -397,20 +404,22 @@ namespace libtorrent
// the upload rate is low, this is the upper limit.
int send_buffer_watermark;
// if auto_upload_slots is true, and a global upload
// limit is set and the upload rate is less than 90%
// of the upload limit, on new slot is opened up. If
// the upload rate is >= upload limit for an extended
// period of time, one upload slot is closed. The
// upload slots are never automatically decreased below
// the manual settings, through max_uploads.
#ifndef TORRENT_NO_DEPRECATE
// deprecated in 0.16
bool auto_upload_slots;
// this only affects the auto upload slots mechanism.
// if auto_upload_slots is false, this field is not
// considered.
bool auto_upload_slots_rate_based;
#endif
enum choking_algorithm_t
{
fixed_slots_choker,
auto_expand_choker,
rate_based_choker,
bittyrant_choker
};
int choking_algorithm;
// if set to true, peers that participate in a failing
// piece is put in parole mode. i.e. They will only
// download whole pieces until they either fail or pass.
@ -725,6 +734,24 @@ namespace libtorrent
// if set to true, files won't have their atime updated
// on disk reads. This works on linux
bool no_atime_storage;
// === BitTyrant unchoker settings ==
// when using BitTyrant choker, this is the default
// assumed reciprocation rate. This is where each peer starts
int default_est_reciprocation_rate;
// this is the increase of the estimated reciprocation rate
// in percent. We increase by this amount once every unchoke
// interval that we are choked by the other peer and we have
// unchoked them
int increase_est_reciprocation_rate;
// each unchoke interval that we stay unchoked by the other
// peer, and we have unchoked this peer as well, we decrease
// our estimate of the reciprocation rate, since we might have
// over-estimated it
int decrease_est_reciprocation_rate;
};
#ifndef TORRENT_DISABLE_DHT

View File

@ -113,7 +113,8 @@ namespace libtorrent {
"upload limit too low (download rate will suffer)",
"download limit too low (upload rate will suffer)",
"send buffer watermark too low (upload rate will suffer)",
"too many optimistic unchoke slots"
"too many optimistic unchoke slots",
"using bittyrant unchoker with no upload rate limit set"
};
return torrent_alert::message() + ": performance warning: "

View File

@ -180,8 +180,8 @@ namespace libtorrent
{
bandwidth_channel* bwc = i->channel[j];
if (bwc->tmp == 0) channels.push_back(bwc);
TORRENT_ASSERT(INT_MAX - bwc->tmp > i->priority);
bwc->tmp += i->priority;
TORRENT_ASSERT(i->priority > 0);
}
}

View File

@ -57,6 +57,8 @@ namespace libtorrent
for (int j = 0; j < 5 && channel[j]; ++j)
{
if (channel[j]->throttle() == 0) continue;
TORRENT_ASSERT(channel[j]->distribute_quota
< (std::numeric_limits<boost::uint64_t>::max)() / priority);
quota = (std::min)(int(boost::uint64_t(channel[j]->distribute_quota)
* priority / channel[j]->tmp), quota);
}
@ -65,7 +67,6 @@ namespace libtorrent
channel[j]->use_quota(quota);
TORRENT_ASSERT(assigned <= request_size);
--ttl;
TORRENT_ASSERT(assigned <= request_size);
return quota;
}
}

View File

@ -85,10 +85,10 @@ namespace libtorrent
, m_last_request(time_now())
, m_last_incoming_request(min_time())
, m_last_unchoke(time_now())
, m_last_unchoked(time_now())
, m_last_receive(time_now())
, m_last_sent(time_now())
, m_requested(min_time())
, m_timeout_extend(0)
, m_remote_dl_update(time_now())
, m_connect(time_now())
, m_became_uninterested(time_now())
@ -101,6 +101,7 @@ namespace libtorrent
, m_remote(endp)
, m_torrent(tor)
, m_receiving_block(-1, -1)
, m_timeout_extend(0)
, m_outstanding_bytes(0)
, m_queued_time_critical(0)
, m_num_pieces(0)
@ -153,6 +154,8 @@ namespace libtorrent
, m_received_in_piece(0)
#endif
{
m_est_reciprocation_rate = m_ses.m_settings.default_est_reciprocation_rate;
#if TORRENT_USE_I2P
if (peerinfo && peerinfo->is_i2p_addr)
{
@ -216,10 +219,10 @@ namespace libtorrent
, m_last_request(time_now())
, m_last_incoming_request(min_time())
, m_last_unchoke(time_now())
, m_last_unchoked(time_now())
, m_last_receive(time_now())
, m_last_sent(time_now())
, m_requested(min_time())
, m_timeout_extend(0)
, m_remote_dl_update(time_now())
, m_connect(time_now())
, m_became_uninterested(time_now())
@ -231,6 +234,7 @@ namespace libtorrent
, m_socket(s)
, m_remote(endp)
, m_receiving_block(-1, -1)
, m_timeout_extend(0)
, m_outstanding_bytes(0)
, m_queued_time_critical(0)
, m_num_pieces(0)
@ -283,6 +287,8 @@ namespace libtorrent
, m_received_in_piece(0)
#endif
{
m_est_reciprocation_rate = m_ses.m_settings.default_est_reciprocation_rate;
#if TORRENT_USE_I2P
if (peerinfo && peerinfo->is_i2p_addr)
{
@ -340,6 +346,62 @@ namespace libtorrent
}
#endif
void peer_connection::increase_est_reciprocation_rate()
{
m_est_reciprocation_rate += m_est_reciprocation_rate
* m_ses.m_settings.increase_est_reciprocation_rate / 100;
}
void peer_connection::decrease_est_reciprocation_rate()
{
m_est_reciprocation_rate -= m_est_reciprocation_rate
* m_ses.m_settings.decrease_est_reciprocation_rate / 100;
}
bool peer_connection::bittyrant_unchoke_compare(
boost::intrusive_ptr<peer_connection const> const& p) const
{
TORRENT_ASSERT(p);
peer_connection const& rhs = *p;
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;
// 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;
boost::shared_ptr<torrent> t1 = m_torrent.lock();
TORRENT_ASSERT(t1);
boost::shared_ptr<torrent> t2 = rhs.associated_torrent().lock();
TORRENT_ASSERT(t2);
// take torrent priority into account
d1 *= 1 + t1->priority();
d2 *= 1 + t2->priority();
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;
// 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() || u1 > (std::max)(t1->torrent_file().piece_length() * pieces, 256 * 1024);
bool c2_done = rhs.is_choked() || u2 > (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 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;
}
bool peer_connection::unchoke_compare(boost::intrusive_ptr<peer_connection const> const& p) const
{
TORRENT_ASSERT(p);
@ -351,6 +413,16 @@ namespace libtorrent
// first compare how many bytes they've sent us
c1 = m_statistics.total_payload_download() - m_downloaded_at_last_unchoke;
c2 = rhs.m_statistics.total_payload_download() - rhs.m_downloaded_at_last_unchoke;
boost::shared_ptr<torrent> t1 = m_torrent.lock();
TORRENT_ASSERT(t1);
boost::shared_ptr<torrent> t2 = rhs.associated_torrent().lock();
TORRENT_ASSERT(t2);
// take torrent priority into account
c1 *= 1 + t1->priority();
c2 *= 1 + t2->priority();
if (c1 > c2) return true;
if (c1 < c2) return false;
@ -361,10 +433,6 @@ namespace libtorrent
// 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
boost::shared_ptr<torrent> t1 = m_torrent.lock();
TORRENT_ASSERT(t1);
boost::shared_ptr<torrent> t2 = rhs.associated_torrent().lock();
TORRENT_ASSERT(t2);
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);
@ -382,9 +450,18 @@ namespace libtorrent
size_type c1;
size_type c2;
boost::shared_ptr<torrent> t1 = m_torrent.lock();
TORRENT_ASSERT(t1);
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;
// take torrent priority into account
c1 *= 1 + t1->priority();
c2 *= 1 + t2->priority();
return c1 > c2;
}
@ -1280,6 +1357,7 @@ namespace libtorrent
(*m_logger) << time_now_string() << " <== UNCHOKE\n";
#endif
m_peer_choked = false;
m_last_unchoked = time_now();
if (is_disconnecting()) return;
if (is_interesting())
@ -3343,6 +3421,11 @@ namespace libtorrent
#endif
p.progress_ppm = p.pieces.count() * 1000000 / p.pieces.size();
}
p.estimated_reciprocation_rate = m_est_reciprocation_rate;
int upload_capacity = m_ses.upload_rate_limit();
if (upload_capacity == 0)
upload_capacity = (std::max)(20000, m_ses.m_peak_up_rate + 10000);
}
// allocates a disk buffer of size 'disk_buffer_size' and replaces the
@ -4014,10 +4097,31 @@ namespace libtorrent
, bandwidth_channel* bwc4)
{
shared_ptr<torrent> t = m_torrent.lock();
int priority = 1 + is_interesting() * 2 + m_requests_in_buffer.size();
if (priority > 255) priority = 255;
priority += t->priority() << 8;
int priority;
if (m_ses.m_settings.choking_algorithm == session_settings::bittyrant_choker
&& !t->is_upload_only())
{
// when we use the bittyrant choker, the priority of a peer
// is decided based on the estimated reciprocation rate and
// the share it represents of the total upload rate capacity
// the torrent priority is taken into account when unchoking peers
int upload_capacity = m_ses.upload_rate_limit();
if (upload_capacity == 0)
{
// we don't know at what rate we can upload. If we have a
// measurement of the peak, use that + 10kB/s, otherwise
// assume 20 kB/s
upload_capacity = (std::max)(20000, m_ses.m_peak_up_rate + 10000);
}
priority = (boost::uint64_t(m_est_reciprocation_rate) << 10) / upload_capacity;
}
else
{
priority = 1 + is_interesting() * 2 + m_requests_in_buffer.size();
if (priority > 255) priority = 255;
priority += t->priority() << 8;
}
TORRENT_ASSERT(priority < 0xffff);
// peers that we are not interested in are non-prioritized
m_channel_state[upload_channel] = peer_info::bw_limit;

View File

@ -204,7 +204,7 @@ namespace libtorrent
set.active_limit = 2000;
set.active_seeds = 2000;
set.auto_upload_slots = false;
set.choking_algorithm = session_settings::fixed_slots_choker;
// in order to be able to deliver very high
// upload rates, this should be able to cover

View File

@ -228,8 +228,11 @@ namespace aux {
TORRENT_SETTING(boolean, free_torrent_hashes)
TORRENT_SETTING(boolean, upnp_ignore_nonrouters)
TORRENT_SETTING(integer, send_buffer_watermark)
#ifndef TORRENT_NO_DEPRECATE
TORRENT_SETTING(boolean, auto_upload_slots)
TORRENT_SETTING(boolean, auto_upload_slots_rate_based)
#endif
TORRENT_SETTING(integer, choking_algorithm)
TORRENT_SETTING(boolean, use_parole_mode)
TORRENT_SETTING(integer, cache_size)
TORRENT_SETTING(integer, cache_buffer_chunk_size)
@ -438,6 +441,8 @@ namespace aux {
, m_auto_scrape_time_scaler(180)
, m_next_explicit_cache_torrent(0)
, m_cache_rotation_timer(0)
, m_peak_up_rate(0)
, m_peak_down_rate(0)
, m_incoming_connection(false)
, m_created(time_now_hires())
, m_last_tick(m_created)
@ -1052,6 +1057,17 @@ namespace aux {
|| m_settings.low_prio_disk != s.low_prio_disk)
update_disk_io_thread = true;
#ifndef TORRENT_NO_DEPRECATE
// support deprecated choker settings
if (s.choking_algorithm == session_settings::rate_based_choker)
{
if (s.auto_upload_slots && !s.auto_upload_slots_rate_based)
m_settings.choking_algorithm = session_settings::auto_expand_choker;
else if (!s.auto_upload_slots)
m_settings.choking_algorithm = session_settings::fixed_slots_choker;
}
#endif
// safety check
if (m_settings.volatile_read_cache
&& (m_settings.suggest_mode == session_settings::suggest_read_cache
@ -1064,6 +1080,12 @@ namespace aux {
m_settings.volatile_read_cache = false;
}
if (m_settings.choking_algorithm != s.choking_algorithm)
{
// trigger recalculation of the unchoked peers
m_unchoke_time_scaler = 0;
}
// if queuing settings were changed, recalculate
// queued torrents sooner
if ((m_settings.active_downloads != s.active_downloads
@ -1089,7 +1111,12 @@ namespace aux {
, performance_alert::too_many_optimistic_unchoke_slots));
}
if (!s.auto_upload_slots) m_allowed_upload_slots = m_max_uploads;
if (s.choking_algorithm == session_settings::fixed_slots_choker)
m_allowed_upload_slots = m_max_uploads;
else if (s.choking_algorithm == session_settings::auto_expand_choker
&& m_allowed_upload_slots < m_max_uploads)
m_allowed_upload_slots = m_max_uploads;
// replace all occurances of '\n' with ' '.
std::string::iterator i = m_settings.user_agent.begin();
while ((i = std::find(i, m_settings.user_agent.end(), '\n'))
@ -1915,6 +1942,9 @@ namespace aux {
}
}
m_peak_up_rate = (std::max)(m_stat.upload_rate(), m_peak_up_rate);
m_peak_down_rate = (std::max)(m_stat.download_rate(), m_peak_down_rate);
m_stat.second_tick(tick_interval_ms);
TORRENT_ASSERT(least_recently_scraped == m_torrents.end()
@ -2394,8 +2424,29 @@ namespace aux {
++i;
torrent* t = p->associated_torrent().lock().get();
policy::peer* pi = p->peer_info_struct();
if (p->ignore_unchoke_slots() || t == 0 || pi == 0) continue;
if (m_settings.choking_algorithm == session_settings::bittyrant_choker)
{
if (!p->is_choked())
{
policy::peer* pi = p->peer_info_struct();
if (!p->has_peer_choked())
{
// we're unchoked, we may want to lower our estimated
// reciprocation rate
p->decrease_est_reciprocation_rate();
}
else
{
// we've unchoked this peer, and it hasn't reciprocated
// we may want to increase our estimated reciprocation rate
p->increase_est_reciprocation_rate();
}
}
}
if (!p->is_peer_interested()
|| p->is_disconnecting()
|| p->is_connecting()
@ -2417,8 +2468,7 @@ namespace aux {
peers.push_back(p.get());
}
if (m_settings.auto_upload_slots_rate_based
&& m_settings.auto_upload_slots)
if (m_settings.choking_algorithm == session_settings::rate_based_choker)
{
m_allowed_upload_slots = 0;
std::sort(peers.begin(), peers.end()
@ -2460,17 +2510,26 @@ namespace aux {
++m_allowed_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
std::sort(peers.begin(), peers.end()
, bind(&peer_connection::unchoke_compare, _1, _2));
if (m_settings.choking_algorithm == session_settings::bittyrant_choker)
{
// if we're using the bittyrant choker, sort peers by their return
// on investment. i.e. download rate / upload rate
std::sort(peers.begin(), peers.end()
, bind(&peer_connection::bittyrant_unchoke_compare, _1, _2));
}
else
{
// 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
std::sort(peers.begin(), peers.end()
, bind(&peer_connection::unchoke_compare, _1, _2));
}
// auto unchoke
int upload_limit = m_bandwidth_channel[peer_connection::upload_channel]->throttle();
if (!m_settings.auto_upload_slots_rate_based
&& m_settings.auto_upload_slots
if (m_settings.choking_algorithm == session_settings::auto_expand_choker
&& upload_limit > 0)
{
// if our current upload rate is less than 90% of our
@ -2494,9 +2553,25 @@ namespace aux {
int num_opt_unchoke = m_settings.num_optimistic_unchoke_slots;
if (num_opt_unchoke == 0) num_opt_unchoke = (std::max)(1, m_allowed_upload_slots / 5);
// reserve one upload slot for optimistic unchokes
// reserve some upload slots for optimistic unchokes
int unchoke_set_size = m_allowed_upload_slots - num_opt_unchoke;
int upload_capacity_left = 0;
if (m_settings.choking_algorithm == session_settings::bittyrant_choker)
{
upload_capacity_left = m_upload_channel.throttle();
if (upload_capacity_left == 0)
{
// we don't know at what rate we can upload. If we have a
// measurement of the peak, use that + 10kB/s, otherwise
// assume 20 kB/s
upload_capacity_left = (std::max)(20000, m_peak_up_rate + 10000);
if (m_alerts.should_post<performance_alert>())
m_alerts.post_alert(performance_alert(torrent_handle()
, performance_alert::bittyrant_with_no_uplimit));
}
}
m_num_unchoked = 0;
// go through all the peers and unchoke the first ones and choke
// all the other ones.
@ -2508,12 +2583,28 @@ namespace aux {
TORRENT_ASSERT(!p->ignore_unchoke_slots());
// this will update the m_uploaded_at_last_unchoke
// #error this should be called for all peers!
p->reset_choke_counters();
torrent* t = p->associated_torrent().lock().get();
TORRENT_ASSERT(t);
if (unchoke_set_size > 0)
// if this peer should be unchoked depends on different things
// in different unchoked schemes
bool unchoke = false;
if (m_settings.choking_algorithm == session_settings::bittyrant_choker)
{
unchoke = p->est_reciprocation_rate() <= upload_capacity_left;
}
else
{
unchoke = unchoke_set_size > 0;
}
if (unchoke)
{
upload_capacity_left -= p->est_reciprocation_rate();
// yes, this peer should be unchoked
if (p->is_choked())
{
@ -3565,7 +3656,7 @@ namespace aux {
std::set<peer_connection*> unique_peers;
TORRENT_ASSERT(m_max_connections > 0);
TORRENT_ASSERT(m_max_uploads >= 0);
if (!m_settings.auto_upload_slots_rate_based || !m_settings.auto_upload_slots)
if (m_settings.choking_algorithm == session_settings::auto_expand_choker)
TORRENT_ASSERT(m_allowed_upload_slots >= m_max_uploads);
int unchokes = 0;
int num_optimistic = 0;

View File

@ -17,10 +17,10 @@ void test_swarm()
session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49010, 50000), "0.0.0.0", 0);
session ses3(fingerprint("LT", 0, 1, 0, 0), std::make_pair(50010, 51000), "0.0.0.0", 0);
ses1.set_severity_level(alert::debug);
ses2.set_severity_level(alert::debug);
ses3.set_severity_level(alert::debug);
ses1.set_alert_mask(alert::all_categories);
ses2.set_alert_mask(alert::all_categories);
ses3.set_alert_mask(alert::all_categories);
// this is to avoid everything finish from a single peer
// immediately. To make the swarm actually connect all
// three peers before finishing.
@ -35,8 +35,7 @@ void test_swarm()
session_settings settings;
settings.allow_multiple_connections_per_ip = true;
settings.ignore_limits_on_local_network = false;
settings.auto_upload_slots = true;
settings.auto_upload_slots_rate_based = false;
settings.choking_algorithm = session_settings::auto_expand_choker;
ses1.set_settings(settings);
ses2.set_settings(settings);
ses3.set_settings(settings);