summaryrefslogtreecommitdiff
path: root/chromium/net/quic/congestion_control/inter_arrival_sender.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/quic/congestion_control/inter_arrival_sender.cc')
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_sender.cc505
1 files changed, 505 insertions, 0 deletions
diff --git a/chromium/net/quic/congestion_control/inter_arrival_sender.cc b/chromium/net/quic/congestion_control/inter_arrival_sender.cc
new file mode 100644
index 00000000000..1aa7ab90e9b
--- /dev/null
+++ b/chromium/net/quic/congestion_control/inter_arrival_sender.cc
@@ -0,0 +1,505 @@
+// 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_sender.h"
+
+namespace net {
+
+namespace {
+const int64 kProbeBitrateKBytesPerSecond = 1200; // 9.6 Mbit/s
+const float kPacketLossBitrateReduction = 0.7f;
+const float kUncertainSafetyMargin = 0.7f;
+const float kMaxBitrateReduction = 0.9f;
+const float kMinBitrateReduction = 0.05f;
+const uint64 kMinBitrateKbit = 10;
+const int kInitialRttMs = 60; // At a typical RTT 60 ms.
+const float kAlpha = 0.125f;
+const float kOneMinusAlpha = 1 - kAlpha;
+
+static const int kBitrateSmoothingPeriodMs = 1000;
+static const int kMinBitrateSmoothingPeriodMs = 500;
+
+} // namespace
+
+InterArrivalSender::InterArrivalSender(const QuicClock* clock)
+ : probing_(true),
+ current_bandwidth_(QuicBandwidth::Zero()),
+ smoothed_rtt_(QuicTime::Delta::Zero()),
+ channel_estimator_(new ChannelEstimator()),
+ bitrate_ramp_up_(new InterArrivalBitrateRampUp(clock)),
+ overuse_detector_(new InterArrivalOveruseDetector()),
+ probe_(new InterArrivalProbe()),
+ state_machine_(new InterArrivalStateMachine(clock)),
+ paced_sender_(new PacedSender(QuicBandwidth::FromKBytesPerSecond(
+ kProbeBitrateKBytesPerSecond))),
+ accumulated_number_of_lost_packets_(0),
+ bandwidth_usage_state_(kBandwidthSteady),
+ back_down_time_(QuicTime::Zero()),
+ back_down_bandwidth_(QuicBandwidth::Zero()),
+ back_down_congestion_delay_(QuicTime::Delta::Zero()) {
+}
+
+InterArrivalSender::~InterArrivalSender() {
+}
+
+// TODO(pwestin): this is really inefficient (4% CPU on the GFE loadtest).
+// static
+QuicBandwidth InterArrivalSender::CalculateSentBandwidth(
+ const SendAlgorithmInterface::SentPacketsMap& sent_packets_map,
+ QuicTime feedback_receive_time) {
+ const QuicTime::Delta kBitrateSmoothingPeriod =
+ QuicTime::Delta::FromMilliseconds(kBitrateSmoothingPeriodMs);
+ const QuicTime::Delta kMinBitrateSmoothingPeriod =
+ QuicTime::Delta::FromMilliseconds(kMinBitrateSmoothingPeriodMs);
+
+ QuicByteCount sum_bytes_sent = 0;
+
+ // Sum packet from new until they are kBitrateSmoothingPeriod old.
+ SendAlgorithmInterface::SentPacketsMap::const_reverse_iterator history_rit =
+ sent_packets_map.rbegin();
+
+ QuicTime::Delta max_diff = QuicTime::Delta::Zero();
+ for (; history_rit != sent_packets_map.rend(); ++history_rit) {
+ QuicTime::Delta diff =
+ feedback_receive_time.Subtract(history_rit->second->SendTimestamp());
+ if (diff > kBitrateSmoothingPeriod) {
+ break;
+ }
+ sum_bytes_sent += history_rit->second->BytesSent();
+ max_diff = diff;
+ }
+ if (max_diff < kMinBitrateSmoothingPeriod) {
+ // No estimate.
+ return QuicBandwidth::Zero();
+ }
+ return QuicBandwidth::FromBytesAndTimeDelta(sum_bytes_sent, max_diff);
+}
+
+void InterArrivalSender::OnIncomingQuicCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& feedback,
+ QuicTime feedback_receive_time,
+ const SentPacketsMap& sent_packets) {
+ DCHECK(feedback.type == kInterArrival);
+
+ if (feedback.type != kInterArrival) {
+ return;
+ }
+
+ QuicBandwidth sent_bandwidth = CalculateSentBandwidth(sent_packets,
+ feedback_receive_time);
+
+ TimeMap::const_iterator received_it;
+ for (received_it = feedback.inter_arrival.received_packet_times.begin();
+ received_it != feedback.inter_arrival.received_packet_times.end();
+ ++received_it) {
+ QuicPacketSequenceNumber sequence_number = received_it->first;
+
+ SentPacketsMap::const_iterator sent_it = sent_packets.find(sequence_number);
+ if (sent_it == sent_packets.end()) {
+ // Too old data; ignore and move forward.
+ DLOG(INFO) << "Too old feedback move forward, sequence_number:"
+ << sequence_number;
+ continue;
+ }
+ QuicTime time_received = received_it->second;
+ QuicTime time_sent = sent_it->second->SendTimestamp();
+ QuicByteCount bytes_sent = sent_it->second->BytesSent();
+
+ channel_estimator_->OnAcknowledgedPacket(
+ sequence_number, bytes_sent, time_sent, time_received);
+ if (probing_) {
+ probe_->OnIncomingFeedback(
+ sequence_number, bytes_sent, time_sent, time_received);
+ } else {
+ bool last_of_send_time = false;
+ SentPacketsMap::const_iterator next_sent_it = ++sent_it;
+ if (next_sent_it == sent_packets.end()) {
+ // No more sent packets; hence this must be the last.
+ last_of_send_time = true;
+ } else {
+ if (time_sent != next_sent_it->second->SendTimestamp()) {
+ // Next sent packet have a different send time.
+ last_of_send_time = true;
+ }
+ }
+ overuse_detector_->OnAcknowledgedPacket(
+ sequence_number, time_sent, last_of_send_time, time_received);
+ }
+ }
+ if (probing_) {
+ probing_ = ProbingPhase(feedback_receive_time);
+ return;
+ }
+
+ bool packet_loss_event = false;
+ if (accumulated_number_of_lost_packets_ !=
+ feedback.inter_arrival.accumulated_number_of_lost_packets) {
+ accumulated_number_of_lost_packets_ =
+ feedback.inter_arrival.accumulated_number_of_lost_packets;
+ packet_loss_event = true;
+ }
+ InterArrivalState state = state_machine_->GetInterArrivalState();
+
+ if (state == kInterArrivalStatePacketLoss ||
+ state == kInterArrivalStateCompetingTcpFLow) {
+ if (packet_loss_event) {
+ if (!state_machine_->PacketLossEvent()) {
+ // Less than one RTT since last PacketLossEvent.
+ return;
+ }
+ EstimateBandwidthAfterLossEvent(feedback_receive_time);
+ } else {
+ EstimateNewBandwidth(feedback_receive_time, sent_bandwidth);
+ }
+ return;
+ }
+ EstimateDelayBandwidth(feedback_receive_time, sent_bandwidth);
+}
+
+bool InterArrivalSender::ProbingPhase(QuicTime feedback_receive_time) {
+ QuicBandwidth available_channel_estimate = QuicBandwidth::Zero();
+ if (!probe_->GetEstimate(&available_channel_estimate)) {
+ // Continue probing phase.
+ return true;
+ }
+ QuicBandwidth channel_estimate = QuicBandwidth::Zero();
+ ChannelEstimateState channel_estimator_state =
+ channel_estimator_->GetChannelEstimate(&channel_estimate);
+
+ QuicBandwidth new_rate =
+ available_channel_estimate.Scale(kUncertainSafetyMargin);
+
+ switch (channel_estimator_state) {
+ case kChannelEstimateUnknown:
+ channel_estimate = available_channel_estimate;
+ break;
+ case kChannelEstimateUncertain:
+ channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin);
+ break;
+ case kChannelEstimateGood:
+ // Do nothing.
+ break;
+ }
+ new_rate = std::max(new_rate,
+ QuicBandwidth::FromKBitsPerSecond(kMinBitrateKbit));
+
+ bitrate_ramp_up_->Reset(new_rate, available_channel_estimate,
+ channel_estimate);
+
+ current_bandwidth_ = new_rate;
+ paced_sender_->UpdateBandwidthEstimate(feedback_receive_time, new_rate);
+ DLOG(INFO) << "Probe result; new rate:"
+ << new_rate.ToKBitsPerSecond() << " Kbits/s "
+ << " available estimate:"
+ << available_channel_estimate.ToKBitsPerSecond() << " Kbits/s "
+ << " channel estimate:"
+ << channel_estimate.ToKBitsPerSecond() << " Kbits/s ";
+ return false;
+}
+
+void InterArrivalSender::OnIncomingAck(
+ QuicPacketSequenceNumber /*acked_sequence_number*/,
+ QuicByteCount acked_bytes,
+ QuicTime::Delta rtt) {
+ // RTT can't be negative.
+ DCHECK_LE(0, rtt.ToMicroseconds());
+
+ if (probing_) {
+ probe_->OnAcknowledgedPacket(acked_bytes);
+ }
+
+ if (rtt.IsInfinite()) {
+ return;
+ }
+
+ if (smoothed_rtt_.IsZero()) {
+ smoothed_rtt_ = rtt;
+ } else {
+ smoothed_rtt_ = QuicTime::Delta::FromMicroseconds(
+ kOneMinusAlpha * smoothed_rtt_.ToMicroseconds() +
+ kAlpha * rtt.ToMicroseconds());
+ }
+ state_machine_->set_rtt(smoothed_rtt_);
+}
+
+void InterArrivalSender::OnIncomingLoss(QuicTime ack_receive_time) {
+ // Packet loss was reported.
+ if (!probing_) {
+ if (!state_machine_->PacketLossEvent()) {
+ // Less than one RTT since last PacketLossEvent.
+ return;
+ }
+ // Calculate new pace rate.
+ EstimateBandwidthAfterLossEvent(ack_receive_time);
+ }
+}
+
+void InterArrivalSender::SentPacket(QuicTime sent_time,
+ QuicPacketSequenceNumber sequence_number,
+ QuicByteCount bytes,
+ Retransmission /*retransmit*/) {
+ if (probing_) {
+ probe_->OnSentPacket(bytes);
+ }
+ paced_sender_->SentPacket(sent_time, bytes);
+}
+
+void InterArrivalSender::AbandoningPacket(
+ QuicPacketSequenceNumber /*sequence_number*/,
+ QuicByteCount abandoned_bytes) {
+ // TODO(pwestin): use for out outer_congestion_window_ logic.
+ if (probing_) {
+ probe_->OnAcknowledgedPacket(abandoned_bytes);
+ }
+}
+
+QuicTime::Delta InterArrivalSender::TimeUntilSend(
+ QuicTime now,
+ Retransmission /*retransmit*/,
+ HasRetransmittableData has_retransmittable_data,
+ IsHandshake /* handshake */) {
+ // TODO(pwestin): implement outer_congestion_window_ logic.
+ QuicTime::Delta outer_window = QuicTime::Delta::Zero();
+
+ if (probing_) {
+ if (has_retransmittable_data == HAS_RETRANSMITTABLE_DATA &&
+ probe_->GetAvailableCongestionWindow() == 0) {
+ outer_window = QuicTime::Delta::Infinite();
+ }
+ }
+ return paced_sender_->TimeUntilSend(now, outer_window);
+}
+
+void InterArrivalSender::EstimateDelayBandwidth(QuicTime feedback_receive_time,
+ QuicBandwidth sent_bandwidth) {
+ QuicTime::Delta estimated_congestion_delay = QuicTime::Delta::Zero();
+ BandwidthUsage new_bandwidth_usage_state =
+ overuse_detector_->GetState(&estimated_congestion_delay);
+
+ switch (new_bandwidth_usage_state) {
+ case kBandwidthDraining:
+ case kBandwidthUnderUsing:
+ // Hold our current bitrate.
+ break;
+ case kBandwidthOverUsing:
+ if (!state_machine_->IncreasingDelayEvent()) {
+ // Less than one RTT since last IncreasingDelayEvent.
+ return;
+ }
+ EstimateBandwidthAfterDelayEvent(feedback_receive_time,
+ estimated_congestion_delay);
+ break;
+ case kBandwidthSteady:
+ // Calculate new pace rate.
+ if (bandwidth_usage_state_ == kBandwidthDraining ||
+ bandwidth_usage_state_ == kBandwidthOverUsing) {
+ EstimateNewBandwidthAfterDraining(feedback_receive_time,
+ estimated_congestion_delay);
+ } else {
+ EstimateNewBandwidth(feedback_receive_time, sent_bandwidth);
+ }
+ break;
+ }
+ bandwidth_usage_state_ = new_bandwidth_usage_state;
+}
+
+QuicBandwidth InterArrivalSender::BandwidthEstimate() {
+ return current_bandwidth_;
+}
+
+QuicTime::Delta InterArrivalSender::SmoothedRtt() {
+ if (smoothed_rtt_.IsZero()) {
+ return QuicTime::Delta::FromMilliseconds(kInitialRttMs);
+ }
+ return smoothed_rtt_;
+}
+
+QuicTime::Delta InterArrivalSender::RetransmissionDelay() {
+ // TODO(pwestin): Calculate and return retransmission delay.
+ // Use 2 * the smoothed RTT for now.
+ return smoothed_rtt_.Add(smoothed_rtt_);
+}
+
+void InterArrivalSender::EstimateNewBandwidth(QuicTime feedback_receive_time,
+ QuicBandwidth sent_bandwidth) {
+ QuicBandwidth new_bandwidth = bitrate_ramp_up_->GetNewBitrate(sent_bandwidth);
+ if (current_bandwidth_ == new_bandwidth) {
+ return;
+ }
+ current_bandwidth_ = new_bandwidth;
+ state_machine_->IncreaseBitrateDecision();
+
+ QuicBandwidth channel_estimate = QuicBandwidth::Zero();
+ ChannelEstimateState channel_estimator_state =
+ channel_estimator_->GetChannelEstimate(&channel_estimate);
+
+ if (channel_estimator_state == kChannelEstimateGood) {
+ bitrate_ramp_up_->UpdateChannelEstimate(channel_estimate);
+ }
+ paced_sender_->UpdateBandwidthEstimate(feedback_receive_time,
+ current_bandwidth_);
+ DLOG(INFO) << "New bandwidth estimate in steady state:"
+ << current_bandwidth_.ToKBitsPerSecond()
+ << " Kbits/s";
+}
+
+// Did we drain the network buffers in our expected pace?
+void InterArrivalSender::EstimateNewBandwidthAfterDraining(
+ QuicTime feedback_receive_time,
+ QuicTime::Delta estimated_congestion_delay) {
+ if (current_bandwidth_ > back_down_bandwidth_) {
+ // Do nothing, our current bandwidth is higher than our bandwidth at the
+ // previous back down.
+ DLOG(INFO) << "Current bandwidth estimate is higher than before draining";
+ return;
+ }
+ if (estimated_congestion_delay >= back_down_congestion_delay_) {
+ // Do nothing, our estimated delay have increased.
+ DLOG(INFO) << "Current delay estimate is higher than before draining";
+ return;
+ }
+ DCHECK(back_down_time_.IsInitialized());
+ QuicTime::Delta buffer_reduction =
+ back_down_congestion_delay_.Subtract(estimated_congestion_delay);
+ QuicTime::Delta elapsed_time =
+ feedback_receive_time.Subtract(back_down_time_).Subtract(SmoothedRtt());
+
+ QuicBandwidth new_estimate = QuicBandwidth::Zero();
+ if (buffer_reduction >= elapsed_time) {
+ // We have drained more than the elapsed time... go back to our old rate.
+ new_estimate = back_down_bandwidth_;
+ } else {
+ float fraction_of_rate =
+ static_cast<float>(buffer_reduction.ToMicroseconds()) /
+ elapsed_time.ToMicroseconds(); // < 1.0
+
+ QuicBandwidth draining_rate = back_down_bandwidth_.Scale(fraction_of_rate);
+ QuicBandwidth max_estimated_draining_rate =
+ back_down_bandwidth_.Subtract(current_bandwidth_);
+ if (draining_rate > max_estimated_draining_rate) {
+ // We drained faster than our old send rate, go back to our old rate.
+ new_estimate = back_down_bandwidth_;
+ } else {
+ // Use our drain rate and our kMinBitrateReduction to go to our
+ // new estimate.
+ new_estimate = std::max(current_bandwidth_,
+ current_bandwidth_.Add(draining_rate).Scale(
+ 1.0f - kMinBitrateReduction));
+ DLOG(INFO) << "Draining calculation; current rate:"
+ << current_bandwidth_.ToKBitsPerSecond() << " Kbits/s "
+ << "draining rate:"
+ << draining_rate.ToKBitsPerSecond() << " Kbits/s "
+ << "new estimate:"
+ << new_estimate.ToKBitsPerSecond() << " Kbits/s "
+ << " buffer reduction:"
+ << buffer_reduction.ToMicroseconds() << " us "
+ << " elapsed time:"
+ << elapsed_time.ToMicroseconds() << " us ";
+ }
+ }
+ if (new_estimate == current_bandwidth_) {
+ return;
+ }
+
+ QuicBandwidth channel_estimate = QuicBandwidth::Zero();
+ ChannelEstimateState channel_estimator_state =
+ channel_estimator_->GetChannelEstimate(&channel_estimate);
+
+ // TODO(pwestin): we need to analyze channel_estimate too.
+ switch (channel_estimator_state) {
+ case kChannelEstimateUnknown:
+ channel_estimate = current_bandwidth_;
+ break;
+ case kChannelEstimateUncertain:
+ channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin);
+ break;
+ case kChannelEstimateGood:
+ // Do nothing, estimate is accurate.
+ break;
+ }
+ bitrate_ramp_up_->Reset(new_estimate, back_down_bandwidth_, channel_estimate);
+ state_machine_->IncreaseBitrateDecision();
+ paced_sender_->UpdateBandwidthEstimate(feedback_receive_time, new_estimate);
+ current_bandwidth_ = new_estimate;
+ DLOG(INFO) << "New bandwidth estimate after draining:"
+ << new_estimate.ToKBitsPerSecond() << " Kbits/s";
+}
+
+void InterArrivalSender::EstimateBandwidthAfterDelayEvent(
+ QuicTime feedback_receive_time,
+ QuicTime::Delta estimated_congestion_delay) {
+ QuicByteCount estimated_byte_buildup =
+ current_bandwidth_.ToBytesPerPeriod(estimated_congestion_delay);
+
+ // To drain all build up buffer within one RTT we need to reduce the
+ // bitrate with the following.
+ // TODO(pwestin): this is a crude first implementation.
+ int64 draining_rate_per_rtt = (estimated_byte_buildup *
+ kNumMicrosPerSecond) / SmoothedRtt().ToMicroseconds();
+
+ float decrease_factor =
+ draining_rate_per_rtt / current_bandwidth_.ToBytesPerSecond();
+
+ decrease_factor = std::max(decrease_factor, kMinBitrateReduction);
+ decrease_factor = std::min(decrease_factor, kMaxBitrateReduction);
+ back_down_congestion_delay_ = estimated_congestion_delay;
+ QuicBandwidth new_target_bitrate =
+ current_bandwidth_.Scale(1.0f - decrease_factor);
+
+ // While in delay sensing mode send at least one packet per RTT.
+ QuicBandwidth min_delay_bitrate =
+ QuicBandwidth::FromBytesAndTimeDelta(kMaxPacketSize, SmoothedRtt());
+ new_target_bitrate = std::max(new_target_bitrate, min_delay_bitrate);
+
+ ResetCurrentBandwidth(feedback_receive_time, new_target_bitrate);
+
+ DLOG(INFO) << "New bandwidth estimate after delay event:"
+ << current_bandwidth_.ToKBitsPerSecond()
+ << " Kbits/s min delay bitrate:"
+ << min_delay_bitrate.ToKBitsPerSecond()
+ << " Kbits/s RTT:"
+ << SmoothedRtt().ToMicroseconds()
+ << " us";
+}
+
+void InterArrivalSender::EstimateBandwidthAfterLossEvent(
+ QuicTime feedback_receive_time) {
+ ResetCurrentBandwidth(feedback_receive_time,
+ current_bandwidth_.Scale(kPacketLossBitrateReduction));
+ DLOG(INFO) << "New bandwidth estimate after loss event:"
+ << current_bandwidth_.ToKBitsPerSecond()
+ << " Kbits/s";
+}
+
+void InterArrivalSender::ResetCurrentBandwidth(QuicTime feedback_receive_time,
+ QuicBandwidth new_rate) {
+ new_rate = std::max(new_rate,
+ QuicBandwidth::FromKBitsPerSecond(kMinBitrateKbit));
+ QuicBandwidth channel_estimate = QuicBandwidth::Zero();
+ ChannelEstimateState channel_estimator_state =
+ channel_estimator_->GetChannelEstimate(&channel_estimate);
+
+ switch (channel_estimator_state) {
+ case kChannelEstimateUnknown:
+ channel_estimate = current_bandwidth_;
+ break;
+ case kChannelEstimateUncertain:
+ channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin);
+ break;
+ case kChannelEstimateGood:
+ // Do nothing.
+ break;
+ }
+ back_down_time_ = feedback_receive_time;
+ back_down_bandwidth_ = current_bandwidth_;
+ bitrate_ramp_up_->Reset(new_rate, current_bandwidth_, channel_estimate);
+ if (new_rate != current_bandwidth_) {
+ current_bandwidth_ = new_rate;
+ paced_sender_->UpdateBandwidthEstimate(feedback_receive_time,
+ current_bandwidth_);
+ state_machine_->DecreaseBitrateDecision();
+ }
+}
+
+} // namespace net