experimental support for the BitTyrant choking algorithm
This commit is contained in:
parent
1a97405189
commit
fb47469834
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: "
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue