diff --git a/ChangeLog b/ChangeLog index 59fc53c82..b2d2e15c4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * added auto-sequential feature. download well-seeded torrents in-order * removed built-in GeoIP support (this functionality is orthogonal to libtorrent) * deprecate proxy settings in favor of regular settings * deprecate separate settings for peer protocol encryption diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index c2397fa12..6076eab3b 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -672,6 +672,7 @@ namespace libtorrent void update_outgoing_interfaces(); void update_listen_interfaces(); void update_privileged_ports(); + void update_auto_sequential(); void update_upnp(); void update_natpmp(); diff --git a/include/libtorrent/settings_pack.hpp b/include/libtorrent/settings_pack.hpp index 046ef6ec4..788de29bb 100644 --- a/include/libtorrent/settings_pack.hpp +++ b/include/libtorrent/settings_pack.hpp @@ -619,6 +619,12 @@ namespace libtorrent // configured proxy, if any. proxy_peer_connections, + // if this setting is true, torrents with a very high availability + // of pieces (and seeds) are downloaded sequentially. This is more + // efficient for the disk I/O. With many seeds, the download order + // is unlikely to matter anyway + auto_sequential, + max_bool_setting_internal, num_bool_settings = max_bool_setting_internal - bool_type_base }; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 93645428c..c08761e57 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -375,7 +375,8 @@ namespace libtorrent void new_external_ip(); - torrent_status::state_t state() const { return (torrent_status::state_t)m_state; } + torrent_status::state_t state() const + { return (torrent_status::state_t)m_state; } void set_state(torrent_status::state_t s); aux::session_settings const& settings() const; @@ -383,7 +384,7 @@ namespace libtorrent void set_sequential_download(bool sd); bool is_sequential_download() const - { return m_sequential_download; } + { return m_sequential_download || m_auto_sequential; } void queue_up(); void queue_down(); @@ -625,6 +626,7 @@ namespace libtorrent // the number of peers that belong to this torrent int num_peers() const { return (int)m_connections.size(); } int num_seeds() const; + int num_downloaders() const; typedef std::vector::iterator peer_iterator; typedef std::vector::const_iterator const_peer_iterator; @@ -643,6 +645,7 @@ namespace libtorrent void add_suggest_piece(int piece); void update_suggest_piece(int index, int change); + void update_auto_sequential(); void refresh_suggest_pieces(); void do_refresh_suggest_pieces(); void on_cache_info(disk_io_job const* j); @@ -1611,7 +1614,11 @@ namespace libtorrent // at high enough rates, it's inactive. bool m_inactive:1; - // TODO: there's space for 1 bits here + // this is set if the auto_sequential setting is true and this swarm + // satisfies the criteria to be considered high-availability. i.e. if + // there's mostly seeds in the swarm, download the files sequentially + // for improved disk I/O performance. + bool m_auto_sequential:1; // ---- diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index fc37c73b5..8046e6062 100644 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -6945,7 +6945,8 @@ namespace libtorrent // if m_num_pieces == 0, we probably don't have the // metadata yet. boost::shared_ptr t = m_torrent.lock(); - return m_num_pieces == (int)m_have_piece.size() && m_num_pieces > 0 && t && t->valid_metadata(); + return m_num_pieces == (int)m_have_piece.size() + && m_num_pieces > 0 && t && t->valid_metadata(); } void peer_connection::set_share_mode(bool u) diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 74ee26110..09861e096 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -6188,6 +6188,13 @@ retry: } } + void session_impl::update_auto_sequential() + { + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + i->second->update_auto_sequential(); + } + void session_impl::update_proxy() { // in case we just set a socks proxy, we might have to diff --git a/src/settings_pack.cpp b/src/settings_pack.cpp index 4bda7356a..3f1dbca87 100644 --- a/src/settings_pack.cpp +++ b/src/settings_pack.cpp @@ -200,6 +200,7 @@ namespace libtorrent SET_NOPREV(prefer_rc4, false, 0), SET_NOPREV(proxy_hostnames, true, 0), SET_NOPREV(proxy_peer_connections, true, 0), + SET_NOPREV(auto_sequential, true, &session_impl::update_auto_sequential), }; int_setting_entry_t int_settings[settings_pack::num_int_settings] = diff --git a/src/torrent.cpp b/src/torrent.cpp index 652294c6c..88cf45f0a 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -3110,6 +3110,8 @@ namespace libtorrent m_incomplete = incomplete; m_downloaded = downloaded; + update_auto_sequential(); + // these numbers are cached in the resume data m_need_save_resume_data = true; } @@ -3306,6 +3308,31 @@ namespace libtorrent state_updated(); } + void torrent::update_auto_sequential() + { + if (!m_ses.settings().get_bool(settings_pack::auto_sequential)) + { + m_auto_sequential = false; + return; + } + + if (int(m_connections.size()) - m_num_connecting < 10) + { + // there are too few peers. Be conservative and don't assume it's + // well seeded until we can connect to more peers + m_auto_sequential = false; + return; + } + + // if there are at least 10 seeds, and there are 10 times more + // seeds than downloaders, enter sequential download mode + // (for performance) + int downloaders = num_downloaders(); + int seeds = num_seeds(); + m_auto_sequential = downloaders * 10 <= seeds + && seeds > 9; + } + void torrent::do_connect_boost() { if (!m_need_connect_boost) return; @@ -10657,6 +10684,7 @@ namespace libtorrent { need_policy(); m_policy->set_seed(p, s); + update_auto_sequential(); } void torrent::clear_failcount(torrent_peer* p) @@ -11322,6 +11350,8 @@ namespace libtorrent m_stats_counters.inc_stats_counter(counters::recv_failed_bytes, b); } + // TODO: 3 make this a gauge that's kept up to date rather than counting + // every time int torrent::num_seeds() const { TORRENT_ASSERT(is_single_thread()); @@ -11334,6 +11364,20 @@ namespace libtorrent return ret; } + // TODO: 3 make this a gauge that's kept up to date rather than counting + // every time + int torrent::num_downloaders() const + { + TORRENT_ASSERT(is_single_thread()); + INVARIANT_CHECK; + + int ret = 0; + for (const_peer_iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + if ((*i)->is_connected() && !(*i)->is_seed()) ++ret; + return ret; + } + void torrent::tracker_request_error(tracker_request const& r , int response_code, error_code const& ec, const std::string& msg , int retry_interval)