| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/quic/congestion_control/rtt_stats.h" |
| |
| #include <complex> // std::abs |
| |
| using std::max; |
| |
| namespace net { |
| |
| namespace { |
| |
| // Default initial rtt used before any samples are received. |
| const int kInitialRttMs = 100; |
| const float kAlpha = 0.125f; |
| const float kOneMinusAlpha = (1 - kAlpha); |
| const float kBeta = 0.25f; |
| const float kOneMinusBeta = (1 - kBeta); |
| const float kHalfWindow = 0.5f; |
| const float kQuarterWindow = 0.25f; |
| |
| } // namespace |
| |
| RttStats::RttStats() |
| : latest_rtt_(QuicTime::Delta::Zero()), |
| min_rtt_(QuicTime::Delta::Zero()), |
| smoothed_rtt_(QuicTime::Delta::Zero()), |
| mean_deviation_(QuicTime::Delta::Zero()), |
| initial_rtt_us_(kInitialRttMs * base::Time::kMicrosecondsPerMillisecond), |
| num_min_rtt_samples_remaining_(0), |
| recent_min_rtt_window_(QuicTime::Delta::Infinite()) {} |
| |
| bool RttStats::HasUpdates() const { |
| return !smoothed_rtt_.IsZero(); |
| } |
| |
| void RttStats::SampleNewRecentMinRtt(uint32 num_samples) { |
| num_min_rtt_samples_remaining_ = num_samples; |
| new_min_rtt_ = RttSample(); |
| } |
| |
| void RttStats::ExpireSmoothedMetrics() { |
| mean_deviation_ = |
| max(mean_deviation_, |
| QuicTime::Delta::FromMicroseconds( |
| std::abs(smoothed_rtt_.Subtract(latest_rtt_).ToMicroseconds()))); |
| smoothed_rtt_ = max(smoothed_rtt_, latest_rtt_); |
| } |
| |
| // Updates the RTT based on a new sample. |
| void RttStats::UpdateRtt(QuicTime::Delta send_delta, |
| QuicTime::Delta ack_delay, |
| QuicTime now) { |
| if (send_delta.IsInfinite() || send_delta <= QuicTime::Delta::Zero()) { |
| LOG(WARNING) << "Ignoring measured send_delta, because it's is " |
| << "either infinite, zero, or negative. send_delta = " |
| << send_delta.ToMicroseconds(); |
| return; |
| } |
| |
| // Update min_rtt_ first. min_rtt_ does not use an rtt_sample corrected for |
| // ack_delay but the raw observed send_delta, since poor clock granularity at |
| // the client may cause a high ack_delay to result in underestimation of the |
| // min_rtt_. |
| if (min_rtt_.IsZero() || min_rtt_ > send_delta) { |
| min_rtt_ = send_delta; |
| } |
| UpdateRecentMinRtt(send_delta, now); |
| |
| // Correct for ack_delay if information received from the peer results in a |
| // positive RTT sample. Otherwise, we use the send_delta as a reasonable |
| // measure for smoothed_rtt. |
| QuicTime::Delta rtt_sample(send_delta); |
| if (rtt_sample > ack_delay) { |
| rtt_sample = rtt_sample.Subtract(ack_delay); |
| } |
| latest_rtt_ = rtt_sample; |
| // First time call. |
| if (!HasUpdates()) { |
| smoothed_rtt_ = rtt_sample; |
| mean_deviation_ = QuicTime::Delta::FromMicroseconds( |
| rtt_sample.ToMicroseconds() / 2); |
| } else { |
| mean_deviation_ = QuicTime::Delta::FromMicroseconds( |
| kOneMinusBeta * mean_deviation_.ToMicroseconds() + |
| kBeta * std::abs(smoothed_rtt_.Subtract(rtt_sample).ToMicroseconds())); |
| smoothed_rtt_ = smoothed_rtt_.Multiply(kOneMinusAlpha).Add( |
| rtt_sample.Multiply(kAlpha)); |
| DVLOG(1) << " smoothed_rtt(us):" << smoothed_rtt_.ToMicroseconds() |
| << " mean_deviation(us):" << mean_deviation_.ToMicroseconds(); |
| } |
| } |
| |
| void RttStats::UpdateRecentMinRtt(QuicTime::Delta rtt_sample, QuicTime now) { |
| // Recent min_rtt update. |
| if (num_min_rtt_samples_remaining_ > 0) { |
| --num_min_rtt_samples_remaining_; |
| if (new_min_rtt_.rtt.IsZero() || rtt_sample <= new_min_rtt_.rtt) { |
| new_min_rtt_ = RttSample(rtt_sample, now); |
| } |
| if (num_min_rtt_samples_remaining_ == 0) { |
| quarter_window_rtt_ = half_window_rtt_ = recent_min_rtt_ = new_min_rtt_; |
| } |
| } |
| |
| // Update the three recent rtt samples. |
| if (recent_min_rtt_.rtt.IsZero() || rtt_sample <= recent_min_rtt_.rtt) { |
| recent_min_rtt_ = RttSample(rtt_sample, now); |
| quarter_window_rtt_ = half_window_rtt_ = recent_min_rtt_; |
| } else if (rtt_sample <= half_window_rtt_.rtt) { |
| half_window_rtt_ = RttSample(rtt_sample, now); |
| quarter_window_rtt_ = half_window_rtt_; |
| } else if (rtt_sample <= quarter_window_rtt_.rtt) { |
| quarter_window_rtt_ = RttSample(rtt_sample, now); |
| } |
| |
| // Expire old min rtt samples. |
| if (recent_min_rtt_.time < now.Subtract(recent_min_rtt_window_)) { |
| recent_min_rtt_ = half_window_rtt_; |
| half_window_rtt_ = quarter_window_rtt_; |
| quarter_window_rtt_ = RttSample(rtt_sample, now); |
| } else if (half_window_rtt_.time < |
| now.Subtract(recent_min_rtt_window_.Multiply(kHalfWindow))) { |
| half_window_rtt_ = quarter_window_rtt_; |
| quarter_window_rtt_ = RttSample(rtt_sample, now); |
| } else if (quarter_window_rtt_.time < |
| now.Subtract(recent_min_rtt_window_.Multiply(kQuarterWindow))) { |
| quarter_window_rtt_ = RttSample(rtt_sample, now); |
| } |
| } |
| |
| QuicTime::Delta RttStats::SmoothedRtt() const { |
| if (!HasUpdates()) { |
| return QuicTime::Delta::FromMicroseconds(initial_rtt_us_); |
| } |
| return smoothed_rtt_; |
| } |
| |
| QuicTime::Delta RttStats::MinRtt() const { |
| if (!HasUpdates()) { |
| return QuicTime::Delta::FromMicroseconds(initial_rtt_us_); |
| } |
| return min_rtt_; |
| } |
| |
| } // namespace net |