summaryrefslogtreecommitdiff
path: root/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc')
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc258
1 files changed, 258 insertions, 0 deletions
diff --git a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc
new file mode 100644
index 00000000000..73e005d788a
--- /dev/null
+++ b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc
@@ -0,0 +1,258 @@
+// Copyright (c) 2013 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/inter_arrival_overuse_detector.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+// Initial noise variance, equal to a standard deviation of 1 millisecond.
+static const float kInitialVarianceNoise = 1000000.0;
+
+// Minimum variance of the time delta.
+static const int kMinVarianceDelta = 10000;
+
+// Threshold for accumulated delta.
+static const int kThresholdAccumulatedDeltasUs = 1000;
+
+// The higher the beta parameter, the lower is the effect of the input and the
+// more damping of the noise. And the longer time for a detection.
+static const float kBeta = 0.98f;
+
+// Same as above, described as numerator and denominator.
+static const int kBetaNumerator = 49;
+static const int kBetaDenominator = 50;
+
+// Trigger a signal when the accumulated time drift is larger than
+// 5 x standard deviation.
+// A lower value triggers earlier with more false detect as a side effect.
+static const int kDetectDriftStandardDeviation = 5;
+
+// Trigger an overuse when the time difference between send time and receive
+// is larger than 7 x standard deviation.
+// A lower value triggers earlier with more false detect as a side effect.
+static const float kDetectTimeDiffStandardDeviation = 7;
+
+// Trigger an overuse when the mean of the time difference diverges too far
+// from 0.
+// A higher value trigger earlier with more false detect as a side effect.
+static const int kDetectSlopeFactor = 14;
+
+// We need to get some initial statistics before the detection can start.
+static const int kMinSamplesBeforeDetect = 10;
+
+namespace net {
+
+InterArrivalOveruseDetector::InterArrivalOveruseDetector()
+ : last_sequence_number_(0),
+ num_of_deltas_(0),
+ accumulated_deltas_(QuicTime::Delta::Zero()),
+ delta_mean_(0.0),
+ delta_variance_(kInitialVarianceNoise),
+ delta_overuse_counter_(0),
+ delta_estimate_(kBandwidthSteady),
+ slope_overuse_counter_(0),
+ slope_estimate_(kBandwidthSteady),
+ send_receive_offset_(QuicTime::Delta::Infinite()),
+ estimated_congestion_delay_(QuicTime::Delta::Zero()) {
+}
+
+void InterArrivalOveruseDetector::OnAcknowledgedPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicTime send_time,
+ bool last_of_send_time,
+ QuicTime receive_time) {
+ if (last_sequence_number_ >= sequence_number) {
+ // This is an old packet and should be ignored. Note that we are called
+ // with a full 64 bit sequence number, even if the wire format may only
+ // convey some low-order bits of that number.
+ DLOG(INFO) << "Skip old packet";
+ return;
+ }
+
+ last_sequence_number_ = sequence_number;
+
+ if (current_packet_group_.send_time != send_time) {
+ // First value in this group. If the last packet of a group is lost we
+ // overwrite the old value and start over with a new measurement.
+ current_packet_group_.send_time = send_time;
+ // The receive_time value might not be first in a packet burst if that
+ // packet was lost, however we will still use it in this calculation.
+ UpdateSendReceiveTimeOffset(receive_time.Subtract(send_time));
+ }
+ if (!last_of_send_time) {
+ // We expect more packet with this send time.
+ return;
+ }
+ // First packet of a later group, the previous group sample is ready.
+ if (previous_packet_group_.send_time.IsInitialized()) {
+ QuicTime::Delta sent_delta = send_time.Subtract(
+ previous_packet_group_.send_time);
+ QuicTime::Delta receive_delta = receive_time.Subtract(
+ previous_packet_group_.last_receive_time);
+ // We assume that groups of packets are sent together as bursts (tagged
+ // with identical send times) because it is too computationally expensive
+ // to pace them out individually. The received_delta is then the total
+ // delta between the receipt of the last packet of the previous group, and
+ // the last packet of the current group. Assuming we are transmitting
+ // these bursts on average at the available bandwidth rate, there should be
+ // no change in overall spread (both deltas should be the same).
+ UpdateFilter(receive_delta, sent_delta);
+ }
+ // Save current as previous.
+ previous_packet_group_ = current_packet_group_;
+ previous_packet_group_.last_receive_time = receive_time;
+}
+
+void InterArrivalOveruseDetector::UpdateSendReceiveTimeOffset(
+ QuicTime::Delta offset) {
+ // Note the send and receive time can have a randomly large offset, however
+ // they are stable in relation to each other, hence no or extremely low clock
+ // drift relative to the duration of our stream.
+ if (offset.ToMicroseconds() < send_receive_offset_.ToMicroseconds()) {
+ send_receive_offset_ = offset;
+ }
+ estimated_congestion_delay_ = offset.Subtract(send_receive_offset_);
+}
+
+BandwidthUsage InterArrivalOveruseDetector::GetState(
+ QuicTime::Delta* estimated_congestion_delay) {
+ *estimated_congestion_delay = estimated_congestion_delay_;
+ int64 sigma_delta = sqrt(static_cast<double>(delta_variance_));
+ DetectSlope(sigma_delta);
+ DetectDrift(sigma_delta);
+ return std::max(slope_estimate_, delta_estimate_);
+}
+
+void InterArrivalOveruseDetector::UpdateFilter(QuicTime::Delta received_delta,
+ QuicTime::Delta sent_delta) {
+ ++num_of_deltas_;
+ QuicTime::Delta time_diff = received_delta.Subtract(sent_delta);
+ UpdateDeltaEstimate(time_diff);
+ accumulated_deltas_ = accumulated_deltas_.Add(time_diff);
+}
+
+void InterArrivalOveruseDetector::UpdateDeltaEstimate(
+ QuicTime::Delta residual) {
+ DCHECK_EQ(1, kBetaDenominator - kBetaNumerator);
+ int64 residual_us = residual.ToMicroseconds();
+ delta_mean_ =
+ (kBetaNumerator * delta_mean_ + residual_us) / kBetaDenominator;
+ delta_variance_ =
+ (kBetaNumerator * delta_variance_ +
+ (delta_mean_ - residual_us) * (delta_mean_ - residual_us)) /
+ kBetaDenominator;
+
+ if (delta_variance_ < kMinVarianceDelta) {
+ delta_variance_ = kMinVarianceDelta;
+ }
+}
+
+void InterArrivalOveruseDetector::DetectDrift(int64 sigma_delta) {
+ // We have 2 drift detectors. The accumulate of deltas and the absolute time
+ // differences.
+ if (num_of_deltas_ < kMinSamplesBeforeDetect) {
+ return;
+ }
+ if (delta_overuse_counter_ > 0 &&
+ accumulated_deltas_.ToMicroseconds() > kThresholdAccumulatedDeltasUs) {
+ if (delta_estimate_ != kBandwidthDraining) {
+ DLOG(INFO) << "Bandwidth estimate drift: Draining buffer(s) "
+ << accumulated_deltas_.ToMilliseconds() << " ms";
+ delta_estimate_ = kBandwidthDraining;
+ }
+ return;
+ }
+ if ((sigma_delta * kDetectTimeDiffStandardDeviation >
+ estimated_congestion_delay_.ToMicroseconds()) &&
+ (sigma_delta * kDetectDriftStandardDeviation >
+ abs(accumulated_deltas_.ToMicroseconds()))) {
+ if (delta_estimate_ != kBandwidthSteady) {
+ DLOG(INFO) << "Bandwidth estimate drift: Steady"
+ << " mean:" << delta_mean_
+ << " sigma:" << sigma_delta
+ << " offset:" << send_receive_offset_.ToMicroseconds()
+ << " delta:" << estimated_congestion_delay_.ToMicroseconds()
+ << " drift:" << accumulated_deltas_.ToMicroseconds();
+ delta_estimate_ = kBandwidthSteady;
+ // Reset drift counter.
+ accumulated_deltas_ = QuicTime::Delta::Zero();
+ delta_overuse_counter_ = 0;
+ }
+ return;
+ }
+ if (accumulated_deltas_.ToMicroseconds() > 0) {
+ if (delta_estimate_ != kBandwidthOverUsing) {
+ ++delta_overuse_counter_;
+ DLOG(INFO) << "Bandwidth estimate drift: Over using"
+ << " mean:" << delta_mean_
+ << " sigma:" << sigma_delta
+ << " offset:" << send_receive_offset_.ToMicroseconds()
+ << " delta:" << estimated_congestion_delay_.ToMicroseconds()
+ << " drift:" << accumulated_deltas_.ToMicroseconds();
+ delta_estimate_ = kBandwidthOverUsing;
+ }
+ } else {
+ if (delta_estimate_ != kBandwidthUnderUsing) {
+ --delta_overuse_counter_;
+ DLOG(INFO) << "Bandwidth estimate drift: Under using"
+ << " mean:" << delta_mean_
+ << " sigma:" << sigma_delta
+ << " offset:" << send_receive_offset_.ToMicroseconds()
+ << " delta:" << estimated_congestion_delay_.ToMicroseconds()
+ << " drift:" << accumulated_deltas_.ToMicroseconds();
+ delta_estimate_ = kBandwidthUnderUsing;
+ }
+ // Adding decay of negative accumulated_deltas_ since it could be caused by
+ // a starting with full buffers. This way we will always converge to 0.
+ accumulated_deltas_ = accumulated_deltas_.Add(
+ QuicTime::Delta::FromMicroseconds(sigma_delta >> 3));
+ }
+}
+
+void InterArrivalOveruseDetector::DetectSlope(int64 sigma_delta) {
+ // We use the mean change since it has a constant expected mean 0
+ // regardless of number of packets and spread. It is also safe to use during
+ // packet loss, since a lost packet only results in a missed filter update
+ // not a drift.
+ if (num_of_deltas_ < kMinSamplesBeforeDetect) {
+ return;
+ }
+ if (slope_overuse_counter_ > 0 && delta_mean_ > 0) {
+ if (slope_estimate_ != kBandwidthDraining) {
+ DLOG(INFO) << "Bandwidth estimate slope: Draining buffer(s)";
+ }
+ slope_estimate_ = kBandwidthDraining;
+ return;
+ }
+ if (sigma_delta > abs(delta_mean_) * kDetectSlopeFactor) {
+ if (slope_estimate_ != kBandwidthSteady) {
+ DLOG(INFO) << "Bandwidth estimate slope: Steady"
+ << " mean:" << delta_mean_
+ << " sigma:" << sigma_delta;
+ slope_overuse_counter_ = 0;
+ slope_estimate_ = kBandwidthSteady;
+ }
+ return;
+ }
+ if (delta_mean_ > 0) {
+ if (slope_estimate_ != kBandwidthOverUsing) {
+ ++slope_overuse_counter_;
+ DLOG(INFO) << "Bandwidth estimate slope: Over using"
+ << " mean:" << delta_mean_
+ << " sigma:" << sigma_delta;
+ slope_estimate_ = kBandwidthOverUsing;
+ }
+ } else {
+ if (slope_estimate_ != kBandwidthUnderUsing) {
+ --slope_overuse_counter_;
+ DLOG(INFO) << "Bandwidth estimate slope: Under using"
+ << " mean:" << delta_mean_
+ << " sigma:" << sigma_delta;
+ slope_estimate_ = kBandwidthUnderUsing;
+ }
+ }
+}
+
+} // namespace net