instead of using a fixed request timeout for every peer, have an adaptable one based on the average download time for a block

This commit is contained in:
Arvid Norberg 2014-12-08 17:06:04 +00:00
parent c4e5df8e4d
commit 4eaec6d87f
5 changed files with 71 additions and 48 deletions

View File

@ -474,10 +474,16 @@ void print_peer_info(std::string& out, std::vector<libtorrent::peer_info> const&
}
if (print_timers)
{
snprintf(str, sizeof(str), "%8d %4d %7d %6d "
char req_timeout[20] = "-";
// timeout is only meaningful if there is at least one outstanding
// request to the peer
if (i->download_queue_length > 0)
snprintf(req_timeout, sizeof(req_timeout), "%d", i->request_timeout);
snprintf(str, sizeof(str), "%8d %4d %7s %6d "
, int(total_seconds(i->last_active))
, int(total_seconds(i->last_request))
, i->request_timeout
, req_timeout
, int(total_seconds(i->download_queue_time)));
out += str;
}

View File

@ -108,23 +108,18 @@ namespace libtorrent
{
pending_block(piece_block const& b)
: block(b), send_buffer_offset(-1), not_wanted(false)
, timed_out(false), busy(false), receiving(false)
, timed_out(false), busy(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.
// remains -1.
// TODO: 3 make this 29 bits, to fit the bools in its tail
int send_buffer_offset;
// if any of these are set to true, this block
@ -142,11 +137,6 @@ 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
@ -831,6 +821,7 @@ namespace libtorrent
void on_disk_write_complete(disk_io_job const* j
, peer_request r, boost::shared_ptr<torrent> t);
void on_seed_mode_hashed(disk_io_job const* j);
int request_timeout() const;
int wanted_transfer(int channel);
int request_bandwidth(int channel, int bytes = 0);
@ -914,9 +905,11 @@ 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;
// the average time between incoming pieces. Or, if there is no
// outstanding request, the time since the piece was requested. It
// is essentially an estimate of the time it will take to completely
// receive a payload message after it has been requested.
sliding_average<20> m_request_time;
// keep the io_service running as long as we
// have peer connections

View File

@ -71,6 +71,7 @@ struct sliding_average
int mean() const { return m_num_samples > 0 ? (m_mean + 32) / 64 : 0; }
int avg_deviation() const { return m_num_samples > 1 ? (m_average_deviation + 32) / 64 : 0; }
int num_samples() const { return m_num_samples; }
private:
// both of these are fixed point values (* 64)

View File

@ -2445,21 +2445,6 @@ namespace libtorrent
{
if (i->block != b) continue;
in_req_queue = true;
if (i->receiving == false)
{
i->receiving = true;
// if send_buffer_offset is greater then or equal to 0, it means
// the callback of the send operation when we sent this
// request hasn't come back yet, and we're already
// receiving the response from it. Count the rtt as 0.
int rtt = (i->send_buffer_offset >= 0) ? 0
: 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 [%d +/- %d ms]", rtt, m_rtt.mean()
, m_rtt.avg_deviation());
#endif
}
break;
}
@ -2653,7 +2638,7 @@ namespace libtorrent
return;
}
ptime now = time_now();
ptime now = time_now_hires();
t->need_picker();
@ -2717,6 +2702,14 @@ namespace libtorrent
if (m_disconnecting) return;
m_request_time.add_sample(total_milliseconds(now - m_requested));
#if defined TORRENT_LOGGING \
|| defined TORRENT_ERROR_LOGGING \
|| defined TORRENT_VERBOSE_LOGGING
peer_log("*** REQUEST-TIME (%d +- %d ms)"
, m_request_time.mean(), m_request_time.avg_deviation());
#endif
// we completed an incoming block, and there are still outstanding
// requests. The next block we expect to receive now has another
// timeout period until we time out. So, reset the timer.
@ -2732,7 +2725,7 @@ namespace libtorrent
// we received a request within the timeout, make sure this peer is
// not snubbed anymore
if (total_seconds(now - m_requested)
< m_settings.get_int(settings_pack::request_timeout)
< request_timeout()
&& m_snubbed)
{
m_snubbed = false;
@ -2783,6 +2776,14 @@ namespace libtorrent
, performance_alert::too_high_disk_queue_limit));
}
m_request_time.add_sample(total_milliseconds(now - m_requested));
#if defined TORRENT_LOGGING \
|| defined TORRENT_ERROR_LOGGING \
|| defined TORRENT_VERBOSE_LOGGING
peer_log("*** REQUEST-TIME (%d +- %d ms)"
, m_request_time.mean(), m_request_time.avg_deviation());
#endif
// we completed an incoming block, and there are still outstanding
// requests. The next block we expect to receive now has another
// timeout period until we time out. So, reset the timer.
@ -4205,6 +4206,35 @@ namespace libtorrent
return false;
}
int peer_connection::request_timeout() const
{
const int deviation = m_request_time.avg_deviation();
const int avg = m_request_time.mean();
int ret;
if (m_request_time.num_samples() < 2)
{
if (m_request_time.num_samples() == 0)
return m_settings.get_int(settings_pack::request_timeout);
ret = avg + avg / 5;
}
else
{
ret = avg + deviation * 3;
}
// ret is milliseconds, the return value is seconds. Convert to
// seconds and round up
ret = (std::min)((ret + 999) / 1000
, m_settings.get_int(settings_pack::request_timeout));
// timeouts should never be less than 2 seconds. The granularity is whole
// seconds, and only checked once per second. 2 is the minimum to avoid
// being considered timed out instantly
return (std::max)(2, ret);
}
void peer_connection::get_peer_info(peer_info& p) const
{
TORRENT_ASSERT(is_single_thread());
@ -4214,7 +4244,7 @@ namespace libtorrent
p.download_rate_peak = m_download_rate_peak;
p.upload_rate_peak = m_upload_rate_peak;
p.rtt = m_rtt.mean();
p.rtt = m_request_time.mean();
p.down_speed = statistics().download_rate();
p.up_speed = statistics().upload_rate();
p.payload_down_speed = statistics().download_payload_rate();
@ -4228,7 +4258,7 @@ namespace libtorrent
p.num_pieces = m_num_pieces;
if (m_download_queue.empty()) p.request_timeout = -1;
else p.request_timeout = int(total_seconds(m_requested - now)
+ m_settings.get_int(settings_pack::request_timeout));
+ request_timeout());
p.download_queue_time = download_queue_time();
p.queue_bytes = m_outstanding_bytes;
@ -4668,13 +4698,10 @@ 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
&& now > m_requested + seconds(m_settings.get_int(settings_pack::request_timeout)))
&& now > m_requested + seconds(request_timeout()))
{
snub_peer();
}
@ -6020,12 +6047,10 @@ namespace libtorrent
INVARIANT_CHECK;
m_rtt.add_sample(int(total_milliseconds(completed - m_connect)));
#if defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
{
boost::shared_ptr<torrent> t = m_torrent.lock();
t->debug_log("END connect [%p] (RTT: %d ms)", this, m_rtt.mean());
t->debug_log("END connect [%p]", this);
m_connect_time = completed;
}
#endif
@ -6102,8 +6127,7 @@ namespace libtorrent
TORRENT_ASSERT(m_socket);
#if defined TORRENT_VERBOSE_LOGGING
peer_log(">>> COMPLETED [ ep: %s rtt: %d ]"
, print_endpoint(m_remote).c_str(), m_rtt.mean());
peer_log(">>> COMPLETED [ ep: %s ]", print_endpoint(m_remote).c_str());
#endif
// set the socket to non-blocking, so that we can
@ -6202,7 +6226,6 @@ namespace libtorrent
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;
}

View File

@ -210,7 +210,7 @@ namespace libtorrent
SET(stop_tracker_timeout, 5, 0),
SET(tracker_maximum_response_length, 1024*1024, 0),
SET(piece_timeout, 20, 0),
SET(request_timeout, 50, 0),
SET(request_timeout, 60, 0),
SET(request_queue_time, 3, 0),
SET(max_allowed_in_request_queue, 500, 0),
SET(max_out_request_queue, 500, 0),