Use max(max_bw, send_rate) as the estimated bandwidth in QUIC's MaxAckHeightTracker. Enabled by the BBRB connection option.
Protected by FLAGS_quic_reloadable_flag_quic_bbr_use_send_rate_in_max_ack_height_tracker.
PiperOrigin-RevId: 400206122
diff --git a/quic/core/congestion_control/bandwidth_sampler.cc b/quic/core/congestion_control/bandwidth_sampler.cc
index 81f9e91..49f2b72 100644
--- a/quic/core/congestion_control/bandwidth_sampler.cc
+++ b/quic/core/congestion_control/bandwidth_sampler.cc
@@ -112,7 +112,8 @@
unacked_packet_map_(unacked_packet_map),
max_ack_height_tracker_(max_height_tracker_window_length),
total_bytes_acked_after_last_ack_event_(0),
- overestimate_avoidance_(false) {}
+ overestimate_avoidance_(false),
+ limit_max_ack_height_tracker_by_send_rate_(false) {}
BandwidthSampler::BandwidthSampler(const BandwidthSampler& other)
: total_bytes_sent_(other.total_bytes_sent_),
@@ -135,7 +136,9 @@
max_ack_height_tracker_(other.max_ack_height_tracker_),
total_bytes_acked_after_last_ack_event_(
other.total_bytes_acked_after_last_ack_event_),
- overestimate_avoidance_(other.overestimate_avoidance_) {}
+ overestimate_avoidance_(other.overestimate_avoidance_),
+ limit_max_ack_height_tracker_by_send_rate_(
+ other.limit_max_ack_height_tracker_by_send_rate_) {}
void BandwidthSampler::EnableOverestimateAvoidance() {
if (overestimate_avoidance_) {
@@ -264,6 +267,7 @@
}
SendTimeState last_acked_packet_send_state;
+ QuicBandwidth max_send_rate = QuicBandwidth::Zero();
for (const auto& packet : acked_packets) {
BandwidthSample sample =
OnPacketAcknowledged(ack_time, packet.packet_number);
@@ -280,6 +284,9 @@
event_sample.sample_max_bandwidth = sample.bandwidth;
event_sample.sample_is_app_limited = sample.state_at_send.is_app_limited;
}
+ if (!sample.send_rate.IsInfinite()) {
+ max_send_rate = std::max(max_send_rate, sample.send_rate);
+ }
const QuicByteCount inflight_sample =
total_bytes_acked() - last_acked_packet_send_state.total_bytes_acked;
if (inflight_sample > event_sample.sample_max_inflight) {
@@ -303,6 +310,9 @@
}
max_bandwidth = std::max(max_bandwidth, event_sample.sample_max_bandwidth);
+ if (limit_max_ack_height_tracker_by_send_rate_) {
+ max_bandwidth = std::max(max_bandwidth, max_send_rate);
+ }
event_sample.extra_acked = OnAckEventEnd(
std::min(est_bandwidth_upper_bound, max_bandwidth), round_trip_count);
@@ -433,6 +443,7 @@
// means that the RTT measurements here can be artificially high, especially
// on low bandwidth connections.
sample.rtt = ack_time - sent_packet.sent_time;
+ sample.send_rate = send_rate;
SentPacketToSendTimeState(sent_packet, &sample.state_at_send);
if (sample.bandwidth.IsZero()) {
diff --git a/quic/core/congestion_control/bandwidth_sampler.h b/quic/core/congestion_control/bandwidth_sampler.h
index 4c72939..bef8250 100644
--- a/quic/core/congestion_control/bandwidth_sampler.h
+++ b/quic/core/congestion_control/bandwidth_sampler.h
@@ -81,17 +81,18 @@
struct QUIC_EXPORT_PRIVATE BandwidthSample {
// The bandwidth at that particular sample. Zero if no valid bandwidth sample
// is available.
- QuicBandwidth bandwidth;
+ QuicBandwidth bandwidth = QuicBandwidth::Zero();
// The RTT measurement at this particular sample. Zero if no RTT sample is
// available. Does not correct for delayed ack time.
- QuicTime::Delta rtt;
+ QuicTime::Delta rtt = QuicTime::Delta::Zero();
+
+ // |send_rate| is computed from the current packet being acked('P') and an
+ // earlier packet that is acked before P was sent.
+ QuicBandwidth send_rate = QuicBandwidth::Infinite();
// States captured when the packet was sent.
SendTimeState state_at_send;
-
- BandwidthSample()
- : bandwidth(QuicBandwidth::Zero()), rtt(QuicTime::Delta::Zero()) {}
};
// MaxAckHeightTracker is part of the BandwidthSampler. It is called after every
@@ -368,6 +369,10 @@
max_ack_height_tracker_.SetStartNewAggregationEpochAfterFullRound(value);
}
+ void SetLimitMaxAckHeightTrackerBySendRate(bool value) {
+ limit_max_ack_height_tracker_by_send_rate_ = value;
+ }
+
// AckPoint represents a point on the ack line.
struct QUIC_NO_EXPORT AckPoint {
QuicTime ack_time = QuicTime::Zero();
@@ -579,6 +584,9 @@
// True if connection option 'BSAO' is set.
bool overestimate_avoidance_;
+
+ // True if connection option 'BBRB' is set.
+ bool limit_max_ack_height_tracker_by_send_rate_;
};
} // namespace quic
diff --git a/quic/core/congestion_control/bbr2_misc.h b/quic/core/congestion_control/bbr2_misc.h
index b3a5679..fb66b98 100644
--- a/quic/core/congestion_control/bbr2_misc.h
+++ b/quic/core/congestion_control/bbr2_misc.h
@@ -422,6 +422,10 @@
bandwidth_sampler_.SetStartNewAggregationEpochAfterFullRound(value);
}
+ void SetLimitMaxAckHeightTrackerBySendRate(bool value) {
+ bandwidth_sampler_.SetLimitMaxAckHeightTrackerBySendRate(value);
+ }
+
bool MaybeExpireMinRtt(const Bbr2CongestionEvent& congestion_event);
QuicBandwidth BandwidthEstimate() const {
diff --git a/quic/core/congestion_control/bbr2_sender.cc b/quic/core/congestion_control/bbr2_sender.cc
index b0f664f..f1c96d6 100644
--- a/quic/core/congestion_control/bbr2_sender.cc
+++ b/quic/core/congestion_control/bbr2_sender.cc
@@ -170,6 +170,12 @@
ContainsQuicTag(connection_options, kBBRA)) {
model_.SetStartNewAggregationEpochAfterFullRound(true);
}
+ if (GetQuicReloadableFlag(quic_bbr_use_send_rate_in_max_ack_height_tracker) &&
+ ContainsQuicTag(connection_options, kBBRB)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(
+ quic_bbr_use_send_rate_in_max_ack_height_tracker, 2, 2);
+ model_.SetLimitMaxAckHeightTrackerBySendRate(true);
+ }
if (GetQuicReloadableFlag(
quic_bbr2_add_bytes_acked_after_inflight_hi_limited) &&
ContainsQuicTag(connection_options, kBBQ0)) {
diff --git a/quic/core/congestion_control/bbr2_simulator_test.cc b/quic/core/congestion_control/bbr2_simulator_test.cc
index 4d9e020..3b08905 100644
--- a/quic/core/congestion_control/bbr2_simulator_test.cc
+++ b/quic/core/congestion_control/bbr2_simulator_test.cc
@@ -463,6 +463,26 @@
EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
}
+TEST_F(Bbr2DefaultTopologyTest, SimpleTransferBBRB) {
+ SetQuicReloadableFlag(quic_bbr_use_send_rate_in_max_ack_height_tracker, true);
+ SetConnectionOption(kBBRB);
+ DefaultTopologyParams params;
+ CreateNetwork(params);
+
+ // Transfer 12MB.
+ DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
+ EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
+
+ EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
+ sender_->ExportDebugState().bandwidth_hi, 0.01f);
+
+ EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
+ // The margin here is high, because the aggregation greatly increases
+ // smoothed rtt.
+ EXPECT_GE(params.RTT() * 4, rtt_stats()->smoothed_rtt());
+ EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
+}
+
TEST_F(Bbr2DefaultTopologyTest, SimpleTransferSmallBuffer) {
DefaultTopologyParams params;
params.switch_queue_capacity_in_bdp = 0.5;
diff --git a/quic/core/congestion_control/bbr_sender.cc b/quic/core/congestion_control/bbr_sender.cc
index 4d6ae70..ba7a357 100644
--- a/quic/core/congestion_control/bbr_sender.cc
+++ b/quic/core/congestion_control/bbr_sender.cc
@@ -283,6 +283,12 @@
ContainsQuicTag(connection_options, kBBRA)) {
sampler_.SetStartNewAggregationEpochAfterFullRound(true);
}
+ if (GetQuicReloadableFlag(quic_bbr_use_send_rate_in_max_ack_height_tracker) &&
+ ContainsQuicTag(connection_options, kBBRB)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(
+ quic_bbr_use_send_rate_in_max_ack_height_tracker, 1, 2);
+ sampler_.SetLimitMaxAckHeightTrackerBySendRate(true);
+ }
}
void BbrSender::AdjustNetworkParameters(const NetworkParams& params) {
diff --git a/quic/core/congestion_control/bbr_sender_test.cc b/quic/core/congestion_control/bbr_sender_test.cc
index ed57eb4..754cf20 100644
--- a/quic/core/congestion_control/bbr_sender_test.cc
+++ b/quic/core/congestion_control/bbr_sender_test.cc
@@ -329,6 +329,39 @@
EXPECT_APPROX_EQ(kTestRtt, rtt_stats_->smoothed_rtt(), 0.2f);
}
+TEST_F(BbrSenderTest, SimpleTransferBBRB) {
+ SetQuicReloadableFlag(quic_bbr_use_send_rate_in_max_ack_height_tracker, true);
+ SetConnectionOption(kBBRB);
+ CreateDefaultSetup();
+
+ // At startup make sure we are at the default.
+ EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_->CanSend(0));
+ // And that window is un-affected.
+ EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
+
+ // Verify that Sender is in slow start.
+ EXPECT_TRUE(sender_->InSlowStart());
+
+ // Verify that pacing rate is based on the initial RTT.
+ QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta(
+ 2.885 * kDefaultWindowTCP, rtt_stats_->initial_rtt());
+ EXPECT_APPROX_EQ(expected_pacing_rate.ToBitsPerSecond(),
+ sender_->PacingRate(0).ToBitsPerSecond(), 0.01f);
+
+ ASSERT_GE(kTestBdp, kDefaultWindowTCP + kDefaultTCPMSS);
+
+ DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(30));
+ EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+ EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost);
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+
+ // The margin here is quite high, since there exists a possibility that the
+ // connection just exited high gain cycle.
+ EXPECT_APPROX_EQ(kTestRtt, rtt_stats_->smoothed_rtt(), 0.2f);
+}
+
// Test a simple transfer in a situation when the buffer is less than BDP.
TEST_F(BbrSenderTest, SimpleTransferSmallBuffer) {
CreateSmallBufferSetup();
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index 7719644..2afa363 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -102,6 +102,8 @@
const QuicTag kBBRA = TAG('B', 'B', 'R', 'A'); // Starts a new ack aggregation
// epoch if a full round has
// passed
+const QuicTag kBBRB = TAG('B', 'B', 'R', 'B'); // Use send rate in BBR's
+ // MaxAckHeightTracker
const QuicTag kBBRS = TAG('B', 'B', 'R', 'S'); // DEPRECATED
const QuicTag kBBQ1 = TAG('B', 'B', 'Q', '1'); // BBR with lower 2.77 STARTUP
// pacing and CWND gain.
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 70e8e8e..727f1e2 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -107,6 +107,8 @@
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_suppress_write_mid_packet_processing, true)
// If true, use BBRv2 as the default congestion controller. Takes precedence over --quic_default_to_bbr.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_to_bbr_v2, false)
+// If true, use max(max_bw, send_rate) as the estimated bandwidth in QUIC\'s MaxAckHeightTracker.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr_use_send_rate_in_max_ack_height_tracker, true)
// If true, use new connection ID in connection migration.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_connection_migration_use_new_cid_v2, true)
// If true, uses conservative cwnd gain and pacing gain when cwnd gets bootstrapped.