diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index b5b31f579..104869548 100644 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -107,12 +107,26 @@ namespace libtorrent struct pending_block { pending_block(piece_block const& b) - : block(b), not_wanted(false) - , timed_out(false), busy(false) + : block(b), send_buffer_offset(-1), not_wanted(false) + , timed_out(false), busy(false), receiving(false) {} piece_block block; + // the time we sent this request. This is used to track the round-trip + // time of receiving the piece. This is not initialized until this + // pending_block is inserted in the download queue (i.e. not the + // requst_queue) + ptime request_time; + + // the number of bytes into the send buffer this request is. Every time + // some portion of the send buffer is transmitted, this offset is + // decremented by the number of bytes sent. once this drops below 0, the + // request_time field is set to the current time. + // if the request has not been written to the send buffer, this field + // remoains -1. + int send_buffer_offset; + // if any of these are set to true, this block // is not allocated // in the piece picker anymore, and open for @@ -128,6 +142,11 @@ namespace libtorrent // busy request at a time in each peer's queue bool busy:1; + // this is true when we first start to receive the resopnse for this + // request. The first time we read the message header for the piece + // response is when we calculate the RTT for this request. + bool receiving:1; + bool operator==(pending_block const& b) { return b.block == block @@ -907,12 +926,6 @@ namespace libtorrent protected: - // estimated round trip time to this peer - // based on the time from when async_connect - // was called to when on_connection_complete - // was called. The rtt is specified in milliseconds - boost::uint16_t m_rtt; - buffer m_recv_buffer; // number of bytes this peer can send and receive @@ -967,6 +980,10 @@ namespace libtorrent sliding_average<20> m_piece_rate; sliding_average<20> m_send_rate; + // the round-trip time of piece requests and the corresponding piece + // message + sliding_average<50> m_rtt; + // keep the io_service running as long as we // have peer connections io_service::work m_work; @@ -1097,10 +1114,6 @@ namespace libtorrent // downloaded from this peer std::vector m_suggested_pieces; - // a list of byte offsets inside the send buffer - // the piece requests - std::vector m_requests_in_buffer; - // the time when this peer last saw a complete copy // of this torrent time_t m_last_seen_complete; diff --git a/include/libtorrent/sliding_average.hpp b/include/libtorrent/sliding_average.hpp index 90c589b1f..032a76ea6 100644 --- a/include/libtorrent/sliding_average.hpp +++ b/include/libtorrent/sliding_average.hpp @@ -35,9 +35,9 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { + // a sliding average accumulator. Add samples to it and it // keeps track of a sliding mean value and an average deviation -// from that average. template struct sliding_average { diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index ff0d544c1..8595070cc 100644 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -131,7 +131,6 @@ namespace libtorrent , m_peer_info(pack.peerinfo) , m_counters(*pack.stats_counters) , m_num_pieces(0) - , m_rtt(0) , m_recv_start(0) , m_desired_queue_size(2) , m_max_out_request_queue(m_settings.get_int(settings_pack::max_out_request_queue)) @@ -2477,11 +2476,21 @@ namespace libtorrent m_receiving_block = b; bool in_req_queue = false; - for (std::vector::const_iterator i = m_download_queue.begin() + for (std::vector::iterator i = m_download_queue.begin() , end(m_download_queue.end()); i != end; ++i) { if (i->block != b) continue; in_req_queue = true; + if (i->receiving == false) + { + i->receiving = true; + int rtt = int(total_milliseconds(time_now_hires() - i->request_time)); + m_rtt.add_sample(rtt); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** RTT: %d ms [%s +/- %s ms]", rtt, m_rtt.mean() + , m_rtt.avg_deviation()); +#endif + } break; } @@ -3733,6 +3742,8 @@ 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)) @@ -3771,6 +3782,7 @@ namespace libtorrent m_counters.inc_stats_counter(counters::num_peers_down_requests); TORRENT_ASSERT(verify_piece(t->to_req(block.block))); + block.send_buffer_offset = m_send_buffer.size(); m_download_queue.push_back(block); m_outstanding_bytes += block_size; #if TORRENT_USE_INVARIANT_CHECKS @@ -3806,6 +3818,7 @@ namespace libtorrent if (m_download_queue.empty()) m_counters.inc_stats_counter(counters::num_peers_down_requests); + block.send_buffer_offset = m_send_buffer.size(); m_download_queue.push_back(block); if (m_queued_time_critical) --m_queued_time_critical; @@ -3862,7 +3875,8 @@ namespace libtorrent // This means we just added a request to this connection m_requested = time_now(); #if defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - t->debug_log("REQUEST [%p] (%d ms)", this, int(total_milliseconds(time_now_hires() - m_unchoke_time))); + t->debug_log("REQUEST [%p] (%d ms)", this + , int(total_milliseconds(time_now_hires() - m_unchoke_time))); #endif } } @@ -4224,7 +4238,7 @@ namespace libtorrent p.download_rate_peak = m_download_rate_peak; p.upload_rate_peak = m_upload_rate_peak; - p.rtt = m_rtt; + p.rtt = m_rtt.mean(); p.down_speed = statistics().download_rate(); p.up_speed = statistics().upload_rate(); p.payload_down_speed = statistics().download_payload_rate(); @@ -4263,7 +4277,10 @@ namespace libtorrent #endif p.download_queue_length = int(download_queue().size() + m_request_queue.size()); - p.requests_in_buffer = int(m_requests_in_buffer.size() + m_request_queue.size()); + p.requests_in_buffer = int(std::count_if(m_download_queue.begin() + , m_download_queue.end() + , boost::bind(&pending_block::send_buffer_offset, _1) >= 0)); + p.target_dl_queue_length = int(desired_queue_size()); p.upload_queue_length = int(upload_queue().size()); p.timed_out_requests = 0; @@ -4715,6 +4732,9 @@ namespace libtorrent return; } + // TODO: 3 instead of using settings_pack::request_timeout, use + // m_rtt.mean() + m_rtt.avg_deviation() * 2 or something like that. + // the configuration option could hopefully be removed if (may_timeout && !m_download_queue.empty() && m_quota[download_channel] > 0 @@ -5743,9 +5763,6 @@ namespace libtorrent void peer_connection::send_buffer(char const* buf, int size, int flags , void (*fun)(char*, int, void*), void* userdata) { - if (flags == message_type_request) - m_requests_in_buffer.push_back(m_send_buffer.size() + size); - int free_space = m_send_buffer.space_in_last_buffer(); if (free_space > size) free_space = size; if (free_space > 0) @@ -6220,12 +6237,12 @@ namespace libtorrent INVARIANT_CHECK; - m_rtt = boost::uint16_t(total_milliseconds(completed - m_connect)); + m_rtt.add_sample(int(total_milliseconds(completed - m_connect))); #if defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING { boost::shared_ptr t = m_torrent.lock(); - t->debug_log("END connect [%p] (%d ms)", this, m_rtt); + t->debug_log("END connect [%p] (RTT: %d ms)", this, m_rtt.mean()); m_connect_time = completed; } #endif @@ -6391,14 +6408,18 @@ namespace libtorrent m_send_buffer.pop_front(bytes_transferred); - for (std::vector::iterator i = m_requests_in_buffer.begin() - , end(m_requests_in_buffer.end()); i != end; ++i) - *i -= bytes_transferred; + ptime now = time_now_hires(); + + for (std::vector::iterator i = m_download_queue.begin() + , end(m_download_queue.end()); i != end; ++i) + { + if (i->send_buffer_offset < 0) continue; + i->send_buffer_offset -= bytes_transferred; + if (i->send_buffer_offset >= 0) continue; + i->request_time = now; + i->send_buffer_offset = -1; + } - while (!m_requests_in_buffer.empty() - && m_requests_in_buffer.front() <= 0) - m_requests_in_buffer.erase(m_requests_in_buffer.begin()); - m_channel_state[upload_channel] &= ~peer_info::bw_network; TORRENT_ASSERT(int(bytes_transferred) <= m_quota[upload_channel]); @@ -6430,7 +6451,7 @@ namespace libtorrent TORRENT_ASSERT(!m_connecting); TORRENT_ASSERT(bytes_transferred > 0); - m_last_sent = time_now(); + m_last_sent = now; #if TORRENT_USE_ASSERTS size_type cur_payload_ul = m_statistics.last_payload_uploaded();