Add a new inflight_hi_limited_in_round to QUIC BBRv2 to replace the more complex logic about when to raise inflight_hi there is today. This is more in line with BBRv2's intended inflight_hi growth, but avoids a number of problems around being app-limited or aggregation. Activated by the BBHI connection option.
Protected by gfe2_reloadable_quic_bbr2_simplify_inflight_hi.
PiperOrigin-RevId: 490084354
diff --git a/quiche/quic/core/congestion_control/bbr2_misc.cc b/quiche/quic/core/congestion_control/bbr2_misc.cc
index 6cc2852..f85c602 100644
--- a/quiche/quic/core/congestion_control/bbr2_misc.cc
+++ b/quiche/quic/core/congestion_control/bbr2_misc.cc
@@ -81,6 +81,9 @@
if (bytes_in_flight < min_bytes_in_flight_in_round_) {
min_bytes_in_flight_in_round_ = bytes_in_flight;
}
+ if (bytes_in_flight + bytes >= inflight_hi_) {
+ inflight_hi_limited_in_round_ = true;
+ }
round_trip_counter_.OnPacketSent(packet_number);
bandwidth_sampler_.OnPacketSent(sent_time, packet_number, bytes,
@@ -383,6 +386,7 @@
loss_events_in_round_ = 0;
max_bytes_delivered_in_round_ = 0;
min_bytes_in_flight_in_round_ = std::numeric_limits<uint64_t>::max();
+ inflight_hi_limited_in_round_ = false;
}
void Bbr2NetworkModel::cap_inflight_lo(QuicByteCount cap) {
diff --git a/quiche/quic/core/congestion_control/bbr2_misc.h b/quiche/quic/core/congestion_control/bbr2_misc.h
index 39d4747..579a7b5 100644
--- a/quiche/quic/core/congestion_control/bbr2_misc.h
+++ b/quiche/quic/core/congestion_control/bbr2_misc.h
@@ -157,6 +157,7 @@
bool probe_up_includes_acks_after_cwnd_limited = false;
bool probe_up_dont_exit_if_no_queue_ = false;
bool probe_up_ignore_inflight_hi = true;
+ bool probe_up_simplify_inflight_hi = false;
/*
* PROBE_RTT parameters.
@@ -489,6 +490,10 @@
return min_bytes_in_flight_in_round_;
}
+ bool inflight_hi_limited_in_round() const {
+ return inflight_hi_limited_in_round_;
+ }
+
QuicPacketNumber end_of_app_limited_phase() const {
return bandwidth_sampler_.end_of_app_limited_phase();
}
@@ -563,6 +568,9 @@
QuicByteCount min_bytes_in_flight_in_round_ =
std::numeric_limits<uint64_t>::max();
+ // True if sending was limited by inflight_hi anytime in the current round.
+ bool inflight_hi_limited_in_round_ = false;
+
// Max bandwidth in the current round. Updated once per congestion event.
QuicBandwidth bandwidth_latest_ = QuicBandwidth::Zero();
// Max bandwidth of recent rounds. Updated once per round.
diff --git a/quiche/quic/core/congestion_control/bbr2_probe_bw.cc b/quiche/quic/core/congestion_control/bbr2_probe_bw.cc
index 88e87b6..9b9ab3c 100644
--- a/quiche/quic/core/congestion_control/bbr2_probe_bw.cc
+++ b/quiche/quic/core/congestion_control/bbr2_probe_bw.cc
@@ -354,20 +354,52 @@
// the number of bytes delivered in a round is larger inflight_hi.
return;
}
- if (Params().probe_bw_check_cwnd_limited_before_aggregation_epoch) {
- if (!model_->cwnd_limited_before_aggregation_epoch()) {
- QUIC_DVLOG(3) << sender_
- << " Raising inflight_hi early return: Not cwnd limited "
- "before aggregation epoch.";
- // Not fully utilizing cwnd, so can't safely grow.
+ if (Params().probe_up_simplify_inflight_hi) {
+ // Raise inflight_hi exponentially if it was utilized this round.
+ cycle_.probe_up_acked += congestion_event.bytes_acked;
+ if (!congestion_event.end_of_round_trip) {
return;
}
- } else if (Params().probe_up_includes_acks_after_cwnd_limited) {
- // Don't continue adding bytes to probe_up_acked if the sender was not
- // app-limited after being inflight_hi limited at least once.
- if (!cycle_.probe_up_app_limited_since_inflight_hi_limited_ ||
- congestion_event.last_packet_send_state.is_app_limited) {
- cycle_.probe_up_app_limited_since_inflight_hi_limited_ = false;
+ if (!model_->inflight_hi_limited_in_round() ||
+ model_->loss_events_in_round() > 0) {
+ cycle_.probe_up_acked = 0;
+ return;
+ }
+ } else {
+ if (Params().probe_bw_check_cwnd_limited_before_aggregation_epoch) {
+ if (!model_->cwnd_limited_before_aggregation_epoch()) {
+ QUIC_DVLOG(3) << sender_
+ << " Raising inflight_hi early return: Not cwnd limited "
+ "before aggregation epoch.";
+ // Not fully utilizing cwnd, so can't safely grow.
+ return;
+ }
+ } else if (Params().probe_up_includes_acks_after_cwnd_limited) {
+ // Don't continue adding bytes to probe_up_acked if the sender was not
+ // app-limited after being inflight_hi limited at least once.
+ if (!cycle_.probe_up_app_limited_since_inflight_hi_limited_ ||
+ congestion_event.last_packet_send_state.is_app_limited) {
+ cycle_.probe_up_app_limited_since_inflight_hi_limited_ = false;
+ if (congestion_event.prior_bytes_in_flight <
+ congestion_event.prior_cwnd) {
+ QUIC_DVLOG(3)
+ << sender_
+ << " Raising inflight_hi early return: Not cwnd limited.";
+ // Not fully utilizing cwnd, so can't safely grow.
+ return;
+ }
+
+ if (congestion_event.prior_cwnd < model_->inflight_hi()) {
+ QUIC_DVLOG(3) << sender_
+ << " Raising inflight_hi early return: inflight_hi not "
+ "fully used.";
+ // Not fully using inflight_hi, so don't grow it.
+ return;
+ }
+ }
+ // Start a new period of adding bytes_acked, because inflight_hi limited.
+ cycle_.probe_up_app_limited_since_inflight_hi_limited_ = true;
+ } else {
if (congestion_event.prior_bytes_in_flight <
congestion_event.prior_cwnd) {
QUIC_DVLOG(3) << sender_
@@ -375,36 +407,21 @@
// Not fully utilizing cwnd, so can't safely grow.
return;
}
-
- if (congestion_event.prior_cwnd < model_->inflight_hi()) {
- QUIC_DVLOG(3)
- << sender_
- << " Raising inflight_hi early return: inflight_hi not fully used.";
- // Not fully using inflight_hi, so don't grow it.
- return;
- }
}
- // Start a new period of adding bytes_acked, because inflight_hi limited.
- cycle_.probe_up_app_limited_since_inflight_hi_limited_ = true;
- } else {
- if (congestion_event.prior_bytes_in_flight < congestion_event.prior_cwnd) {
- QUIC_DVLOG(3) << sender_
- << " Raising inflight_hi early return: Not cwnd limited.";
- // Not fully utilizing cwnd, so can't safely grow.
+
+ if (congestion_event.prior_cwnd < model_->inflight_hi()) {
+ QUIC_DVLOG(3)
+ << sender_
+ << " Raising inflight_hi early return: inflight_hi not fully used.";
+ // Not fully using inflight_hi, so don't grow it.
return;
}
+
+ // Increase inflight_hi by the number of probe_up_bytes within
+ // probe_up_acked.
+ cycle_.probe_up_acked += congestion_event.bytes_acked;
}
- if (congestion_event.prior_cwnd < model_->inflight_hi()) {
- QUIC_DVLOG(3)
- << sender_
- << " Raising inflight_hi early return: inflight_hi not fully used.";
- // Not fully using inflight_hi, so don't grow it.
- return;
- }
-
- // Increase inflight_hi by the number of probe_up_bytes within probe_up_acked.
- cycle_.probe_up_acked += congestion_event.bytes_acked;
if (cycle_.probe_up_acked >= cycle_.probe_up_bytes) {
uint64_t delta = cycle_.probe_up_acked / cycle_.probe_up_bytes;
cycle_.probe_up_acked -= delta * cycle_.probe_up_bytes;
diff --git a/quiche/quic/core/congestion_control/bbr2_sender.cc b/quiche/quic/core/congestion_control/bbr2_sender.cc
index 33fe2e6..86e00ed 100644
--- a/quiche/quic/core/congestion_control/bbr2_sender.cc
+++ b/quiche/quic/core/congestion_control/bbr2_sender.cc
@@ -203,6 +203,14 @@
// Derived constant to ensure fairness.
params_.probe_bw_probe_down_pacing_gain = 0.91;
}
+ if (GetQuicReloadableFlag(quic_bbr2_simplify_inflight_hi) &&
+ ContainsQuicTag(connection_options, kBBHI)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_simplify_inflight_hi);
+ params_.probe_up_simplify_inflight_hi = true;
+ // Simplify inflight_hi is intended as an alternative to ignoring it,
+ // so ensure we're not ignoring it.
+ params_.probe_up_ignore_inflight_hi = false;
+ }
}
Limits<QuicByteCount> Bbr2Sender::GetCwndLimitsByMode() const {
diff --git a/quiche/quic/core/congestion_control/bbr2_simulator_test.cc b/quiche/quic/core/congestion_control/bbr2_simulator_test.cc
index d1cf602..08237bb 100644
--- a/quiche/quic/core/congestion_control/bbr2_simulator_test.cc
+++ b/quiche/quic/core/congestion_control/bbr2_simulator_test.cc
@@ -989,6 +989,120 @@
sender_->ExportDebugState().bandwidth_hi, 0.91f);
}
+// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with BBHI
+TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncreaseBBHI)) {
+ SetQuicReloadableFlag(quic_bbr2_simplify_inflight_hi, true);
+ SetConnectionOption(kBBHI);
+ 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(Bbr2ModeIsOneOf({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.30);
+
+ // 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.02f);
+}
+
+// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with BBHI
+// in the presence of ACK aggregation.
+TEST_F(Bbr2DefaultTopologyTest,
+ QUIC_SLOW_TEST(BandwidthIncreaseBBHIAggregation)) {
+ SetQuicReloadableFlag(quic_bbr2_simplify_inflight_hi, true);
+ SetConnectionOption(kBBHI);
+ 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(Bbr2ModeIsOneOf({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.60f);
+ EXPECT_LE(sender_loss_rate_in_packets(), 0.35);
+
+ // 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.90f);
+}
+
+// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with BBHI
+// and B202, which changes the exit criteria to be based on
+// min_bytes_in_flight_in_round, in the presence of ACK aggregation.
+TEST_F(Bbr2DefaultTopologyTest,
+ QUIC_SLOW_TEST(BandwidthIncreaseBBHI_B202Aggregation)) {
+ SetQuicReloadableFlag(quic_bbr2_simplify_inflight_hi, true);
+ SetConnectionOption(kBBHI);
+ SetConnectionOption(kB202);
+ 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(Bbr2ModeIsOneOf({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.60f);
+ EXPECT_LE(sender_loss_rate_in_packets(), 0.35);
+
+ // 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 18% of the bandwidth is discovered.
+ EXPECT_APPROX_EQ(params.test_link.bandwidth,
+ sender_->ExportDebugState().bandwidth_hi, 0.85f);
+}
+
// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B204
TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncreaseB204)) {
SetConnectionOption(kB204);
diff --git a/quiche/quic/core/crypto/crypto_protocol.h b/quiche/quic/core/crypto/crypto_protocol.h
index d72183c..bc9556d 100644
--- a/quiche/quic/core/crypto/crypto_protocol.h
+++ b/quiche/quic/core/crypto/crypto_protocol.h
@@ -127,6 +127,9 @@
const QuicTag kBBQ0 = TAG('B', 'B', 'Q', '0'); // Increase bytes_acked in
// PROBE_UP when app limited.
const QuicTag kBBPD = TAG('B', 'B', 'P', 'D'); // Use 0.91 PROBE_DOWN gain.
+const QuicTag kBBHI = TAG('B', 'B', 'H', 'I'); // Increase inflight_hi in
+ // PROBE_UP if ever inflight_hi
+ // limited in round
const QuicTag kRENO = TAG('R', 'E', 'N', 'O'); // Reno Congestion Control
const QuicTag kTPCC = TAG('P', 'C', 'C', '\0'); // Performance-Oriented
// Congestion Control
diff --git a/quiche/quic/core/quic_flags_list.h b/quiche/quic/core/quic_flags_list.h
index 7f855cb..d88f628 100644
--- a/quiche/quic/core/quic_flags_list.h
+++ b/quiche/quic/core/quic_flags_list.h
@@ -95,6 +95,8 @@
QUIC_FLAG(quic_reloadable_flag_quic_default_to_bbr, false)
// When true, support draft-ietf-quic-v2-01
QUIC_FLAG(quic_reloadable_flag_quic_enable_version_2_draft_01, false)
+// When true, the BBHI copt causes QUIC BBRv2 to use a simpler algorithm for raising inflight_hi in PROBE_UP.
+QUIC_FLAG(quic_reloadable_flag_quic_bbr2_simplify_inflight_hi, true)
// When true, the BBR4 copt sets the extra_acked window to 20 RTTs and BBR5 sets it to 40 RTTs.
QUIC_FLAG(quic_reloadable_flag_quic_bbr2_extra_acked_window, true)