| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/quic/congestion_control/hybrid_slow_start.h" | 5 #include "net/quic/congestion_control/hybrid_slow_start.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "net/quic/congestion_control/rtt_stats.h" | 9 #include "net/quic/congestion_control/rtt_stats.h" |
| 10 | 10 |
| 11 using std::max; | 11 using std::max; |
| 12 using std::min; | 12 using std::min; |
| 13 | 13 |
| 14 namespace net { | 14 namespace net { |
| 15 | 15 |
| 16 // Note(pwestin): the magic clamping numbers come from the original code in | 16 // Note(pwestin): the magic clamping numbers come from the original code in |
| 17 // tcp_cubic.c. | 17 // tcp_cubic.c. |
| 18 const int64 kHybridStartLowWindow = 16; | 18 const int64 kHybridStartLowWindow = 16; |
| 19 // Number of delay samples for detecting the increase of delay. | 19 // Number of delay samples for detecting the increase of delay. |
| 20 const int kHybridStartMinSamples = 8; | 20 const int kHybridStartMinSamples = 8; |
| 21 const int kHybridStartDelayFactorExp = 4; // 2^4 = 16 | 21 const int kHybridStartDelayFactorExp = 4; // 2^4 = 16 |
| 22 const int kHybridStartDelayMinThresholdUs = 2000; | 22 const int kHybridStartDelayMinThresholdUs = 2000; |
| 23 // TODO(ianswett): The original paper specifies 8, not 16ms. |
| 23 const int kHybridStartDelayMaxThresholdUs = 16000; | 24 const int kHybridStartDelayMaxThresholdUs = 16000; |
| 24 | 25 |
| 25 HybridSlowStart::HybridSlowStart(const QuicClock* clock) | 26 HybridSlowStart::HybridSlowStart(const QuicClock* clock) |
| 26 : clock_(clock), | 27 : clock_(clock), |
| 27 started_(false), | 28 started_(false), |
| 28 found_ack_train_(false), | 29 found_ack_train_(false), |
| 29 found_delay_(false), | 30 found_delay_(false), |
| 30 round_start_(QuicTime::Zero()), | 31 round_start_(QuicTime::Zero()), |
| 31 update_end_sequence_number_(true), | 32 update_end_sequence_number_(true), |
| 32 sender_end_sequence_number_(0), | 33 sender_end_sequence_number_(0), |
| 33 end_sequence_number_(0), | 34 end_sequence_number_(0), |
| 34 last_time_(QuicTime::Zero()), | 35 last_close_ack_pair_time_(QuicTime::Zero()), |
| 35 sample_count_(0), | 36 rtt_sample_count_(0), |
| 36 current_rtt_(QuicTime::Delta::Zero()) { | 37 current_min_rtt_(QuicTime::Delta::Zero()) { |
| 37 } | 38 } |
| 38 | 39 |
| 39 void HybridSlowStart::OnPacketAcked( | 40 void HybridSlowStart::OnPacketAcked( |
| 40 QuicPacketSequenceNumber acked_sequence_number, bool in_slow_start) { | 41 QuicPacketSequenceNumber acked_sequence_number, bool in_slow_start) { |
| 41 if (in_slow_start) { | 42 if (in_slow_start && IsEndOfRound(acked_sequence_number)) { |
| 42 if (IsEndOfRound(acked_sequence_number)) { | 43 Reset(sender_end_sequence_number_); |
| 43 Reset(sender_end_sequence_number_); | |
| 44 } | |
| 45 } | 44 } |
| 46 | 45 |
| 47 if (sender_end_sequence_number_ == acked_sequence_number) { | 46 if (sender_end_sequence_number_ == acked_sequence_number) { |
| 48 DVLOG(1) << "Start update end sequence number @" << acked_sequence_number; | 47 DVLOG(1) << "Start update end sequence number @" << acked_sequence_number; |
| 49 update_end_sequence_number_ = true; | 48 update_end_sequence_number_ = true; |
| 50 } | 49 } |
| 51 } | 50 } |
| 52 | 51 |
| 53 void HybridSlowStart::OnPacketSent(QuicPacketSequenceNumber sequence_number, | 52 void HybridSlowStart::OnPacketSent(QuicPacketSequenceNumber sequence_number, |
| 54 QuicByteCount available_send_window) { | 53 QuicByteCount available_send_window) { |
| 55 if (update_end_sequence_number_) { | 54 if (update_end_sequence_number_) { |
| 56 sender_end_sequence_number_ = sequence_number; | 55 sender_end_sequence_number_ = sequence_number; |
| 57 if (available_send_window == 0) { | 56 if (available_send_window == 0) { |
| 58 update_end_sequence_number_ = false; | 57 update_end_sequence_number_ = false; |
| 59 DVLOG(1) << "Stop update end sequence number @" << sequence_number; | 58 DVLOG(1) << "Stop update end sequence number @" << sequence_number; |
| 60 } | 59 } |
| 61 } | 60 } |
| 62 } | 61 } |
| 63 | 62 |
| 64 bool HybridSlowStart::ShouldExitSlowStart(const RttStats* rtt_stats, | 63 bool HybridSlowStart::ShouldExitSlowStart(const RttStats* rtt_stats, |
| 65 int64 congestion_window) { | 64 int64 congestion_window) { |
| 65 // Hybrid start triggers when cwnd is larger than some threshold. |
| 66 if (congestion_window < kHybridStartLowWindow) { | 66 if (congestion_window < kHybridStartLowWindow) { |
| 67 return false; | 67 return false; |
| 68 } | 68 } |
| 69 if (!started()) { | 69 if (!started_) { |
| 70 // Time to start the hybrid slow start. | 70 // Time to start the hybrid slow start. |
| 71 Reset(sender_end_sequence_number_); | 71 Reset(sender_end_sequence_number_); |
| 72 } | 72 } |
| 73 Update(rtt_stats->latest_rtt(), rtt_stats->min_rtt()); | 73 // TODO(ianswett): Merge UpdateAndMaybeExit into ShouldExitSlowStart in |
| 74 return Exit(); | 74 // another refactor CL, since it's only used here. |
| 75 return UpdateAndMaybeExit(rtt_stats->latest_rtt(), rtt_stats->min_rtt()); |
| 75 } | 76 } |
| 76 | 77 |
| 77 void HybridSlowStart::Restart() { | 78 void HybridSlowStart::Restart() { |
| 79 started_ = false; |
| 78 found_ack_train_ = false; | 80 found_ack_train_ = false; |
| 79 found_delay_ = false; | 81 found_delay_ = false; |
| 80 } | 82 } |
| 81 | 83 |
| 82 void HybridSlowStart::Reset(QuicPacketSequenceNumber end_sequence_number) { | 84 void HybridSlowStart::Reset(QuicPacketSequenceNumber end_sequence_number) { |
| 83 DVLOG(1) << "Reset hybrid slow start @" << end_sequence_number; | 85 DVLOG(1) << "Reset hybrid slow start @" << end_sequence_number; |
| 84 round_start_ = last_time_ = clock_->ApproximateNow(); | 86 round_start_ = last_close_ack_pair_time_ = clock_->ApproximateNow(); |
| 85 end_sequence_number_ = end_sequence_number; | 87 end_sequence_number_ = end_sequence_number; |
| 86 current_rtt_ = QuicTime::Delta::Zero(); | 88 current_min_rtt_ = QuicTime::Delta::Zero(); |
| 87 sample_count_ = 0; | 89 rtt_sample_count_ = 0; |
| 88 started_ = true; | 90 started_ = true; |
| 89 } | 91 } |
| 90 | 92 |
| 91 bool HybridSlowStart::IsEndOfRound(QuicPacketSequenceNumber ack) const { | 93 bool HybridSlowStart::IsEndOfRound(QuicPacketSequenceNumber ack) const { |
| 92 return end_sequence_number_ <= ack; | 94 return end_sequence_number_ <= ack; |
| 93 } | 95 } |
| 94 | 96 |
| 95 void HybridSlowStart::Update(QuicTime::Delta rtt, QuicTime::Delta delay_min) { | 97 bool HybridSlowStart::UpdateAndMaybeExit(QuicTime::Delta rtt, |
| 98 QuicTime::Delta min_rtt) { |
| 96 // The original code doesn't invoke this until we hit 16 packet per burst. | 99 // The original code doesn't invoke this until we hit 16 packet per burst. |
| 97 // Since the code handles lower than 16 grecefully and I removed that | 100 // Since the code handles lower than 16 gracefully I removed that limit. |
| 98 // limit. | |
| 99 if (found_ack_train_ || found_delay_) { | 101 if (found_ack_train_ || found_delay_) { |
| 100 return; | 102 return true; |
| 101 } | 103 } |
| 102 QuicTime current_time = clock_->ApproximateNow(); | 104 QuicTime current_time = clock_->ApproximateNow(); |
| 103 | 105 |
| 104 // First detection parameter - ack-train detection. | 106 // First detection parameter - ack-train detection. |
| 105 // Since slow start burst out packets we can indirectly estimate the inter- | 107 // Since slow start burst out packets we can indirectly estimate the inter- |
| 106 // arrival time by looking at the arrival time of the ACKs if the ACKs are | 108 // arrival time by looking at the arrival time of the ACKs if the ACKs are |
| 107 // spread out more then half the minimum RTT packets are beeing spread out | 109 // spread out more then half the minimum RTT packets are being spread out |
| 108 // more than the capacity. | 110 // more than the capacity. |
| 109 // This first trigger will not come into play until we hit roughly 4.8 Mbit/s. | 111 // This first trigger will not come into play until we hit roughly 9.6 Mbps |
| 112 // with delayed acks (or 4.8Mbps without delayed acks) |
| 110 // TODO(pwestin): we need to make sure our pacing don't trigger this detector. | 113 // TODO(pwestin): we need to make sure our pacing don't trigger this detector. |
| 111 if (current_time.Subtract(last_time_).ToMicroseconds() <= | 114 if (current_time.Subtract(last_close_ack_pair_time_).ToMicroseconds() <= |
| 112 kHybridStartDelayMinThresholdUs) { | 115 kHybridStartDelayMinThresholdUs) { |
| 113 last_time_ = current_time; | 116 last_close_ack_pair_time_ = current_time; |
| 114 if (current_time.Subtract(round_start_).ToMicroseconds() >= | 117 found_ack_train_ = current_time.Subtract(round_start_).ToMicroseconds() >= |
| 115 (delay_min.ToMicroseconds() >> 1)) { | 118 min_rtt.ToMicroseconds() >> 1; |
| 116 found_ack_train_ = true; | |
| 117 } | |
| 118 } | 119 } |
| 119 // Second detection parameter - delay increase detection. | 120 // Second detection parameter - delay increase detection. |
| 120 // Compare the minimum delay (current_rtt_) of the current | 121 // Compare the minimum delay (current_min_rtt_) of the current |
| 121 // burst of packets relative to the minimum delay during the session. | 122 // burst of packets relative to the minimum delay during the session. |
| 122 // Note: we only look at the first few(8) packets in each burst, since we | 123 // Note: we only look at the first few(8) packets in each burst, since we |
| 123 // only want to compare the lowest RTT of the burst relative to previous | 124 // only want to compare the lowest RTT of the burst relative to previous |
| 124 // bursts. | 125 // bursts. |
| 125 sample_count_++; | 126 rtt_sample_count_++; |
| 126 if (sample_count_ <= kHybridStartMinSamples) { | 127 if (rtt_sample_count_ <= kHybridStartMinSamples) { |
| 127 if (current_rtt_.IsZero() || current_rtt_ > rtt) { | 128 if (current_min_rtt_.IsZero() || current_min_rtt_ > rtt) { |
| 128 current_rtt_ = rtt; | 129 current_min_rtt_ = rtt; |
| 129 } | 130 } |
| 130 } | 131 } |
| 131 // We only need to check this once. | 132 // We only need to check this once. |
| 132 if (sample_count_ == kHybridStartMinSamples) { | 133 if (rtt_sample_count_ == kHybridStartMinSamples) { |
| 133 int accepted_variance_us = delay_min.ToMicroseconds() >> | 134 // Divide min_rtt by 16 to get a rtt increase threshold for exiting. |
| 135 int min_rtt_increase_threshold_us = min_rtt.ToMicroseconds() >> |
| 134 kHybridStartDelayFactorExp; | 136 kHybridStartDelayFactorExp; |
| 135 accepted_variance_us = min(accepted_variance_us, | 137 // Ensure the rtt threshold is never less than 2ms or more than 16ms. |
| 136 kHybridStartDelayMaxThresholdUs); | 138 min_rtt_increase_threshold_us = min(min_rtt_increase_threshold_us, |
| 137 QuicTime::Delta accepted_variance = QuicTime::Delta::FromMicroseconds( | 139 kHybridStartDelayMaxThresholdUs); |
| 138 max(accepted_variance_us, kHybridStartDelayMinThresholdUs)); | 140 QuicTime::Delta min_rtt_increase_threshold = |
| 141 QuicTime::Delta::FromMicroseconds(max(min_rtt_increase_threshold_us, |
| 142 kHybridStartDelayMinThresholdUs)); |
| 139 | 143 |
| 140 if (current_rtt_ > delay_min.Add(accepted_variance)) { | 144 found_delay_ = current_min_rtt_ > min_rtt.Add(min_rtt_increase_threshold); |
| 141 found_delay_ = true; | |
| 142 } | |
| 143 } | 145 } |
| 144 } | 146 // If either of the two conditions are met we exit from slow start. |
| 145 | 147 return found_ack_train_ || found_delay_; |
| 146 bool HybridSlowStart::Exit() { | |
| 147 // If either one of the two conditions are met we exit from slow start | |
| 148 // immediately. | |
| 149 if (found_ack_train_ || found_delay_) { | |
| 150 return true; | |
| 151 } | |
| 152 return false; | |
| 153 } | 148 } |
| 154 | 149 |
| 155 } // namespace net | 150 } // namespace net |
| OLD | NEW |