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.
5 #include "net/quic/congestion_control/inter_arrival_overuse_detector.h"
12 // Initial noise variance, equal to a standard deviation of 1 millisecond.
13 static const float kInitialVarianceNoise = 1000000.0;
15 // Minimum variance of the time delta.
16 static const int kMinVarianceDelta = 10000;
18 // Threshold for accumulated delta.
19 static const int kThresholdAccumulatedDeltasUs = 1000;
21 // Same as above, described as numerator and denominator.
22 static const int kBetaNumerator = 49;
23 static const int kBetaDenominator = 50;
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;
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;
35 // Trigger an overuse when the mean of the time difference diverges too far
37 // A higher value trigger earlier with more false detect as a side effect.
38 static const int kDetectSlopeFactor = 14;
40 // We need to get some initial statistics before the detection can start.
41 static const int kMinSamplesBeforeDetect = 10;
45 InterArrivalOveruseDetector::InterArrivalOveruseDetector()
46 : last_sequence_number_(0),
48 accumulated_deltas_(QuicTime::Delta::Zero()),
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()) {
59 void InterArrivalOveruseDetector::OnAcknowledgedPacket(
60 QuicPacketSequenceNumber sequence_number,
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 DLOG(INFO) << "Skip old packet";
72 last_sequence_number_ = sequence_number;
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));
82 if (!last_of_send_time) {
83 // We expect more packet with this send time.
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);
101 // Save current as previous.
102 previous_packet_group_ = current_packet_group_;
103 previous_packet_group_.last_receive_time = receive_time;
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;
114 estimated_congestion_delay_ = offset.Subtract(send_receive_offset_);
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_);
126 void InterArrivalOveruseDetector::UpdateFilter(QuicTime::Delta received_delta,
127 QuicTime::Delta sent_delta) {
129 QuicTime::Delta time_diff = received_delta.Subtract(sent_delta);
130 UpdateDeltaEstimate(time_diff);
131 accumulated_deltas_ = accumulated_deltas_.Add(time_diff);
134 void InterArrivalOveruseDetector::UpdateDeltaEstimate(
135 QuicTime::Delta residual) {
136 DCHECK_EQ(1, kBetaDenominator - kBetaNumerator);
137 int64 residual_us = residual.ToMicroseconds();
139 (kBetaNumerator * delta_mean_ + residual_us) / kBetaDenominator;
141 (kBetaNumerator * delta_variance_ +
142 (delta_mean_ - residual_us) * (delta_mean_ - residual_us)) /
145 if (delta_variance_ < kMinVarianceDelta) {
146 delta_variance_ = kMinVarianceDelta;
150 void InterArrivalOveruseDetector::DetectDrift(int64 sigma_delta) {
151 // We have 2 drift detectors. The accumulate of deltas and the absolute time
153 if (num_of_deltas_ < kMinSamplesBeforeDetect) {
156 if (delta_overuse_counter_ > 0 &&
157 accumulated_deltas_.ToMicroseconds() > kThresholdAccumulatedDeltasUs) {
158 if (delta_estimate_ != kBandwidthDraining) {
159 DLOG(INFO) << "Bandwidth estimate drift: Draining buffer(s) "
160 << accumulated_deltas_.ToMilliseconds() << " ms";
161 delta_estimate_ = kBandwidthDraining;
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 DLOG(INFO) << "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;
183 if (accumulated_deltas_.ToMicroseconds() > 0) {
184 if (delta_estimate_ != kBandwidthOverUsing) {
185 ++delta_overuse_counter_;
186 DLOG(INFO) << "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;
195 if (delta_estimate_ != kBandwidthUnderUsing) {
196 --delta_overuse_counter_;
197 DLOG(INFO) << "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;
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));
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
217 if (num_of_deltas_ < kMinSamplesBeforeDetect) {
220 if (slope_overuse_counter_ > 0 && delta_mean_ > 0) {
221 if (slope_estimate_ != kBandwidthDraining) {
222 DLOG(INFO) << "Bandwidth estimate slope: Draining buffer(s)";
224 slope_estimate_ = kBandwidthDraining;
227 if (sigma_delta > abs(delta_mean_) * kDetectSlopeFactor) {
228 if (slope_estimate_ != kBandwidthSteady) {
229 DLOG(INFO) << "Bandwidth estimate slope: Steady"
230 << " mean:" << delta_mean_
231 << " sigma:" << sigma_delta;
232 slope_overuse_counter_ = 0;
233 slope_estimate_ = kBandwidthSteady;
237 if (delta_mean_ > 0) {
238 if (slope_estimate_ != kBandwidthOverUsing) {
239 ++slope_overuse_counter_;
240 DLOG(INFO) << "Bandwidth estimate slope: Over using"
241 << " mean:" << delta_mean_
242 << " sigma:" << sigma_delta;
243 slope_estimate_ = kBandwidthOverUsing;
246 if (slope_estimate_ != kBandwidthUnderUsing) {
247 --slope_overuse_counter_;
248 DLOG(INFO) << "Bandwidth estimate slope: Under using"
249 << " mean:" << delta_mean_
250 << " sigma:" << sigma_delta;
251 slope_estimate_ = kBandwidthUnderUsing;