Home | History | Annotate | Download | only in congestion_control
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/quic/congestion_control/inter_arrival_overuse_detector.h"
      6 
      7 #include <math.h>
      8 #include <stdlib.h>
      9 
     10 #include <algorithm>
     11 
     12 // Initial noise variance, equal to a standard deviation of 1 millisecond.
     13 static const float kInitialVarianceNoise = 1000000.0;
     14 
     15 // Minimum variance of the time delta.
     16 static const int kMinVarianceDelta = 10000;
     17 
     18 // Threshold for accumulated delta.
     19 static const int kThresholdAccumulatedDeltasUs = 1000;
     20 
     21 // Same as above, described as numerator and denominator.
     22 static const int kBetaNumerator = 49;
     23 static const int kBetaDenominator = 50;
     24 
     25 // Trigger a signal when the accumulated time drift is larger than
     26 // 5 x standard deviation.
     27 // A lower value triggers earlier with more false detect as a side effect.
     28 static const int kDetectDriftStandardDeviation = 5;
     29 
     30 // Trigger an overuse when the time difference between send time and receive
     31 // is larger than 7 x standard deviation.
     32 // A lower value triggers earlier with more false detect as a side effect.
     33 static const float kDetectTimeDiffStandardDeviation = 7;
     34 
     35 // Trigger an overuse when the mean of the time difference diverges too far
     36 // from 0.
     37 // A higher value trigger earlier with more false detect as a side effect.
     38 static const int kDetectSlopeFactor = 14;
     39 
     40 // We need to get some initial statistics before the detection can start.
     41 static const int kMinSamplesBeforeDetect = 10;
     42 
     43 namespace net {
     44 
     45 InterArrivalOveruseDetector::InterArrivalOveruseDetector()
     46     : last_sequence_number_(0),
     47       num_of_deltas_(0),
     48       accumulated_deltas_(QuicTime::Delta::Zero()),
     49       delta_mean_(0.0),
     50       delta_variance_(kInitialVarianceNoise),
     51       delta_overuse_counter_(0),
     52       delta_estimate_(kBandwidthSteady),
     53       slope_overuse_counter_(0),
     54       slope_estimate_(kBandwidthSteady),
     55       send_receive_offset_(QuicTime::Delta::Infinite()),
     56       estimated_congestion_delay_(QuicTime::Delta::Zero()) {
     57 }
     58 
     59 void InterArrivalOveruseDetector::OnAcknowledgedPacket(
     60     QuicPacketSequenceNumber sequence_number,
     61     QuicTime send_time,
     62     bool last_of_send_time,
     63     QuicTime receive_time) {
     64   if (last_sequence_number_ >= sequence_number) {
     65     // This is an old packet and should be ignored.  Note that we are called
     66     // with a full 64 bit sequence number, even if the wire format may only
     67     // convey some low-order bits of that number.
     68     DVLOG(1) << "Skip old packet";
     69     return;
     70   }
     71 
     72   last_sequence_number_ = sequence_number;
     73 
     74   if (current_packet_group_.send_time != send_time) {
     75     // First value in this group. If the last packet of a group is lost we
     76     // overwrite the old value and start over with a new measurement.
     77     current_packet_group_.send_time = send_time;
     78     // The receive_time value might not be first in a packet burst if that
     79     // packet was lost, however we will still use it in this calculation.
     80     UpdateSendReceiveTimeOffset(receive_time.Subtract(send_time));
     81   }
     82   if (!last_of_send_time) {
     83     // We expect more packet with this send time.
     84     return;
     85   }
     86   // First packet of a later group, the previous group sample is ready.
     87   if (previous_packet_group_.send_time.IsInitialized()) {
     88     QuicTime::Delta sent_delta = send_time.Subtract(
     89         previous_packet_group_.send_time);
     90     QuicTime::Delta receive_delta = receive_time.Subtract(
     91         previous_packet_group_.last_receive_time);
     92     // We assume that groups of packets are sent together as bursts (tagged
     93     // with identical send times) because it is too computationally expensive
     94     // to pace them out individually.  The received_delta is then the total
     95     // delta between the receipt of the last packet of the previous group, and
     96     // the last packet of the current group.  Assuming we are transmitting
     97     // these bursts on average at the available bandwidth rate, there should be
     98     // no change in overall spread (both deltas should be the same).
     99     UpdateFilter(receive_delta, sent_delta);
    100   }
    101   // Save current as previous.
    102   previous_packet_group_ = current_packet_group_;
    103   previous_packet_group_.last_receive_time = receive_time;
    104 }
    105 
    106 void InterArrivalOveruseDetector::UpdateSendReceiveTimeOffset(
    107     QuicTime::Delta offset) {
    108   // Note the send and receive time can have a randomly large offset, however
    109   // they are stable in relation to each other, hence no or extremely low clock
    110   // drift relative to the duration of our stream.
    111   if (offset.ToMicroseconds() < send_receive_offset_.ToMicroseconds()) {
    112     send_receive_offset_ = offset;
    113   }
    114   estimated_congestion_delay_ = offset.Subtract(send_receive_offset_);
    115 }
    116 
    117 BandwidthUsage InterArrivalOveruseDetector::GetState(
    118     QuicTime::Delta* estimated_congestion_delay) {
    119   *estimated_congestion_delay = estimated_congestion_delay_;
    120   int64 sigma_delta = sqrt(static_cast<double>(delta_variance_));
    121   DetectSlope(sigma_delta);
    122   DetectDrift(sigma_delta);
    123   return std::max(slope_estimate_, delta_estimate_);
    124 }
    125 
    126 void InterArrivalOveruseDetector::UpdateFilter(QuicTime::Delta received_delta,
    127                                                QuicTime::Delta sent_delta) {
    128   ++num_of_deltas_;
    129   QuicTime::Delta time_diff = received_delta.Subtract(sent_delta);
    130   UpdateDeltaEstimate(time_diff);
    131   accumulated_deltas_ = accumulated_deltas_.Add(time_diff);
    132 }
    133 
    134 void InterArrivalOveruseDetector::UpdateDeltaEstimate(
    135     QuicTime::Delta residual) {
    136   DCHECK_EQ(1, kBetaDenominator - kBetaNumerator);
    137   int64 residual_us = residual.ToMicroseconds();
    138   delta_mean_ =
    139       (kBetaNumerator * delta_mean_ + residual_us) / kBetaDenominator;
    140   delta_variance_ =
    141       (kBetaNumerator * delta_variance_ +
    142       (delta_mean_ - residual_us) * (delta_mean_ - residual_us)) /
    143       kBetaDenominator;
    144 
    145   if (delta_variance_ < kMinVarianceDelta) {
    146     delta_variance_ = kMinVarianceDelta;
    147   }
    148 }
    149 
    150 void InterArrivalOveruseDetector::DetectDrift(int64 sigma_delta) {
    151   // We have 2 drift detectors. The accumulate of deltas and the absolute time
    152   // differences.
    153   if (num_of_deltas_ < kMinSamplesBeforeDetect) {
    154     return;
    155   }
    156   if (delta_overuse_counter_ > 0 &&
    157       accumulated_deltas_.ToMicroseconds() > kThresholdAccumulatedDeltasUs) {
    158     if (delta_estimate_ != kBandwidthDraining) {
    159       DVLOG(1) << "Bandwidth estimate drift: Draining buffer(s) "
    160                  << accumulated_deltas_.ToMilliseconds() << " ms";
    161       delta_estimate_ = kBandwidthDraining;
    162     }
    163     return;
    164   }
    165   if ((sigma_delta * kDetectTimeDiffStandardDeviation >
    166           estimated_congestion_delay_.ToMicroseconds()) &&
    167       (sigma_delta * kDetectDriftStandardDeviation >
    168           abs(accumulated_deltas_.ToMicroseconds()))) {
    169     if (delta_estimate_ != kBandwidthSteady) {
    170       DVLOG(1) << "Bandwidth estimate drift: Steady"
    171                  << " mean:" << delta_mean_
    172                  << " sigma:" << sigma_delta
    173                  << " offset:" << send_receive_offset_.ToMicroseconds()
    174                  << " delta:" << estimated_congestion_delay_.ToMicroseconds()
    175                  << " drift:" << accumulated_deltas_.ToMicroseconds();
    176       delta_estimate_ = kBandwidthSteady;
    177       // Reset drift counter.
    178       accumulated_deltas_ = QuicTime::Delta::Zero();
    179       delta_overuse_counter_ = 0;
    180     }
    181     return;
    182   }
    183   if (accumulated_deltas_.ToMicroseconds() > 0) {
    184     if (delta_estimate_ != kBandwidthOverUsing) {
    185       ++delta_overuse_counter_;
    186       DVLOG(1) << "Bandwidth estimate drift: Over using"
    187                  << " mean:" << delta_mean_
    188                  << " sigma:" << sigma_delta
    189                  << " offset:" << send_receive_offset_.ToMicroseconds()
    190                  << " delta:" << estimated_congestion_delay_.ToMicroseconds()
    191                  << " drift:" << accumulated_deltas_.ToMicroseconds();
    192       delta_estimate_ = kBandwidthOverUsing;
    193     }
    194   } else {
    195     if (delta_estimate_ != kBandwidthUnderUsing) {
    196       --delta_overuse_counter_;
    197       DVLOG(1) << "Bandwidth estimate drift: Under using"
    198                  << " mean:" << delta_mean_
    199                  << " sigma:" << sigma_delta
    200                  << " offset:" << send_receive_offset_.ToMicroseconds()
    201                  << " delta:" << estimated_congestion_delay_.ToMicroseconds()
    202                  << " drift:" << accumulated_deltas_.ToMicroseconds();
    203       delta_estimate_ = kBandwidthUnderUsing;
    204     }
    205     // Adding decay of negative accumulated_deltas_ since it could be caused by
    206     // a starting with full buffers. This way we will always converge to 0.
    207     accumulated_deltas_ = accumulated_deltas_.Add(
    208         QuicTime::Delta::FromMicroseconds(sigma_delta >> 3));
    209   }
    210 }
    211 
    212 void InterArrivalOveruseDetector::DetectSlope(int64 sigma_delta) {
    213   // We use the mean change since it has a constant expected mean 0
    214   // regardless of number of packets and spread. It is also safe to use during
    215   // packet loss, since a lost packet only results in a missed filter update
    216   // not a drift.
    217   if (num_of_deltas_ < kMinSamplesBeforeDetect) {
    218     return;
    219   }
    220   if (slope_overuse_counter_ > 0 && delta_mean_ > 0) {
    221     if (slope_estimate_ != kBandwidthDraining) {
    222       DVLOG(1) << "Bandwidth estimate slope: Draining buffer(s)";
    223     }
    224     slope_estimate_ = kBandwidthDraining;
    225     return;
    226   }
    227   if (sigma_delta > abs(delta_mean_) * kDetectSlopeFactor) {
    228     if (slope_estimate_ != kBandwidthSteady) {
    229       DVLOG(1) << "Bandwidth estimate slope: Steady"
    230                  << " mean:" << delta_mean_
    231                  << " sigma:" << sigma_delta;
    232       slope_overuse_counter_ = 0;
    233       slope_estimate_ = kBandwidthSteady;
    234     }
    235     return;
    236   }
    237   if (delta_mean_ > 0) {
    238     if (slope_estimate_ != kBandwidthOverUsing) {
    239       ++slope_overuse_counter_;
    240       DVLOG(1) << "Bandwidth estimate slope: Over using"
    241                  << " mean:" << delta_mean_
    242                  << " sigma:" << sigma_delta;
    243       slope_estimate_ = kBandwidthOverUsing;
    244     }
    245   } else {
    246     if (slope_estimate_ != kBandwidthUnderUsing) {
    247       --slope_overuse_counter_;
    248       DVLOG(1) << "Bandwidth estimate slope: Under using"
    249                  << " mean:" << delta_mean_
    250                  << " sigma:" << sigma_delta;
    251       slope_estimate_ = kBandwidthUnderUsing;
    252     }
    253   }
    254 }
    255 
    256 }  // namespace net
    257