From 228e22548956d4138c8cebccf203cb130f9fbdfa Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Thu, 24 Apr 2008 07:49:23 +0000 Subject: [PATCH] scans at most 300 peers when finding a connect candidate. Supposedly fixes cpu spikes on large swarms --- include/libtorrent/policy.hpp | 9 ++ src/policy.cpp | 193 +++++++++++++--------------------- 2 files changed, 81 insertions(+), 121 deletions(-) diff --git a/include/libtorrent/policy.hpp b/include/libtorrent/policy.hpp index 29753a11e..e5e76f9ab 100755 --- a/include/libtorrent/policy.hpp +++ b/include/libtorrent/policy.hpp @@ -260,8 +260,17 @@ namespace libtorrent m_num_connect_candidates = 1; } + void erase_peer(iterator i); + private: + bool compare_peer(policy::peer const& lhs, policy::peer const& rhs + , address const& external_ip) const; + + // since the peer list can grow too large + // to scan all of it, start at this iterator + iterator m_round_robin; + iterator find_disconnect_candidate(); iterator find_connect_candidate(); diff --git a/src/policy.cpp b/src/policy.cpp index 29c5f1286..76d21cc3c 100755 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -384,12 +384,26 @@ namespace libtorrent , "blocked peer removed from peer list")); } } - if (p) p->clear_peer(&i->second); - if (i->second.seed) --m_num_seeds; - m_peers.erase(i++); + erase_peer(i++); } } - + + // any peer that is erased from m_peers will be + // erased through this function. This way we can make + // sure that any references to the peer are removed + // as well, such as in the piece picker. + void policy::erase_peer(iterator i) + { + if (m_torrent->has_picker()) + m_torrent->picker().clear_peer(&i->second); + if (i->second.seed) --m_num_seeds; + if (is_connect_candidate(i->second, m_torrent->is_finished())) + --m_num_connect_candidates; + if (m_round_robin == i) ++m_round_robin; + + m_peers.erase(i); + } + bool policy::is_connect_candidate(peer const& p, bool finished) { if (p.connection @@ -450,16 +464,14 @@ namespace libtorrent policy::iterator policy::find_connect_candidate() { - INVARIANT_CHECK; +// too expensive +// INVARIANT_CHECK; ptime now = time_now(); - ptime min_connect_time(now); iterator candidate = m_peers.end(); int min_reconnect_time = m_torrent->settings().min_reconnect_time; bool finished = m_torrent->is_finished(); - - int min_cidr_distance = (std::numeric_limits::max)(); address external_ip = m_torrent->session().external_address(); // don't bias any particular peers when seeding @@ -472,75 +484,35 @@ namespace libtorrent external_ip = address_v4(bytes); } -#ifndef TORRENT_DISABLE_GEO_IP - int max_inet_as_rate = -1; - bool has_db = m_torrent->session().has_asnum_db(); -#endif + if (m_round_robin == iterator() || m_round_robin == m_peers.end()) + m_round_robin = m_peers.begin(); - int connect_candidates = 0; - int seeds = 0; - for (iterator i = m_peers.begin(); i != m_peers.end(); ++i) + for (int iterations = (std::min)(int(m_peers.size()), 300); + iterations > 0; ++m_round_robin, --iterations) { - if (i->second.seed) ++seeds; - if (!is_connect_candidate(i->second, finished)) continue; - ++connect_candidates; + if (m_round_robin == m_peers.end()) m_round_robin = m_peers.begin(); + + if (!is_connect_candidate(m_round_robin->second, finished)) continue; - // prefer peers with lower failcount if (candidate != m_peers.end() - && candidate->second.failcount < i->second.failcount) + && !compare_peer(candidate->second, m_round_robin->second, external_ip)) continue; + + if (now - m_round_robin->second.connected < + seconds(m_round_robin->second.failcount * min_reconnect_time)) continue; - if (now - i->second.connected < seconds(i->second.failcount * min_reconnect_time)) - continue; - - TORRENT_ASSERT(i->second.connected <= now); - - // don't replace a candidate that is on the local - // network with one that isn't. Local peers should - // always be tried first - if (candidate != m_peers.end() - && is_local(candidate->second.ip.address()) - && !is_local(i->second.ip.address())) - continue; - - if (i->second.connected > min_connect_time) continue; - -#ifndef TORRENT_DISABLE_GEO_IP - if (!finished && has_db) - { - // don't bias fast peers when seeding - std::pair* inet_as = i->second.inet_as; - int peak_rate = inet_as ? inet_as->second : 0; - if (peak_rate <= max_inet_as_rate) continue; - max_inet_as_rate = peak_rate; - } - - if (max_inet_as_rate <= 0) -#endif - { - int distance = cidr_distance(external_ip, i->second.ip.address()); - if (distance > min_cidr_distance) continue; - - min_cidr_distance = distance; - } - - min_connect_time = i->second.connected; - candidate = i; + candidate = m_round_robin; } - m_num_connect_candidates = connect_candidates; - m_num_seeds = seeds; - TORRENT_ASSERT(min_connect_time <= now); - #if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING if (candidate != m_peers.end()) { (*m_torrent->session().m_logger) << time_now_string() << " *** FOUND CONNECTION CANDIDATE [" " ip: " << candidate->second.ip << - " d: " << min_cidr_distance << + " d: " << cidr_distance(external_ip, candidate->second.ip.address()) << " external: " << external_ip << - " t: " << total_seconds(time_now() - min_connect_time) << + " t: " << total_seconds(time_now() - candidate->second.connected) << " ]\n"; } #endif @@ -554,10 +526,6 @@ namespace libtorrent if (m_torrent->is_paused()) return; - piece_picker* p = 0; - if (m_torrent->has_picker()) - p = &m_torrent->picker(); - #ifndef TORRENT_DISABLE_DHT bool pinged = false; #endif @@ -589,9 +557,7 @@ namespace libtorrent && !pe.banned && now - pe.connected > minutes(120)) { - if (p) p->clear_peer(&pe); - if (pe.seed) --m_num_seeds; - m_peers.erase(i++); + erase_peer(i++); } else { @@ -599,52 +565,6 @@ namespace libtorrent } } - // ------------------------------------- - // maintain the number of connections - // ------------------------------------- -/* - // count the number of connected peers except for peers - // that are currently in the process of disconnecting - int num_connected_peers = 0; - - for (iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - if (i->connection && !i->connection->is_disconnecting()) - ++num_connected_peers; - } - - if (m_torrent->max_connections() != (std::numeric_limits::max)()) - { - int max_connections = m_torrent->max_connections(); - - if (num_connected_peers >= max_connections) - { - // every minute, disconnect the worst peer in hope of finding a better peer - - ptime local_time = time_now(); - if (m_last_optimistic_disconnect + seconds(120) <= local_time - && find_connect_candidate() != m_peers.end()) - { - m_last_optimistic_disconnect = local_time; - --max_connections; // this will have the effect of disconnecting the worst peer - } - } - else - { - // don't do a disconnect earlier than 1 minute after some peer was connected - m_last_optimistic_disconnect = time_now(); - } - - while (num_connected_peers > max_connections) - { - bool ret = disconnect_one_peer(); - (void)ret; - TORRENT_ASSERT(ret); - --num_connected_peers; - } - } -*/ // ------------------------ // upload shift // ------------------------ @@ -769,6 +689,9 @@ namespace libtorrent "with higher priority, closing"); } } + + if (m_num_connect_candidates > 0) + --m_num_connect_candidates; } else { @@ -797,8 +720,6 @@ namespace libtorrent TORRENT_ASSERT(i->second.connection); if (!c.fast_reconnect()) i->second.connected = time_now(); - if (m_num_connect_candidates > 0) - --m_num_connect_candidates; return true; } @@ -823,10 +744,7 @@ namespace libtorrent p->connection->disconnect("duplicate connection"); return false; } - if (m_torrent->has_picker()) - m_torrent->picker().clear_peer(&i->second); - if (i->second.seed) --m_num_seeds; - m_peers.erase(i); + erase_peer(i); } } else @@ -1406,5 +1324,38 @@ namespace libtorrent return prev_amount_upload; } } + + // this returns true if lhs is a better connect candidate than rhs + bool policy::compare_peer(policy::peer const& lhs, policy::peer const& rhs + , address const& external_ip) const + { + // prefer peers with lower failcount + if (lhs.failcount < rhs.failcount) return true; + if (lhs.failcount > rhs.failcount) return false; + + // Local peers should always be tried first + bool lhs_local = is_local(lhs.ip.address()); + bool rhs_local = is_local(rhs.ip.address()); + if (lhs_local && !rhs_local) return true; + if (!lhs_local && rhs_local) return false; + + if (lhs.connected < rhs.connected) return true; + if (lhs.connected > rhs.connected) return false; + +#ifndef TORRENT_DISABLE_GEO_IP + // don't bias fast peers when seeding + if (!m_torrent->is_finished() && m_torrent->session().has_asnum_db()) + { + std::pair* lhs_as = lhs.inet_as; + std::pair* rhs_as = rhs.inet_as; + if (lhs_as ? lhs_as->second : 0 > rhs_as ? rhs->second : 0) return true; + if (lhs_as ? lhs_as->second : 0 < rhs_as ? rhs->second : 0) return false; + } +#endif + int lhs_distance = cidr_distance(external_ip, lhs.ip.address()); + int rhs_distance = cidr_distance(external_ip, rhs.ip.address()); + if (lhs_distance < rhs_distance) return true; + return false; + } }