Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(536)

Side by Side Diff: net/quic/congestion_control/tcp_cubic_bytes_sender.cc

Issue 1785663003: Deprecate FLAG_quic_use_new_tcp_sender (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@115885351
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2015 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/tcp_cubic_bytes_sender.h"
6
7 #include <algorithm>
8
9 #include "net/quic/congestion_control/prr_sender.h"
10 #include "net/quic/congestion_control/rtt_stats.h"
11 #include "net/quic/crypto/crypto_protocol.h"
12 #include "net/quic/proto/cached_network_parameters.pb.h"
13 #include "net/quic/quic_bug_tracker.h"
14 #include "net/quic/quic_flags.h"
15
16 using std::max;
17 using std::min;
18
19 namespace net {
20
21 namespace {
22 // Constants based on TCP defaults.
23 // The minimum cwnd based on RFC 3782 (TCP NewReno) for cwnd reductions on a
24 // fast retransmission.
25 const QuicByteCount kDefaultMinimumCongestionWindow = 2 * kDefaultTCPMSS;
26 const QuicByteCount kMaxBurstBytes = 3 * kDefaultTCPMSS;
27 const float kRenoBeta = 0.7f; // Reno backoff factor.
28 const uint32_t kDefaultNumConnections = 2; // N-connection emulation.
29 } // namespace
30
31 TcpCubicBytesSender::TcpCubicBytesSender(
32 const QuicClock* clock,
33 const RttStats* rtt_stats,
34 bool reno,
35 QuicPacketCount initial_tcp_congestion_window,
36 QuicPacketCount max_congestion_window,
37 QuicConnectionStats* stats)
38 : cubic_(clock),
39 rtt_stats_(rtt_stats),
40 stats_(stats),
41 reno_(reno),
42 num_connections_(kDefaultNumConnections),
43 num_acked_packets_(0),
44 largest_sent_packet_number_(0),
45 largest_acked_packet_number_(0),
46 largest_sent_at_last_cutback_(0),
47 congestion_window_(initial_tcp_congestion_window * kDefaultTCPMSS),
48 min_congestion_window_(kDefaultMinimumCongestionWindow),
49 min4_mode_(false),
50 max_congestion_window_(max_congestion_window * kDefaultTCPMSS),
51 slowstart_threshold_(max_congestion_window * kDefaultTCPMSS),
52 last_cutback_exited_slowstart_(false),
53 initial_tcp_congestion_window_(initial_tcp_congestion_window *
54 kDefaultTCPMSS),
55 initial_max_tcp_congestion_window_(max_congestion_window *
56 kDefaultTCPMSS),
57 slow_start_large_reduction_(false) {}
58
59 TcpCubicBytesSender::~TcpCubicBytesSender() {}
60
61 void TcpCubicBytesSender::SetFromConfig(const QuicConfig& config,
62 Perspective perspective) {
63 if (perspective == Perspective::IS_SERVER) {
64 if (config.HasReceivedConnectionOptions() &&
65 ContainsQuicTag(config.ReceivedConnectionOptions(), kIW10)) {
66 // Initial window experiment.
67 congestion_window_ = 10 * kDefaultTCPMSS;
68 }
69 if (config.HasReceivedConnectionOptions() &&
70 ContainsQuicTag(config.ReceivedConnectionOptions(), kMIN1)) {
71 // Min CWND experiment.
72 min_congestion_window_ = kDefaultTCPMSS;
73 }
74 if (config.HasReceivedConnectionOptions() &&
75 ContainsQuicTag(config.ReceivedConnectionOptions(), kMIN4)) {
76 // Min CWND of 4 experiment.
77 min4_mode_ = true;
78 min_congestion_window_ = kDefaultTCPMSS;
79 }
80 if (config.HasReceivedConnectionOptions() &&
81 ContainsQuicTag(config.ReceivedConnectionOptions(), kSSLR)) {
82 // Slow Start Fast Exit experiment.
83 slow_start_large_reduction_ = true;
84 }
85 }
86 }
87
88 void TcpCubicBytesSender::ResumeConnectionState(
89 const CachedNetworkParameters& cached_network_params,
90 bool max_bandwidth_resumption) {
91 QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond(
92 max_bandwidth_resumption
93 ? cached_network_params.max_bandwidth_estimate_bytes_per_second()
94 : cached_network_params.bandwidth_estimate_bytes_per_second());
95 QuicTime::Delta rtt_ms =
96 QuicTime::Delta::FromMilliseconds(cached_network_params.min_rtt_ms());
97
98 // Make sure CWND is in appropriate range (in case of bad data).
99 QuicByteCount new_congestion_window = bandwidth.ToBytesPerPeriod(rtt_ms);
100 congestion_window_ =
101 max(min(new_congestion_window, kMaxCongestionWindow * kDefaultTCPMSS),
102 kMinCongestionWindowForBandwidthResumption * kDefaultTCPMSS);
103 }
104
105 void TcpCubicBytesSender::SetNumEmulatedConnections(int num_connections) {
106 num_connections_ = max(1, num_connections);
107 cubic_.SetNumConnections(num_connections_);
108 }
109
110 void TcpCubicBytesSender::SetMaxCongestionWindow(
111 QuicByteCount max_congestion_window) {
112 max_congestion_window_ = max_congestion_window;
113 }
114
115 float TcpCubicBytesSender::RenoBeta() const {
116 // kNConnectionBeta is the backoff factor after loss for our N-connection
117 // emulation, which emulates the effective backoff of an ensemble of N
118 // TCP-Reno connections on a single loss event. The effective multiplier is
119 // computed as:
120 return (num_connections_ - 1 + kRenoBeta) / num_connections_;
121 }
122
123 void TcpCubicBytesSender::OnCongestionEvent(
124 bool rtt_updated,
125 QuicByteCount bytes_in_flight,
126 const CongestionVector& acked_packets,
127 const CongestionVector& lost_packets) {
128 if (rtt_updated && InSlowStart() &&
129 hybrid_slow_start_.ShouldExitSlowStart(
130 rtt_stats_->latest_rtt(), rtt_stats_->min_rtt(),
131 congestion_window_ / kDefaultTCPMSS)) {
132 slowstart_threshold_ = congestion_window_;
133 }
134 for (CongestionVector::const_iterator it = lost_packets.begin();
135 it != lost_packets.end(); ++it) {
136 OnPacketLost(it->first, bytes_in_flight);
137 }
138 for (CongestionVector::const_iterator it = acked_packets.begin();
139 it != acked_packets.end(); ++it) {
140 OnPacketAcked(it->first, it->second, bytes_in_flight);
141 }
142 }
143
144 void TcpCubicBytesSender::OnPacketAcked(QuicPacketNumber acked_packet_number,
145 QuicByteCount acked_bytes,
146 QuicByteCount bytes_in_flight) {
147 largest_acked_packet_number_ =
148 max(acked_packet_number, largest_acked_packet_number_);
149 if (InRecovery()) {
150 // PRR is used when in recovery.
151 prr_.OnPacketAcked(acked_bytes);
152 return;
153 }
154 MaybeIncreaseCwnd(acked_packet_number, acked_bytes, bytes_in_flight);
155 if (InSlowStart()) {
156 hybrid_slow_start_.OnPacketAcked(acked_packet_number);
157 }
158 }
159
160 void TcpCubicBytesSender::OnPacketLost(QuicPacketNumber packet_number,
161 QuicByteCount bytes_in_flight) {
162 // TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets
163 // already sent should be treated as a single loss event, since it's expected.
164 if (packet_number <= largest_sent_at_last_cutback_) {
165 if (last_cutback_exited_slowstart_) {
166 ++stats_->slowstart_packets_lost;
167 if (slow_start_large_reduction_) {
168 // Reduce congestion window by 1 MSS for every loss.
169 congestion_window_ =
170 max(congestion_window_ - kDefaultTCPMSS, min_congestion_window_);
171 slowstart_threshold_ = congestion_window_;
172 }
173 }
174 DVLOG(1) << "Ignoring loss for largest_missing:" << packet_number
175 << " because it was sent prior to the last CWND cutback.";
176 return;
177 }
178 ++stats_->tcp_loss_events;
179 last_cutback_exited_slowstart_ = InSlowStart();
180 if (InSlowStart()) {
181 ++stats_->slowstart_packets_lost;
182 }
183
184 prr_.OnPacketLost(bytes_in_flight);
185
186 // TODO(jri): Separate out all of slow start into a separate class.
187 if (slow_start_large_reduction_ && InSlowStart()) {
188 DCHECK_LT(kDefaultTCPMSS, congestion_window_);
189 congestion_window_ = congestion_window_ - kDefaultTCPMSS;
190 } else if (reno_) {
191 congestion_window_ = congestion_window_ * RenoBeta();
192 } else {
193 congestion_window_ =
194 cubic_.CongestionWindowAfterPacketLoss(congestion_window_);
195 }
196 // Enforce TCP's minimum congestion window of 2*MSS.
197 if (congestion_window_ < min_congestion_window_) {
198 congestion_window_ = min_congestion_window_;
199 }
200 slowstart_threshold_ = congestion_window_;
201 largest_sent_at_last_cutback_ = largest_sent_packet_number_;
202 // Reset packet count from congestion avoidance mode. We start counting again
203 // when we're out of recovery.
204 num_acked_packets_ = 0;
205 DVLOG(1) << "Incoming loss; congestion window: " << congestion_window_
206 << " slowstart threshold: " << slowstart_threshold_;
207 }
208
209 bool TcpCubicBytesSender::OnPacketSent(
210 QuicTime /*sent_time*/,
211 QuicByteCount /*bytes_in_flight*/,
212 QuicPacketNumber packet_number,
213 QuicByteCount bytes,
214 HasRetransmittableData is_retransmittable) {
215 if (InSlowStart()) {
216 ++(stats_->slowstart_packets_sent);
217 }
218
219 // Only update bytes_in_flight_ for data packets.
220 if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) {
221 return false;
222 }
223 if (InRecovery()) {
224 // PRR is used when in recovery.
225 prr_.OnPacketSent(bytes);
226 }
227 DCHECK_LT(largest_sent_packet_number_, packet_number);
228 largest_sent_packet_number_ = packet_number;
229 hybrid_slow_start_.OnPacketSent(packet_number);
230 return true;
231 }
232
233 QuicTime::Delta TcpCubicBytesSender::TimeUntilSend(
234 QuicTime /* now */,
235 QuicByteCount bytes_in_flight,
236 HasRetransmittableData has_retransmittable_data) const {
237 if (has_retransmittable_data == NO_RETRANSMITTABLE_DATA) {
238 DCHECK(!FLAGS_quic_respect_send_alarm2);
239 // For TCP we can always send an ACK immediately.
240 return QuicTime::Delta::Zero();
241 }
242 if (InRecovery()) {
243 // PRR is used when in recovery.
244 return prr_.TimeUntilSend(GetCongestionWindow(), bytes_in_flight,
245 slowstart_threshold_);
246 }
247 if (GetCongestionWindow() > bytes_in_flight) {
248 return QuicTime::Delta::Zero();
249 }
250 if (min4_mode_ && bytes_in_flight < 4 * kDefaultTCPMSS) {
251 return QuicTime::Delta::Zero();
252 }
253 return QuicTime::Delta::Infinite();
254 }
255
256 QuicBandwidth TcpCubicBytesSender::PacingRate() const {
257 // We pace at twice the rate of the underlying sender's bandwidth estimate
258 // during slow start and 1.25x during congestion avoidance to ensure pacing
259 // doesn't prevent us from filling the window.
260 QuicTime::Delta srtt = rtt_stats_->smoothed_rtt();
261 if (srtt.IsZero()) {
262 srtt = QuicTime::Delta::FromMicroseconds(rtt_stats_->initial_rtt_us());
263 }
264 const QuicBandwidth bandwidth =
265 QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(), srtt);
266 return bandwidth.Scale(InSlowStart() ? 2 : 1.25);
267 }
268
269 QuicBandwidth TcpCubicBytesSender::BandwidthEstimate() const {
270 QuicTime::Delta srtt = rtt_stats_->smoothed_rtt();
271 if (srtt.IsZero()) {
272 // If we haven't measured an rtt, the bandwidth estimate is unknown.
273 return QuicBandwidth::Zero();
274 }
275 return QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(), srtt);
276 }
277
278 QuicTime::Delta TcpCubicBytesSender::RetransmissionDelay() const {
279 if (rtt_stats_->smoothed_rtt().IsZero()) {
280 return QuicTime::Delta::Zero();
281 }
282 return rtt_stats_->smoothed_rtt().Add(
283 rtt_stats_->mean_deviation().Multiply(4));
284 }
285
286 QuicByteCount TcpCubicBytesSender::GetCongestionWindow() const {
287 return congestion_window_;
288 }
289
290 bool TcpCubicBytesSender::InSlowStart() const {
291 return congestion_window_ < slowstart_threshold_;
292 }
293
294 QuicByteCount TcpCubicBytesSender::GetSlowStartThreshold() const {
295 return slowstart_threshold_;
296 }
297
298 bool TcpCubicBytesSender::IsCwndLimited(QuicByteCount bytes_in_flight) const {
299 if (bytes_in_flight >= congestion_window_) {
300 return true;
301 }
302 const QuicByteCount available_bytes = congestion_window_ - bytes_in_flight;
303 const bool slow_start_limited =
304 InSlowStart() && bytes_in_flight > congestion_window_ / 2;
305 return slow_start_limited || available_bytes <= kMaxBurstBytes;
306 }
307
308 bool TcpCubicBytesSender::InRecovery() const {
309 return largest_acked_packet_number_ <= largest_sent_at_last_cutback_ &&
310 largest_acked_packet_number_ != 0;
311 }
312
313 // Called when we receive an ack. Normal TCP tracks how many packets one ack
314 // represents, but quic has a separate ack for each packet.
315 void TcpCubicBytesSender::MaybeIncreaseCwnd(
316 QuicPacketNumber acked_packet_number,
317 QuicByteCount acked_bytes,
318 QuicByteCount bytes_in_flight) {
319 QUIC_BUG_IF(InRecovery()) << "Never increase the CWND during recovery.";
320 // Do not increase the congestion window unless the sender is close to using
321 // the current window.
322 if (!IsCwndLimited(bytes_in_flight)) {
323 cubic_.OnApplicationLimited();
324 return;
325 }
326 if (congestion_window_ >= max_congestion_window_) {
327 return;
328 }
329 if (InSlowStart()) {
330 // TCP slow start, exponential growth, increase by one for each ACK.
331 congestion_window_ += kDefaultTCPMSS;
332 DVLOG(1) << "Slow start; congestion window: " << congestion_window_
333 << " slowstart threshold: " << slowstart_threshold_;
334 return;
335 }
336 // Congestion avoidance.
337 if (reno_) {
338 // Classic Reno congestion avoidance.
339 ++num_acked_packets_;
340 // Divide by num_connections to smoothly increase the CWND at a faster rate
341 // than conventional Reno.
342 if (num_acked_packets_ * num_connections_ >=
343 congestion_window_ / kDefaultTCPMSS) {
344 congestion_window_ += kDefaultTCPMSS;
345 num_acked_packets_ = 0;
346 }
347
348 DVLOG(1) << "Reno; congestion window: " << congestion_window_
349 << " slowstart threshold: " << slowstart_threshold_
350 << " congestion window count: " << num_acked_packets_;
351 } else {
352 congestion_window_ =
353 min(max_congestion_window_,
354 cubic_.CongestionWindowAfterAck(acked_bytes, congestion_window_,
355 rtt_stats_->min_rtt()));
356 DVLOG(1) << "Cubic; congestion window: " << congestion_window_
357 << " slowstart threshold: " << slowstart_threshold_;
358 }
359 }
360
361 void TcpCubicBytesSender::OnRetransmissionTimeout(bool packets_retransmitted) {
362 largest_sent_at_last_cutback_ = 0;
363 if (!packets_retransmitted) {
364 return;
365 }
366 cubic_.Reset();
367 hybrid_slow_start_.Restart();
368 slowstart_threshold_ = congestion_window_ / 2;
369 congestion_window_ = min_congestion_window_;
370 }
371
372 CongestionControlType TcpCubicBytesSender::GetCongestionControlType() const {
373 return reno_ ? kRenoBytes : kCubicBytes;
374 }
375
376 void TcpCubicBytesSender::OnConnectionMigration() {
377 hybrid_slow_start_.Restart();
378 cubic_.Reset();
379 prr_ = PrrSender();
380 num_acked_packets_ = 0;
381 largest_sent_packet_number_ = 0;
382 largest_acked_packet_number_ = 0;
383 largest_sent_at_last_cutback_ = 0;
384 congestion_window_ = initial_tcp_congestion_window_;
385 max_congestion_window_ = initial_max_tcp_congestion_window_;
386 slowstart_threshold_ = initial_max_tcp_congestion_window_;
387 last_cutback_exited_slowstart_ = false;
388 }
389
390 } // namespace net
OLDNEW
« no previous file with comments | « net/quic/congestion_control/tcp_cubic_bytes_sender.h ('k') | net/quic/congestion_control/tcp_cubic_bytes_sender_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698