| // 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) { |
| QuicTime::Delta rtt_sample(QuicTime::Delta::Zero()); |
| if (send_delta > ack_delay) { |
| rtt_sample = send_delta.Subtract(ack_delay); |
| } else if (!HasUpdates()) { |
| // Even though we received information from the peer suggesting |
| // an invalid (negative) RTT, we can use the send delta as an |
| // approximation until we get a better estimate. |
| rtt_sample = send_delta; |
| } |
| |
| if (rtt_sample.IsInfinite() || rtt_sample.IsZero()) { |
| DVLOG(1) << "Ignoring rtt, because it's " |
| << (rtt_sample.IsZero() ? "Zero" : "Infinite"); |
| return; |
| } |
| // RTT can't be negative. |
| DCHECK_LT(0, rtt_sample.ToMicroseconds()); |
| |
| latest_rtt_ = rtt_sample; |
| // First time call or link delay decreases. |
| if (min_rtt_.IsZero() || min_rtt_ > rtt_sample) { |
| min_rtt_ = rtt_sample; |
| } |
| UpdateRecentMinRtt(rtt_sample, now); |
| // 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_; |
| } |
| |
| } // namespace net |