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 uint32 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 |