Update CWND calculations to match the latest BBR draft. https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html Removes B205 from BBRv3, because it's default enabled. Change B2HR to set `inflight_hi_headroom` to 0.10 instead of 0.15, because 0.15 is the default. PiperOrigin-RevId: 911969463
diff --git a/quiche/quic/core/congestion_control/bbr2_misc.h b/quiche/quic/core/congestion_control/bbr2_misc.h index e9ee27b..9d78bee 100644 --- a/quiche/quic/core/congestion_control/bbr2_misc.h +++ b/quiche/quic/core/congestion_control/bbr2_misc.h
@@ -112,6 +112,7 @@ // If true, include extra acked during STARTUP and proactively reduce extra // acked when bandwidth increases. + // Always true in BBR3, so this value is ignored. bool startup_include_extra_acked = false;
diff --git a/quiche/quic/core/congestion_control/bbr3_sender.cc b/quiche/quic/core/congestion_control/bbr3_sender.cc index 1f248f6..4fe8829 100644 --- a/quiche/quic/core/congestion_control/bbr3_sender.cc +++ b/quiche/quic/core/congestion_control/bbr3_sender.cc
@@ -62,7 +62,6 @@ // The BBR IETF draft uses 0.9 as the PROBE_DOWN pacing gain. params_.probe_bw_probe_down_pacing_gain = 0.9f; // STARTUP has a shorter MaxAckHeightTracker window of 1 round. - params_.startup_include_extra_acked = true; model_.SetMaxAckHeightTrackerWindowLength(1); // Use the derived startup pacing gain in the IETF draft. model_.set_pacing_gain(kDerivedStartupPacingGain); @@ -94,7 +93,8 @@ params_.startup_full_bw_rounds = 2; } if (ContainsQuicTag(connection_options, kB2HR)) { - params_.inflight_hi_headroom = 0.15; + // The default is 0.15. + params_.inflight_hi_headroom = 0.10; } if (ContainsQuicTag(connection_options, kICW1)) { max_cwnd_when_network_parameters_adjusted_ = 100 * kDefaultTCPMSS; @@ -148,15 +148,15 @@ if (ContainsQuicTag(connection_options, kB202)) { params_.max_probe_up_queue_rounds = 1; } + if (ContainsQuicTag(connection_options, kBB2U)) { + params_.max_probe_up_queue_rounds = 2; + } if (ContainsQuicTag(connection_options, kB203)) { params_.probe_up_ignore_inflight_hi = false; } if (ContainsQuicTag(connection_options, kB204)) { model_.SetReduceExtraAckedOnBandwidthIncrease(true); } - if (ContainsQuicTag(connection_options, kB205)) { - params_.startup_include_extra_acked = true; - } if (ContainsQuicTag(connection_options, kBBRA)) { model_.SetStartNewAggregationEpochAfterFullRound(true); } @@ -175,9 +175,6 @@ // so ensure we're not ignoring it. params_.probe_up_ignore_inflight_hi = false; } - if (ContainsQuicTag(connection_options, kBB2U)) { - params_.max_probe_up_queue_rounds = 2; - } } Limits<QuicByteCount> Bbr3Sender::GetCwndLimitsByMode() const { @@ -425,24 +422,28 @@ } void Bbr3Sender::UpdateCongestionWindow(QuicByteCount bytes_acked) { - QuicByteCount target_cwnd = GetTargetCongestionWindow(model_.cwnd_gain()); + // From "Congestion Window" section of bbr3-draft. + QuicByteCount max_inflight = + model_.BDP(model_.BandwidthEstimate(), model_.cwnd_gain()) + + model_.MaxAckHeight(); + max_inflight = ApplyQuantizationBudget(max_inflight); const QuicByteCount prior_cwnd = cwnd_; - if (model_.full_bandwidth_reached() || Params().startup_include_extra_acked) { - target_cwnd += model_.MaxAckHeight(); - cwnd_ = std::min(prior_cwnd + bytes_acked, target_cwnd); - } else if (prior_cwnd < target_cwnd || prior_cwnd < 2 * initial_cwnd_) { - cwnd_ = prior_cwnd + bytes_acked; + // Never increase CWND by more than bytes_acked. + if (model_.full_bandwidth_reached()) { + cwnd_ = std::min(cwnd_ + bytes_acked, max_inflight); + } else if (cwnd_ < max_inflight || + model_.total_bytes_acked() < initial_cwnd_) { + // For the first flight of packets in STARTUP, increase CWND by bytes_acked. + cwnd_ = cwnd_ + bytes_acked; } const QuicByteCount desired_cwnd = cwnd_; - cwnd_ = GetCwndLimitsByMode().ApplyLimits(cwnd_); const QuicByteCount model_limited_cwnd = cwnd_; - cwnd_ = params_.cwnd_limits.ApplyLimits(cwnd_); - QUIC_DVLOG(3) << this << " Updating CWND. target_cwnd:" << target_cwnd + QUIC_DVLOG(3) << this << " Updating CWND. max_inflight:" << max_inflight << ", max_ack_height:" << model_.MaxAckHeight() << ", full_bw:" << model_.full_bandwidth_reached() << ", bytes_acked:" << bytes_acked @@ -453,9 +454,14 @@ << " => (final_cwnd) " << cwnd_; } -QuicByteCount Bbr3Sender::GetTargetCongestionWindow(float gain) const { - return std::max(model_.BDP(model_.BandwidthEstimate(), gain), - params_.cwnd_limits.Min()); +QuicByteCount Bbr3Sender::ApplyQuantizationBudget( + QuicByteCount inflight_cap) const { + // TODO(ianswett): Ensure inflight_cap is at least the offload budget. + inflight_cap = std::max(inflight_cap, kDefaultMinimumCongestionWindow); + if (mode_ == Bbr2Mode::PROBE_BW && probe_bw_.phase == ProbePhase::PROBE_UP) { + inflight_cap += 2 * kDefaultTCPMSS; + } + return inflight_cap; } void Bbr3Sender::OnPacketSent(QuicTime sent_time, QuicByteCount bytes_in_flight,
diff --git a/quiche/quic/core/congestion_control/bbr3_sender.h b/quiche/quic/core/congestion_control/bbr3_sender.h index a3aea6a..ab4243d 100644 --- a/quiche/quic/core/congestion_control/bbr3_sender.h +++ b/quiche/quic/core/congestion_control/bbr3_sender.h
@@ -119,7 +119,7 @@ private: void UpdatePacingRate(QuicByteCount bytes_acked); void UpdateCongestionWindow(QuicByteCount bytes_acked); - QuicByteCount GetTargetCongestionWindow(float gain) const; + QuicByteCount ApplyQuantizationBudget(QuicByteCount inflight_cap) const; // Helper function for Bbr2Mode transitions. void LeaveStartup(QuicTime now); Bbr2Mode OnCongestionEventStartup(
diff --git a/quiche/quic/core/congestion_control/bbr3_simulator_test.cc b/quiche/quic/core/congestion_control/bbr3_simulator_test.cc index 9818b12..402dedf 100644 --- a/quiche/quic/core/congestion_control/bbr3_simulator_test.cc +++ b/quiche/quic/core/congestion_control/bbr3_simulator_test.cc
@@ -379,7 +379,7 @@ ASSERT_TRUE(simulator_result); const auto debug_state = sender_->ExportDebugState(); EXPECT_EQ(Bbr2Mode::DRAIN, debug_state.mode); - EXPECT_EQ(2u, debug_state.round_trip_count - max_bw_round); + EXPECT_EQ(3u, debug_state.round_trip_count - max_bw_round); EXPECT_EQ(3u, debug_state.startup.round_trips_without_bandwidth_growth); EXPECT_EQ(0u, sender_connection_stats().packets_lost); EXPECT_APPROX_EQ(params.BottleneckBandwidth(), debug_state.bandwidth_hi, @@ -789,7 +789,7 @@ // This is much farther off when aggregation is present, // Ideally BSAO or another option would fix this. EXPECT_APPROX_EQ(params.test_link.bandwidth, - sender_->ExportDebugState().bandwidth_est, 0.6f); + sender_->ExportDebugState().bandwidth_est, 0.8f); EXPECT_LE(sender_loss_rate_in_packets(), 0.35); // Now increase the bottleneck bandwidth from 100Kbps to 10Mbps. @@ -1056,76 +1056,6 @@ sender_->ExportDebugState().bandwidth_hi, 0.95f); } -// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B205 -TEST_F(Bbr3DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncreaseB205)) { - SetConnectionOption(kB205); - DefaultTopologyParams params; - params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000); - params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100); - CreateNetwork(params); - - sender_endpoint_.AddBytesToTransfer(10 * 1024 * 1024); - - simulator_.RunFor(QuicTime::Delta::FromSeconds(15)); - EXPECT_TRUE(Bbr3ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); - QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow(); - - EXPECT_APPROX_EQ(params.test_link.bandwidth, - sender_->ExportDebugState().bandwidth_est, 0.1f); - EXPECT_LE(sender_loss_rate_in_packets(), 0.10); - - // Now increase the bottleneck bandwidth from 100Kbps to 10Mbps. - params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000); - TestLink()->set_bandwidth(params.test_link.bandwidth); - - bool simulator_result = simulator_.RunUntilOrTimeout( - [this]() { return sender_endpoint_.bytes_to_transfer() == 0; }, - QuicTime::Delta::FromSeconds(50)); - EXPECT_TRUE(simulator_result); - // Ensure the full bandwidth is discovered. - EXPECT_APPROX_EQ(params.test_link.bandwidth, - sender_->ExportDebugState().bandwidth_hi, 0.1f); -} - -// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B205 -// in the presence of ACK aggregation. -TEST_F(Bbr3DefaultTopologyTest, - QUIC_SLOW_TEST(BandwidthIncreaseB205Aggregation)) { - SetConnectionOption(kB205); - DefaultTopologyParams params; - params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000); - params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100); - CreateNetwork(params); - - // 2 RTTs of aggregation, with a max of 10kb. - EnableAggregation(10 * 1024, 2 * params.RTT()); - - // Reduce the payload to 2MB because 10MB takes too long. - sender_endpoint_.AddBytesToTransfer(2 * 1024 * 1024); - - simulator_.RunFor(QuicTime::Delta::FromSeconds(15)); - EXPECT_TRUE(Bbr3ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); - QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow(); - - // This is much farther off when aggregation is present, - // Ideally BSAO or another option would fix this. - EXPECT_APPROX_EQ(params.test_link.bandwidth, - sender_->ExportDebugState().bandwidth_est, 0.45f); - EXPECT_LE(sender_loss_rate_in_packets(), 0.15); - - // Now increase the bottleneck bandwidth from 100Kbps to 10Mbps. - params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000); - TestLink()->set_bandwidth(params.test_link.bandwidth); - - bool simulator_result = simulator_.RunUntilOrTimeout( - [this]() { return sender_endpoint_.bytes_to_transfer() == 0; }, - QuicTime::Delta::FromSeconds(50)); - EXPECT_TRUE(simulator_result); - // Ensure at least 5% of full bandwidth is discovered. - EXPECT_APPROX_EQ(params.test_link.bandwidth, - sender_->ExportDebugState().bandwidth_hi, 0.9f); -} - // Test Bbr2's reaction to a 100x bandwidth increase during a transfer with BB2U TEST_F(Bbr3DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncreaseBB2U)) { SetConnectionOption(kBB2U); @@ -1180,7 +1110,7 @@ // This is much farther off when aggregation is present, // Ideally BSAO or another option would fix this. EXPECT_APPROX_EQ(params.test_link.bandwidth, - sender_->ExportDebugState().bandwidth_est, 0.45f); + sender_->ExportDebugState().bandwidth_est, 0.8f); EXPECT_LE(sender_loss_rate_in_packets(), 0.30); // Now increase the bottleneck bandwidth from 100Kbps to 10Mbps. @@ -1220,7 +1150,7 @@ // This is much farther off when aggregation is present, // Ideally BSAO or another option would fix this. EXPECT_APPROX_EQ(params.test_link.bandwidth, - sender_->ExportDebugState().bandwidth_est, 0.45f); + sender_->ExportDebugState().bandwidth_est, 0.80f); EXPECT_LE(sender_loss_rate_in_packets(), 0.30); // Now increase the bottleneck bandwidth from 100Kbps to 10Mbps. @@ -1540,7 +1470,7 @@ const auto debug_state = sender_->ExportDebugState(); EXPECT_EQ(Bbr2Mode::DRAIN, debug_state.mode); EXPECT_GE(2u, debug_state.round_trip_count - max_bw_round); - EXPECT_EQ(2u, debug_state.startup.round_trips_without_bandwidth_growth); + EXPECT_EQ(1u, debug_state.startup.round_trips_without_bandwidth_growth); EXPECT_NE(0u, sender_connection_stats().packets_lost); EXPECT_FALSE(debug_state.last_sample_is_app_limited); @@ -1573,7 +1503,7 @@ const auto debug_state = sender_->ExportDebugState(); EXPECT_EQ(Bbr2Mode::DRAIN, debug_state.mode); EXPECT_GE(2u, debug_state.round_trip_count - max_bw_round); - EXPECT_EQ(2u, debug_state.startup.round_trips_without_bandwidth_growth); + EXPECT_EQ(1u, debug_state.startup.round_trips_without_bandwidth_growth); EXPECT_NE(0u, sender_connection_stats().packets_lost); EXPECT_FALSE(debug_state.last_sample_is_app_limited);