diff --git a/quic/s2n-quic-core/src/recovery/cubic.rs b/quic/s2n-quic-core/src/recovery/cubic.rs index acfd76c426..a41cd30037 100644 --- a/quic/s2n-quic-core/src/recovery/cubic.rs +++ b/quic/s2n-quic-core/src/recovery/cubic.rs @@ -82,6 +82,7 @@ pub struct CubicCongestionController { //# congestion feedback. bytes_in_flight: BytesInFlight, time_of_last_sent_packet: Option, + under_utilized: bool, } type BytesInFlight = Counter; @@ -107,7 +108,9 @@ impl CongestionController for CubicCongestionController { .try_add(bytes_sent) .expect("bytes sent should not exceed u32::MAX"); - if !self.is_congestion_limited() { + self.under_utilized = self.is_congestion_window_under_utilized(); + + if self.under_utilized { if let CongestionAvoidance(ref mut avoidance_start_time) = self.state { //= https://tools.ietf.org/rfc/rfc8312#5.8 //# CUBIC does not raise its congestion window size if the flow is @@ -156,13 +159,11 @@ impl CongestionController for CubicCongestionController { rtt_estimator: &RTTEstimator, ack_receive_time: Timestamp, ) { - // Check if the congestion window is under utilized before updating bytes in flight - let under_utilized = !self.is_congestion_limited(); self.bytes_in_flight .try_sub(sent_bytes) .expect("sent bytes should not exceed u32::MAX"); - if under_utilized { + if self.under_utilized { //= https://tools.ietf.org/id/draft-ietf-quic-recovery-32.txt#7.8 //# When bytes in flight is smaller than the congestion window and //# sending is not pacing limited, the congestion window is under- @@ -330,6 +331,7 @@ impl CubicCongestionController { state: SlowStart, bytes_in_flight: Counter::new(0), time_of_last_sent_packet: None, + under_utilized: true, } } @@ -430,6 +432,31 @@ impl CubicCongestionController { fn packets_to_bytes(&self, cwnd: f32) -> f32 { cwnd * self.max_datagram_size as f32 } + + /// Returns true if the congestion window is under utilized and should not grow larger + /// without further evidence of the stability of the current window. + fn is_congestion_window_under_utilized(&self) -> bool { + // This value is based on kMaxBurstBytes from Chromium + // https://source.chromium.org/chromium/chromium/src/+/master:net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.cc;l=23;drc=f803516d2656ed829e54b2e819731763ca6cf4d9 + const MAX_BURST_MULTIPLIER: u32 = 3; + + if self.is_congestion_limited() { + return false; + } + + // In slow start, allow the congestion window to increase as long as half of it is + // being used. This allows for the window to increase rapidly. + if matches!(self.state, SlowStart) && self.bytes_in_flight >= self.congestion_window() / 2 { + return false; + } + + // Otherwise allow the window to increase while MAX_BURST_MULTIPLIER packets are available + // in the window. + let available_congestion_window = self + .congestion_window() + .saturating_sub(*self.bytes_in_flight); + available_congestion_window > self.max_datagram_size as u32 * MAX_BURST_MULTIPLIER + } } /// Core functions of "CUBIC for Fast Long-Distance Networks" as specified in @@ -708,6 +735,32 @@ mod test { assert!(cc.is_congestion_limited()); } + #[test] + fn is_congestion_window_under_utilized() { + let max_datagram_size = 1200; + let mut cc = CubicCongestionController::new(max_datagram_size); + cc.congestion_window = 12000.0; + + // In Slow Start, the window is under utilized if it is less than half full + cc.bytes_in_flight = BytesInFlight::new(5999); + cc.state = SlowStart; + assert!(cc.is_congestion_window_under_utilized()); + + cc.bytes_in_flight = BytesInFlight::new(6000); + assert!(!cc.is_congestion_window_under_utilized()); + + cc.state = CongestionAvoidance(NoopClock.get_time()); + assert!(cc.is_congestion_window_under_utilized()); + + // In Congestion Avoidance, the window is under utilized if there are more than + // 3 * MTU bytes available in the congestion window (12000 - 3 * 1200 = 8400) + cc.bytes_in_flight = BytesInFlight::new(8399); + assert!(cc.is_congestion_window_under_utilized()); + + cc.bytes_in_flight = BytesInFlight::new(8400); + assert!(!cc.is_congestion_window_under_utilized()); + } + //= https://tools.ietf.org/id/draft-ietf-quic-recovery-32.txt#7.2 //= type=test //# Endpoints SHOULD use an initial congestion @@ -808,13 +861,13 @@ mod test { let now = NoopClock.get_time(); cc.congestion_window = 100_000.0; - cc.bytes_in_flight = BytesInFlight::new(96_500); + cc.bytes_in_flight = BytesInFlight::new(92_500); cc.state = SlowStart; // t0: Send a packet in Slow Start cc.on_packet_sent(now, 1000); - assert_eq!(cc.bytes_in_flight, 97_500); + assert_eq!(cc.bytes_in_flight, 93_500); assert_eq!(cc.time_of_last_sent_packet, Some(now)); // t10: Enter Congestion Avoidance @@ -823,7 +876,7 @@ mod test { // t15: Send a packet in Congestion Avoidance cc.on_packet_sent(now + Duration::from_secs(15), 1000); - assert_eq!(cc.bytes_in_flight, 98_500); + assert_eq!(cc.bytes_in_flight, 94_500); assert_eq!( cc.time_of_last_sent_packet, Some(now + Duration::from_secs(15)) @@ -832,7 +885,7 @@ mod test { // so the CongestionAvoidance increases by the time from avoidance start to now assert_eq!(cc.state, CongestionAvoidance(now + Duration::from_secs(15))); - cc.bytes_in_flight = BytesInFlight::new(97500); + cc.bytes_in_flight = BytesInFlight::new(93500); // t25: Send a packet in Congestion Avoidance cc.on_packet_sent(now + Duration::from_secs(25), 1000); @@ -868,7 +921,7 @@ mod test { let now = NoopClock.get_time(); let rtt_estimator = &RTTEstimator::new(Duration::from_secs(0)); - cc.congestion_window = 3000.0; + cc.congestion_window = 6000.0; cc.bytes_in_flight = BytesInFlight::new(0); cc.state = SlowStart; @@ -882,9 +935,11 @@ mod test { // t15: Send a packet in Congestion Avoidance while under utilized cc.on_packet_sent(now + Duration::from_secs(15), 1000); + assert!(cc.is_congestion_window_under_utilized()); // t15: Send a packet in Congestion Avoidance while not under utilized cc.on_packet_sent(now + Duration::from_secs(15), 1000); + assert!(!cc.is_congestion_window_under_utilized()); assert_eq!(cc.bytes_in_flight, 3000); @@ -1075,6 +1130,7 @@ mod test { let now = NoopClock.get_time(); cc.congestion_window = 100_000.0; cc.bytes_in_flight = BytesInFlight::new(10000); + cc.under_utilized = true; cc.state = SlowStart; cc.on_packet_ack(now, 1, &RTTEstimator::new(Duration::from_secs(0)), now); @@ -1088,6 +1144,43 @@ mod test { assert_delta!(cc.congestion_window, 100_000.0, 0.001); } + #[test] + fn on_packet_ack_utilized_then_under_utilized() { + let mut cc = CubicCongestionController::new(5000); + let now = NoopClock.get_time(); + let mut rtt_estimator = RTTEstimator::new(Duration::from_secs(0)); + rtt_estimator.update_rtt( + Duration::from_secs(0), + Duration::from_millis(200), + now, + true, + PacketNumberSpace::ApplicationData, + ); + cc.congestion_window = 100_000.0; + cc.state = SlowStart; + + cc.on_packet_sent(now, 60_000); + cc.on_packet_ack(now, 50_000, &rtt_estimator, now); + let cwnd = cc.congestion_window(); + + assert!(!cc.under_utilized); + assert!(cwnd > 100_000); + + // Now the window is under utilized, but we still grow the window until more packets are sent + assert!(cc.is_congestion_window_under_utilized()); + cc.on_packet_ack(now, 1200, &rtt_estimator, now + Duration::from_millis(100)); + assert!(cc.congestion_window() > cwnd); + + let cwnd = cc.congestion_window(); + + // Now the application has had a chance to send more data, but it didn't send enough to + // utilize the congestion window, so the window does not grow. + cc.on_packet_sent(now, 1200); + assert!(cc.under_utilized); + cc.on_packet_ack(now, 1200, &rtt_estimator, now + Duration::from_millis(201)); + assert_eq!(cc.congestion_window(), cwnd); + } + #[test] #[compliance::tests("https://tools.ietf.org/id/draft-ietf-quic-recovery-32.txt#7.3.2")] fn on_packet_ack_recovery_to_congestion_avoidance() { @@ -1097,6 +1190,7 @@ mod test { cc.cubic.w_max = bytes_to_packets(25000.0, 5000); cc.state = Recovery(now, Idle); cc.bytes_in_flight = BytesInFlight::new(25000); + cc.under_utilized = false; cc.on_packet_ack( now + Duration::from_millis(1), @@ -1121,6 +1215,7 @@ mod test { cc.congestion_window = 10000.0; cc.bytes_in_flight = BytesInFlight::new(10000); cc.slow_start.threshold = 10050.0; + cc.under_utilized = false; cc.on_packet_ack( now, @@ -1174,6 +1269,7 @@ mod test { cc.congestion_window = 10000.0; cc.bytes_in_flight = BytesInFlight::new(10000); cc.cubic.w_max = bytes_to_packets(10000.0, max_datagram_size); + cc.under_utilized = false; cc2.congestion_window = 10000.0; cc2.bytes_in_flight = BytesInFlight::new(10000); diff --git a/quic/s2n-quic-core/tests/recovery/simulation.rs b/quic/s2n-quic-core/tests/recovery/simulation.rs index ef09dc8fed..59bc58323e 100644 --- a/quic/s2n-quic-core/tests/recovery/simulation.rs +++ b/quic/s2n-quic-core/tests/recovery/simulation.rs @@ -31,9 +31,18 @@ fn loss_at_3mb_test() { loss_at_3mb(cc, 135).finish(); } +#[test] +#[cfg_attr(miri, ignore)] +fn app_limited_1mb_test() { + let cc = CubicCongestionController::new(MINIMUM_MTU); + + app_limited_1mb(cc, 120).finish(); +} + #[derive(Debug)] struct Simulation { name: &'static str, + description: &'static str, cc: &'static str, rounds: Vec, } @@ -65,12 +74,16 @@ impl Simulation { fn plot + ?Sized>(&self, path: &T) { let root_area = SVGBackend::new(path, CHART_DIMENSIONS).into_drawing_area(); root_area.fill(&WHITE).expect("Could not fill chart"); + root_area + .titled(&*self.name(), ("sans-serif", 40)) + .expect("Could not add title"); let mut ctx = ChartBuilder::on(&root_area) - .set_label_area_size(LabelAreaPosition::Left, 100) + .set_label_area_size(LabelAreaPosition::Left, 120) .set_label_area_size(LabelAreaPosition::Bottom, 60) .margin(20) - .caption(self.name(), ("sans-serif", 40)) + .margin_top(40) + .caption(self.description, ("sans-serif", 20)) .build_cartesian_2d(self.x_spec(), self.y_spec()) .expect("Could not build chart"); @@ -93,9 +106,12 @@ impl Simulation { } fn y_spec(&self) -> Range { - let max = self.rounds.iter().map(|r| r.cwnd as i32).max().unwrap_or(0); + let mut max = self.rounds.iter().map(|r| r.cwnd as i32).max().unwrap_or(0); + + // Add a 5% buffer + max = (max as f32 * 1.05) as i32; - 0..max + MINIMUM_MTU as i32 + 0..max } fn assert_snapshot(&self) { @@ -164,6 +180,7 @@ fn slow_start_unlimited( Simulation { name: "Slow Start Unlimited", + description: "Full congestion window utilization with no congestion experienced", cc: core::any::type_name::(), rounds, } @@ -178,10 +195,7 @@ fn loss_at_3mb( let mut rtt_estimator = RTTEstimator::new(Duration::from_millis(0)); let mut rounds = Vec::with_capacity(num_rounds); - // Ensure the congestion window is fully utilized - congestion_controller.on_packet_sent(time_zero, u32::MAX as usize); - - let mut ack_receive_time = time_zero + Duration::from_millis(1); + let mut round_start = time_zero + Duration::from_millis(1); // Update the rtt with 200 ms rtt_estimator.update_rtt( @@ -195,10 +209,17 @@ fn loss_at_3mb( let mut slow_start_round = 0; while congestion_controller.congestion_window() < 3_000_000 && slow_start_round < num_rounds { - ack_receive_time += Duration::from_millis(200); + round_start += Duration::from_millis(200); + + let send_bytes = congestion_controller.congestion_window() as usize; - // Ack the full congestion window - ack_cwnd(&mut congestion_controller, &rtt_estimator, ack_receive_time); + // Send and ack the full congestion window + send_and_ack( + &mut congestion_controller, + &rtt_estimator, + round_start, + send_bytes, + ); rounds.push(Round { number: slow_start_round, @@ -209,7 +230,8 @@ fn loss_at_3mb( } // Lose a packet to exit slow start - congestion_controller.on_packets_lost(MINIMUM_MTU as u32, false, ack_receive_time); + congestion_controller.on_packet_sent(round_start, MINIMUM_MTU as usize); + congestion_controller.on_packets_lost(MINIMUM_MTU as u32, false, round_start); for round in slow_start_round..num_rounds { rounds.push(Round { @@ -217,36 +239,126 @@ fn loss_at_3mb( cwnd: congestion_controller.congestion_window(), }); - ack_receive_time += Duration::from_millis(200); + round_start += Duration::from_millis(200); + + let send_bytes = congestion_controller.congestion_window() as usize; - // Ack the full congestion window - ack_cwnd(&mut congestion_controller, &rtt_estimator, ack_receive_time); + // Send and ack the full congestion window + send_and_ack( + &mut congestion_controller, + &rtt_estimator, + round_start, + send_bytes, + ); } Simulation { name: "Loss at 3MB", + description: "Full congestion window utilization with loss encountered at ~3MB", + cc: core::any::type_name::(), + rounds, + } +} + +/// Simulates a network that experiences loss at a 750KB congestion window with the application +/// sending at most 1MB of data per round. +fn app_limited_1mb( + mut congestion_controller: CC, + num_rounds: usize, +) -> Simulation { + let time_zero = NoopClock.get_time(); + let mut rtt_estimator = RTTEstimator::new(Duration::from_millis(0)); + let mut rounds = Vec::with_capacity(num_rounds); + + let mut round_start = time_zero + Duration::from_millis(1); + + const APP_LIMIT_BYTES: usize = 1_000_000; + + // Update the rtt with 200 ms + rtt_estimator.update_rtt( + Duration::from_millis(0), + Duration::from_millis(200), + time_zero, + true, + PacketNumberSpace::ApplicationData, + ); + + let mut slow_start_round = 0; + + while congestion_controller.congestion_window() < 750_000 && slow_start_round < num_rounds { + round_start += Duration::from_millis(200); + + // Send and ack the full congestion window + let cwnd = congestion_controller.congestion_window() as usize; + + send_and_ack( + &mut congestion_controller, + &rtt_estimator, + round_start, + cwnd, + ); + + rounds.push(Round { + number: slow_start_round, + cwnd: congestion_controller.congestion_window(), + }); + + slow_start_round += 1; + } + + // Lose a packet to exit slow start + congestion_controller.on_packet_sent(round_start, MINIMUM_MTU as usize); + congestion_controller.on_packets_lost(MINIMUM_MTU as u32, false, round_start); + + for round in slow_start_round..num_rounds { + rounds.push(Round { + number: round, + cwnd: congestion_controller.congestion_window(), + }); + + round_start += Duration::from_millis(200); + + // Send and ack up to APP_LIMIT_BYTES + let send_bytes = (congestion_controller.congestion_window() as usize).min(APP_LIMIT_BYTES); + + send_and_ack( + &mut congestion_controller, + &rtt_estimator, + round_start, + send_bytes, + ); + } + + Simulation { + name: "App Limited 1MB", + description: "App limited to 1MB per round with loss encountered at ~750KB", cc: core::any::type_name::(), rounds, } } /// Acknowledge a full congestion window of packets using the given congestion controller -fn ack_cwnd( +fn send_and_ack( congestion_controller: &mut CC, rtt_estimator: &RTTEstimator, timestamp: Timestamp, + bytes: usize, ) { - let mut cwnd = congestion_controller.congestion_window(); - while cwnd >= MINIMUM_MTU as u32 { + congestion_controller.on_packet_sent(timestamp, bytes); + + let ack_receive_time = timestamp + rtt_estimator.min_rtt(); + + let mut remaining = bytes; + + while remaining > 0 { + let bytes_sent = remaining.min(MINIMUM_MTU as usize); + congestion_controller.on_packet_ack( - timestamp, - MINIMUM_MTU as usize, + ack_receive_time, + bytes_sent, rtt_estimator, - timestamp, + ack_receive_time, ); - cwnd -= MINIMUM_MTU as u32; - // Ensure the congestion window is always fully utilized by sending a packet the - // same size as the one that we just acked. - congestion_controller.on_packet_sent(timestamp, MINIMUM_MTU as usize); + remaining -= bytes_sent; } } diff --git a/quic/s2n-quic-core/tests/recovery/snapshots/recovery_simulation__AppLimited1MB-CubicCongestionController.snap b/quic/s2n-quic-core/tests/recovery/snapshots/recovery_simulation__AppLimited1MB-CubicCongestionController.snap new file mode 100644 index 0000000000..6eef74acce --- /dev/null +++ b/quic/s2n-quic-core/tests/recovery/snapshots/recovery_simulation__AppLimited1MB-CubicCongestionController.snap @@ -0,0 +1,131 @@ +--- +source: quic/s2n-quic-core/tests/recovery/simulation.rs +expression: self +--- +Simulation { + name: "App Limited 1MB", + description: "App limited to 1MB per round with loss encountered at ~750KB", + cc: "s2n_quic_core::recovery::cubic::CubicCongestionController", + rounds: [ + 0: cwnd: 24000, + 1: cwnd: 48000, + 2: cwnd: 96000, + 3: cwnd: 192000, + 4: cwnd: 384000, + 5: cwnd: 768000, + 6: cwnd: 537600, + 7: cwnd: 548411, + 8: cwnd: 562667, + 9: cwnd: 577679, + 10: cwnd: 592472, + 11: cwnd: 606664, + 12: cwnd: 620145, + 13: cwnd: 632876, + 14: cwnd: 644870, + 15: cwnd: 656132, + 16: cwnd: 666681, + 17: cwnd: 676548, + 18: cwnd: 685750, + 19: cwnd: 694315, + 20: cwnd: 702261, + 21: cwnd: 709616, + 22: cwnd: 716397, + 23: cwnd: 722627, + 24: cwnd: 728339, + 25: cwnd: 733545, + 26: cwnd: 738276, + 27: cwnd: 742552, + 28: cwnd: 746393, + 29: cwnd: 749825, + 30: cwnd: 752873, + 31: cwnd: 755559, + 32: cwnd: 757904, + 33: cwnd: 759933, + 34: cwnd: 761669, + 35: cwnd: 763134, + 36: cwnd: 764351, + 37: cwnd: 765344, + 38: cwnd: 766136, + 39: cwnd: 766750, + 40: cwnd: 767209, + 41: cwnd: 767535, + 42: cwnd: 767752, + 43: cwnd: 767883, + 44: cwnd: 767950, + 45: cwnd: 767980, + 46: cwnd: 767982, + 47: cwnd: 768004, + 48: cwnd: 768056, + 49: cwnd: 768160, + 50: cwnd: 768337, + 51: cwnd: 768609, + 52: cwnd: 769004, + 53: cwnd: 769544, + 54: cwnd: 770251, + 55: cwnd: 771149, + 56: cwnd: 772260, + 57: cwnd: 773608, + 58: cwnd: 775215, + 59: cwnd: 777105, + 60: cwnd: 779300, + 61: cwnd: 781824, + 62: cwnd: 784700, + 63: cwnd: 787948, + 64: cwnd: 791597, + 65: cwnd: 795665, + 66: cwnd: 800180, + 67: cwnd: 805156, + 68: cwnd: 810623, + 69: cwnd: 816606, + 70: cwnd: 823123, + 71: cwnd: 830197, + 72: cwnd: 837854, + 73: cwnd: 846119, + 74: cwnd: 855011, + 75: cwnd: 864550, + 76: cwnd: 874764, + 77: cwnd: 885670, + 78: cwnd: 897307, + 79: cwnd: 909677, + 80: cwnd: 922822, + 81: cwnd: 936753, + 82: cwnd: 951488, + 83: cwnd: 967057, + 84: cwnd: 983489, + 85: cwnd: 1000806, + 86: cwnd: 1019020, + 87: cwnd: 1019020, + 88: cwnd: 1019020, + 89: cwnd: 1019020, + 90: cwnd: 1019020, + 91: cwnd: 1019020, + 92: cwnd: 1019020, + 93: cwnd: 1019020, + 94: cwnd: 1019020, + 95: cwnd: 1019020, + 96: cwnd: 1019020, + 97: cwnd: 1019020, + 98: cwnd: 1019020, + 99: cwnd: 1019020, + 100: cwnd: 1019020, + 101: cwnd: 1019020, + 102: cwnd: 1019020, + 103: cwnd: 1019020, + 104: cwnd: 1019020, + 105: cwnd: 1019020, + 106: cwnd: 1019020, + 107: cwnd: 1019020, + 108: cwnd: 1019020, + 109: cwnd: 1019020, + 110: cwnd: 1019020, + 111: cwnd: 1019020, + 112: cwnd: 1019020, + 113: cwnd: 1019020, + 114: cwnd: 1019020, + 115: cwnd: 1019020, + 116: cwnd: 1019020, + 117: cwnd: 1019020, + 118: cwnd: 1019020, + 119: cwnd: 1019020, + ], +} diff --git a/quic/s2n-quic-core/tests/recovery/snapshots/recovery_simulation__Lossat3MB-CubicCongestionController.snap b/quic/s2n-quic-core/tests/recovery/snapshots/recovery_simulation__Lossat3MB-CubicCongestionController.snap index 1fed9aa43f..5a60ce48d6 100644 --- a/quic/s2n-quic-core/tests/recovery/snapshots/recovery_simulation__Lossat3MB-CubicCongestionController.snap +++ b/quic/s2n-quic-core/tests/recovery/snapshots/recovery_simulation__Lossat3MB-CubicCongestionController.snap @@ -4,6 +4,7 @@ expression: self --- Simulation { name: "Loss at 3MB", + description: "Full congestion window utilization with loss encountered at ~3MB", cc: "s2n_quic_core::recovery::cubic::CubicCongestionController", rounds: [ 0: cwnd: 24000, @@ -16,59 +17,59 @@ Simulation { 7: cwnd: 3072000, 8: cwnd: 2150400, 9: cwnd: 2177957, - 10: cwnd: 2214775, - 11: cwnd: 2254203, - 12: cwnd: 2293757, - 13: cwnd: 2332521, - 14: cwnd: 2370159, - 15: cwnd: 2406577, - 16: cwnd: 2441731, - 17: cwnd: 2475632, - 18: cwnd: 2508305, - 19: cwnd: 2539758, - 20: cwnd: 2570018, - 21: cwnd: 2599107, - 22: cwnd: 2627047, - 23: cwnd: 2653873, - 24: cwnd: 2679589, - 25: cwnd: 2704229, - 26: cwnd: 2727817, - 27: cwnd: 2750374, - 28: cwnd: 2771917, - 29: cwnd: 2792478, - 30: cwnd: 2812084, - 31: cwnd: 2830746, - 32: cwnd: 2848489, - 33: cwnd: 2865341, - 34: cwnd: 2881322, - 35: cwnd: 2896459, - 36: cwnd: 2910767, - 37: cwnd: 2924277, - 38: cwnd: 2937010, - 39: cwnd: 2948994, - 40: cwnd: 2960242, - 41: cwnd: 2970777, - 42: cwnd: 2980633, - 43: cwnd: 2989827, - 44: cwnd: 2998377, - 45: cwnd: 3006319, - 46: cwnd: 3013659, - 47: cwnd: 3020435, - 48: cwnd: 3026662, - 49: cwnd: 3032365, - 50: cwnd: 3037566, - 51: cwnd: 3042295, - 52: cwnd: 3046558, - 53: cwnd: 3050411, - 54: cwnd: 3053826, - 55: cwnd: 3056868, - 56: cwnd: 3059568, - 57: cwnd: 3061892, - 58: cwnd: 3063914, + 10: cwnd: 2214787, + 11: cwnd: 2254221, + 12: cwnd: 2293776, + 13: cwnd: 2332539, + 14: cwnd: 2370177, + 15: cwnd: 2406593, + 16: cwnd: 2441748, + 17: cwnd: 2475647, + 18: cwnd: 2508321, + 19: cwnd: 2539773, + 20: cwnd: 2570031, + 21: cwnd: 2599120, + 22: cwnd: 2627060, + 23: cwnd: 2653885, + 24: cwnd: 2679601, + 25: cwnd: 2704239, + 26: cwnd: 2727827, + 27: cwnd: 2750384, + 28: cwnd: 2771926, + 29: cwnd: 2792487, + 30: cwnd: 2812091, + 31: cwnd: 2830753, + 32: cwnd: 2848496, + 33: cwnd: 2865348, + 34: cwnd: 2881329, + 35: cwnd: 2896465, + 36: cwnd: 2910773, + 37: cwnd: 2924282, + 38: cwnd: 2937015, + 39: cwnd: 2948998, + 40: cwnd: 2960247, + 41: cwnd: 2970782, + 42: cwnd: 2980636, + 43: cwnd: 2989831, + 44: cwnd: 2998380, + 45: cwnd: 3006321, + 46: cwnd: 3013662, + 47: cwnd: 3020437, + 48: cwnd: 3026664, + 49: cwnd: 3032367, + 50: cwnd: 3037568, + 51: cwnd: 3042297, + 52: cwnd: 3046559, + 53: cwnd: 3050413, + 54: cwnd: 3053828, + 55: cwnd: 3056870, + 56: cwnd: 3059569, + 57: cwnd: 3061893, + 58: cwnd: 3063915, 59: cwnd: 3065672, 60: cwnd: 3067118, 61: cwnd: 3068308, - 62: cwnd: 3069279, + 62: cwnd: 3069280, 63: cwnd: 3070099, 64: cwnd: 3070764, 65: cwnd: 3071157, @@ -90,56 +91,56 @@ Simulation { 81: cwnd: 3076211, 82: cwnd: 3077562, 83: cwnd: 3079215, - 84: cwnd: 3081099, - 85: cwnd: 3083278, - 86: cwnd: 3085812, - 87: cwnd: 3088706, - 88: cwnd: 3091947, - 89: cwnd: 3095602, - 90: cwnd: 3099679, - 91: cwnd: 3104188, - 92: cwnd: 3109183, - 93: cwnd: 3114645, - 94: cwnd: 3120634, - 95: cwnd: 3127153, - 96: cwnd: 3134236, - 97: cwnd: 3141895, - 98: cwnd: 3150168, - 99: cwnd: 3159061, - 100: cwnd: 3168615, - 101: cwnd: 3178830, - 102: cwnd: 3189755, - 103: cwnd: 3201391, - 104: cwnd: 3213771, - 105: cwnd: 3226925, - 106: cwnd: 3240861, - 107: cwnd: 3255610, - 108: cwnd: 3271199, - 109: cwnd: 3287639, - 110: cwnd: 3304966, - 111: cwnd: 3323202, - 112: cwnd: 3342357, - 113: cwnd: 3362466, - 114: cwnd: 3383553, - 115: cwnd: 3405627, - 116: cwnd: 3428726, - 117: cwnd: 3452869, - 118: cwnd: 3478076, - 119: cwnd: 3504371, - 120: cwnd: 3531778, - 121: cwnd: 3560325, - 122: cwnd: 3590020, - 123: cwnd: 3620900, - 124: cwnd: 3652987, - 125: cwnd: 3686304, - 126: cwnd: 3720861, - 127: cwnd: 3756695, - 128: cwnd: 3793830, - 129: cwnd: 3832280, - 130: cwnd: 3872073, - 131: cwnd: 3913230, - 132: cwnd: 3955782, - 133: cwnd: 3999736, - 134: cwnd: 4045131, + 84: cwnd: 3081100, + 85: cwnd: 3083279, + 86: cwnd: 3085813, + 87: cwnd: 3088707, + 88: cwnd: 3091948, + 89: cwnd: 3095603, + 90: cwnd: 3099680, + 91: cwnd: 3104189, + 92: cwnd: 3109185, + 93: cwnd: 3114647, + 94: cwnd: 3120636, + 95: cwnd: 3127155, + 96: cwnd: 3134238, + 97: cwnd: 3141898, + 98: cwnd: 3150171, + 99: cwnd: 3159064, + 100: cwnd: 3168618, + 101: cwnd: 3178833, + 102: cwnd: 3189759, + 103: cwnd: 3201395, + 104: cwnd: 3213776, + 105: cwnd: 3226930, + 106: cwnd: 3240866, + 107: cwnd: 3255616, + 108: cwnd: 3271205, + 109: cwnd: 3287647, + 110: cwnd: 3304973, + 111: cwnd: 3323207, + 112: cwnd: 3342363, + 113: cwnd: 3362472, + 114: cwnd: 3383560, + 115: cwnd: 3405635, + 116: cwnd: 3428735, + 117: cwnd: 3452877, + 118: cwnd: 3478084, + 119: cwnd: 3504379, + 120: cwnd: 3531786, + 121: cwnd: 3560333, + 122: cwnd: 3590029, + 123: cwnd: 3620909, + 124: cwnd: 3652997, + 125: cwnd: 3686314, + 126: cwnd: 3720871, + 127: cwnd: 3756705, + 128: cwnd: 3793841, + 129: cwnd: 3832292, + 130: cwnd: 3872083, + 131: cwnd: 3913241, + 132: cwnd: 3955793, + 133: cwnd: 3999748, + 134: cwnd: 4045144, ], } diff --git a/quic/s2n-quic-core/tests/recovery/snapshots/recovery_simulation__SlowStartUnlimited-CubicCongestionController.snap b/quic/s2n-quic-core/tests/recovery/snapshots/recovery_simulation__SlowStartUnlimited-CubicCongestionController.snap index aa2b9a9242..923b6e81db 100644 --- a/quic/s2n-quic-core/tests/recovery/snapshots/recovery_simulation__SlowStartUnlimited-CubicCongestionController.snap +++ b/quic/s2n-quic-core/tests/recovery/snapshots/recovery_simulation__SlowStartUnlimited-CubicCongestionController.snap @@ -4,19 +4,20 @@ expression: self --- Simulation { name: "Slow Start Unlimited", + description: "Full congestion window utilization with no congestion experienced", cc: "s2n_quic_core::recovery::cubic::CubicCongestionController", rounds: [ 0: cwnd: 12000, - 1: cwnd: 13200, - 2: cwnd: 14400, - 3: cwnd: 15600, - 4: cwnd: 16800, - 5: cwnd: 18000, - 6: cwnd: 19200, - 7: cwnd: 20400, - 8: cwnd: 21600, - 9: cwnd: 22800, - 10: cwnd: 24000, - 11: cwnd: 25200, + 1: cwnd: 25200, + 2: cwnd: 51600, + 3: cwnd: 104400, + 4: cwnd: 210000, + 5: cwnd: 421200, + 6: cwnd: 843600, + 7: cwnd: 1688400, + 8: cwnd: 3378000, + 9: cwnd: 6757200, + 10: cwnd: 13515600, + 11: cwnd: 27032400, ], }