When true, the B204 connection option reduces the MaxAckHeight filter's extra acked when MaxBW increases and B205 causes BBRv2 to include extra acked in STARTUP's CWND.
Protected by quic_reloadable_flag_quic_bbr2_startup_extra_acked.
PiperOrigin-RevId: 400933672
diff --git a/quic/core/congestion_control/bandwidth_sampler.cc b/quic/core/congestion_control/bandwidth_sampler.cc
index 49f2b72..240e9dd 100644
--- a/quic/core/congestion_control/bandwidth_sampler.cc
+++ b/quic/core/congestion_control/bandwidth_sampler.cc
@@ -24,12 +24,40 @@
}
QuicByteCount MaxAckHeightTracker::Update(
- QuicBandwidth bandwidth_estimate, QuicRoundTripCount round_trip_count,
+ QuicBandwidth bandwidth_estimate, bool is_new_max_bandwidth,
+ QuicRoundTripCount round_trip_count,
QuicPacketNumber last_sent_packet_number,
QuicPacketNumber last_acked_packet_number, QuicTime ack_time,
QuicByteCount bytes_acked) {
bool force_new_epoch = false;
+ if (reduce_extra_acked_on_bandwidth_increase_ && is_new_max_bandwidth) {
+ // Save and clear existing entries.
+ ExtraAckedEvent best = max_ack_height_filter_.GetBest();
+ ExtraAckedEvent second_best = max_ack_height_filter_.GetSecondBest();
+ ExtraAckedEvent third_best = max_ack_height_filter_.GetThirdBest();
+ max_ack_height_filter_.Clear();
+
+ // Reinsert the heights into the filter after recalculating.
+ QuicByteCount expected_bytes_acked = bandwidth_estimate * best.time_delta;
+ if (expected_bytes_acked < best.bytes_acked) {
+ best.extra_acked = best.bytes_acked - expected_bytes_acked;
+ max_ack_height_filter_.Update(best, best.round);
+ }
+ expected_bytes_acked = bandwidth_estimate * second_best.time_delta;
+ if (expected_bytes_acked < second_best.bytes_acked) {
+ QUICHE_DCHECK_LE(best.round, second_best.round);
+ second_best.extra_acked = second_best.bytes_acked - expected_bytes_acked;
+ max_ack_height_filter_.Update(second_best, second_best.round);
+ }
+ expected_bytes_acked = bandwidth_estimate * third_best.time_delta;
+ if (expected_bytes_acked < third_best.bytes_acked) {
+ QUICHE_DCHECK_LE(second_best.round, third_best.round);
+ third_best.extra_acked = third_best.bytes_acked - expected_bytes_acked;
+ max_ack_height_filter_.Update(third_best, third_best.round);
+ }
+ }
+
// If any packet sent after the start of the epoch has been acked, start a new
// epoch.
if (start_new_aggregation_epoch_after_full_round_ &&
@@ -42,6 +70,11 @@
"last_sent_packet_number_before_epoch_:"
<< last_sent_packet_number_before_epoch_
<< ", last_acked_packet_number:" << last_acked_packet_number;
+ if (reduce_extra_acked_on_bandwidth_increase_) {
+ QUIC_BUG(quic_bwsampler_46)
+ << "A full round of aggregation should never "
+ << "pass with startup_include_extra_acked(B204) enabled.";
+ }
force_new_epoch = true;
}
if (aggregation_epoch_start_time_ == QuicTime::Zero() || force_new_epoch) {
@@ -54,8 +87,8 @@
// Compute how many bytes are expected to be delivered, assuming max bandwidth
// is correct.
- QuicByteCount expected_bytes_acked =
- bandwidth_estimate * (ack_time - aggregation_epoch_start_time_);
+ QuicTime::Delta aggregation_delta = ack_time - aggregation_epoch_start_time_;
+ QuicByteCount expected_bytes_acked = bandwidth_estimate * aggregation_delta;
// Reset the current aggregation epoch as soon as the ack arrival rate is less
// than or equal to the max bandwidth.
if (aggregation_epoch_bytes_ <=
@@ -68,8 +101,7 @@
<< ack_aggregation_bandwidth_threshold_
<< ", expected_bytes_acked:" << expected_bytes_acked
<< ", bandwidth_estimate:" << bandwidth_estimate
- << ", aggregation_duration:"
- << (ack_time - aggregation_epoch_start_time_)
+ << ", aggregation_duration:" << aggregation_delta
<< ", new_aggregation_epoch:" << ack_time
<< ", new_aggregation_bytes_acked:" << bytes_acked;
// Reset to start measuring a new aggregation epoch.
@@ -92,7 +124,11 @@
<< ", expected_bytes_acked:" << expected_bytes_acked
<< ", aggregation_epoch_bytes_:" << aggregation_epoch_bytes_
<< ", extra_bytes_acked:" << extra_bytes_acked;
- max_ack_height_filter_.Update(extra_bytes_acked, round_trip_count);
+ ExtraAckedEvent new_event;
+ new_event.extra_acked = extra_bytes_acked;
+ new_event.bytes_acked = aggregation_epoch_bytes_;
+ new_event.time_delta = aggregation_delta;
+ max_ack_height_filter_.Update(new_event, round_trip_count);
return extra_bytes_acked;
}
@@ -309,18 +345,21 @@
: last_acked_packet_send_state;
}
+ bool is_new_max_bandwidth = event_sample.sample_max_bandwidth > max_bandwidth;
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);
+ // TODO(ianswett): Why is the min being passed in here?
+ event_sample.extra_acked =
+ OnAckEventEnd(std::min(est_bandwidth_upper_bound, max_bandwidth),
+ is_new_max_bandwidth, round_trip_count);
return event_sample;
}
QuicByteCount BandwidthSampler::OnAckEventEnd(
- QuicBandwidth bandwidth_estimate,
+ QuicBandwidth bandwidth_estimate, bool is_new_max_bandwidth,
QuicRoundTripCount round_trip_count) {
const QuicByteCount newly_acked_bytes =
total_bytes_acked_ - total_bytes_acked_after_last_ack_event_;
@@ -329,10 +368,10 @@
return 0;
}
total_bytes_acked_after_last_ack_event_ = total_bytes_acked_;
-
QuicByteCount extra_acked = max_ack_height_tracker_.Update(
- bandwidth_estimate, round_trip_count, last_sent_packet_,
- last_acked_packet_, last_acked_packet_ack_time_, newly_acked_bytes);
+ bandwidth_estimate, is_new_max_bandwidth, round_trip_count,
+ last_sent_packet_, last_acked_packet_, last_acked_packet_ack_time_,
+ newly_acked_bytes);
// If |extra_acked| is zero, i.e. this ack event marks the start of a new ack
// aggregation epoch, save LessRecentPoint, which is the last ack point of the
// previous epoch, as a A0 candidate.
diff --git a/quic/core/congestion_control/bandwidth_sampler.h b/quic/core/congestion_control/bandwidth_sampler.h
index bef8250..8145717 100644
--- a/quic/core/congestion_control/bandwidth_sampler.h
+++ b/quic/core/congestion_control/bandwidth_sampler.h
@@ -78,6 +78,24 @@
QuicByteCount bytes_in_flight;
};
+struct QUIC_NO_EXPORT ExtraAckedEvent {
+ // The excess bytes acknowlwedged in the time delta for this event.
+ QuicByteCount extra_acked = 0;
+
+ // The bytes acknowledged and time delta from the event.
+ QuicByteCount bytes_acked = 0;
+ QuicTime::Delta time_delta = QuicTime::Delta::Zero();
+ // The round trip of the event.
+ QuicRoundTripCount round = 0;
+
+ inline bool operator>=(const ExtraAckedEvent& other) const {
+ return extra_acked >= other.extra_acked;
+ }
+ inline bool operator==(const ExtraAckedEvent& other) const {
+ return extra_acked == other.extra_acked;
+ }
+};
+
struct QUIC_EXPORT_PRIVATE BandwidthSample {
// The bandwidth at that particular sample. Zero if no valid bandwidth sample
// is available.
@@ -100,11 +118,14 @@
class QUIC_EXPORT_PRIVATE MaxAckHeightTracker {
public:
explicit MaxAckHeightTracker(QuicRoundTripCount initial_filter_window)
- : max_ack_height_filter_(initial_filter_window, 0, 0) {}
+ : max_ack_height_filter_(initial_filter_window, ExtraAckedEvent(), 0) {}
- QuicByteCount Get() const { return max_ack_height_filter_.GetBest(); }
+ QuicByteCount Get() const {
+ return max_ack_height_filter_.GetBest().extra_acked;
+ }
QuicByteCount Update(QuicBandwidth bandwidth_estimate,
+ bool is_new_max_bandwidth,
QuicRoundTripCount round_trip_count,
QuicPacketNumber last_sent_packet_number,
QuicPacketNumber last_acked_packet_number,
@@ -115,7 +136,10 @@
}
void Reset(QuicByteCount new_height, QuicRoundTripCount new_time) {
- max_ack_height_filter_.Reset(new_height, new_time);
+ ExtraAckedEvent new_event;
+ new_event.extra_acked = new_height;
+ new_event.round = new_time;
+ max_ack_height_filter_.Reset(new_event, new_time);
}
void SetAckAggregationBandwidthThreshold(double threshold) {
@@ -126,6 +150,10 @@
start_new_aggregation_epoch_after_full_round_ = value;
}
+ void SetReduceExtraAckedOnBandwidthIncrease(bool value) {
+ reduce_extra_acked_on_bandwidth_increase_ = value;
+ }
+
double ack_aggregation_bandwidth_threshold() const {
return ack_aggregation_bandwidth_threshold_;
}
@@ -137,10 +165,9 @@
private:
// Tracks the maximum number of bytes acked faster than the estimated
// bandwidth.
- using MaxAckHeightFilter = WindowedFilter<QuicByteCount,
- MaxFilter<QuicByteCount>,
- QuicRoundTripCount,
- QuicRoundTripCount>;
+ using MaxAckHeightFilter =
+ WindowedFilter<ExtraAckedEvent, MaxFilter<ExtraAckedEvent>,
+ QuicRoundTripCount, QuicRoundTripCount>;
MaxAckHeightFilter max_ack_height_filter_;
// The time this aggregation started and the number of bytes acked during it.
@@ -154,6 +181,7 @@
double ack_aggregation_bandwidth_threshold_ =
GetQuicFlag(FLAGS_quic_ack_aggregation_bandwidth_threshold);
bool start_new_aggregation_epoch_after_full_round_ = false;
+ bool reduce_extra_acked_on_bandwidth_increase_ = false;
};
// An interface common to any class that can provide bandwidth samples from the
@@ -335,6 +363,7 @@
QuicBandwidth est_bandwidth_upper_bound,
QuicRoundTripCount round_trip_count) override;
QuicByteCount OnAckEventEnd(QuicBandwidth bandwidth_estimate,
+ bool is_new_max_bandwidth,
QuicRoundTripCount round_trip_count);
void OnAppLimited() override;
@@ -373,6 +402,10 @@
limit_max_ack_height_tracker_by_send_rate_ = value;
}
+ void SetReduceExtraAckedOnBandwidthIncrease(bool value) {
+ max_ack_height_tracker_.SetReduceExtraAckedOnBandwidthIncrease(value);
+ }
+
// AckPoint represents a point on the ack line.
struct QUIC_NO_EXPORT AckPoint {
QuicTime ack_time = QuicTime::Zero();
diff --git a/quic/core/congestion_control/bandwidth_sampler_test.cc b/quic/core/congestion_control/bandwidth_sampler_test.cc
index a371079..0e0dac4 100644
--- a/quic/core/congestion_control/bandwidth_sampler_test.cc
+++ b/quic/core/congestion_control/bandwidth_sampler_test.cc
@@ -756,7 +756,7 @@
for (QuicByteCount bytes = 0; bytes < aggregation_bytes;
bytes += bytes_per_ack) {
QuicByteCount extra_acked = tracker_.Update(
- bandwidth_, RoundTripCount(), last_sent_packet_number_,
+ bandwidth_, true, RoundTripCount(), last_sent_packet_number_,
last_acked_packet_number_, now_, bytes_per_ack);
QUIC_VLOG(1) << "T" << now_ << ": Update after " << bytes_per_ack
<< " bytes acked, " << extra_acked << " extra bytes acked";
@@ -882,8 +882,9 @@
// Update with a tiny bandwidth causes a very low expected bytes acked, which
// in turn causes the current epoch to continue if the |tracker_| doesn't
// check the packet numbers.
- tracker_.Update(bandwidth_ * 0.1, RoundTripCount(), last_sent_packet_number_,
- last_acked_packet_number_, now_, 100);
+ tracker_.Update(bandwidth_ * 0.1, true, RoundTripCount(),
+ last_sent_packet_number_, last_acked_packet_number_, now_,
+ 100);
if (GetQuicReloadableFlag(
quic_bbr_start_new_aggregation_epoch_after_a_full_round)) {
diff --git a/quic/core/congestion_control/bbr2_misc.h b/quic/core/congestion_control/bbr2_misc.h
index a603064..d017b55 100644
--- a/quic/core/congestion_control/bbr2_misc.h
+++ b/quic/core/congestion_control/bbr2_misc.h
@@ -95,6 +95,10 @@
// If false, exit STARTUP on loss only if bandwidth is below threshold.
bool always_exit_startup_on_excess_loss = false;
+ // If true, inclue extra acked during STARTUP and proactively reduce extra
+ // acked when bandwidth increases.
+ bool startup_include_extra_acked = false;
+
/*
* DRAIN parameters.
*/
@@ -431,6 +435,10 @@
bandwidth_sampler_.SetMaxAckHeightTrackerWindowLength(value);
}
+ void SetReduceExtraAckedOnBandwidthIncrease(bool value) {
+ bandwidth_sampler_.SetReduceExtraAckedOnBandwidthIncrease(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 bddb06a..1e27649 100644
--- a/quic/core/congestion_control/bbr2_sender.cc
+++ b/quic/core/congestion_control/bbr2_sender.cc
@@ -175,6 +175,25 @@
quic_bbr2_check_cwnd_limited_before_aggregation_epoch);
params_.probe_bw_check_cwnd_limited_before_aggregation_epoch = true;
}
+ if (GetQuicReloadableFlag(quic_bbr2_no_probe_up_exit_if_no_queue) &&
+ ContainsQuicTag(connection_options, kB202)) {
+ params_.probe_up_dont_exit_if_no_queue_ = true;
+ }
+ if (GetQuicReloadableFlag(quic_bbr2_ignore_inflight_hi_in_probe_up) &&
+ ContainsQuicTag(connection_options, kB203)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_ignore_inflight_hi_in_probe_up);
+ params_.probe_up_ignore_inflight_hi = true;
+ }
+ if (GetQuicReloadableFlag(quic_bbr2_startup_extra_acked) &&
+ ContainsQuicTag(connection_options, kB204)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_startup_extra_acked, 1, 2);
+ model_.SetReduceExtraAckedOnBandwidthIncrease(true);
+ }
+ if (GetQuicReloadableFlag(quic_bbr2_startup_extra_acked) &&
+ ContainsQuicTag(connection_options, kB205)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_startup_extra_acked, 2, 2);
+ params_.startup_include_extra_acked = true;
+ }
if (GetQuicReloadableFlag(
quic_bbr_start_new_aggregation_epoch_after_a_full_round) &&
ContainsQuicTag(connection_options, kBBRA)) {
@@ -191,15 +210,6 @@
ContainsQuicTag(connection_options, kBBQ0)) {
params_.probe_up_includes_acks_after_cwnd_limited = true;
}
- if (GetQuicReloadableFlag(quic_bbr2_no_probe_up_exit_if_no_queue) &&
- ContainsQuicTag(connection_options, kB202)) {
- params_.probe_up_dont_exit_if_no_queue_ = true;
- }
- if (GetQuicReloadableFlag(quic_bbr2_ignore_inflight_hi_in_probe_up) &&
- ContainsQuicTag(connection_options, kB203)) {
- QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_ignore_inflight_hi_in_probe_up);
- params_.probe_up_ignore_inflight_hi = true;
- }
if (GetQuicReloadableFlag(quic_bbr2_startup_probe_up_loss_events) &&
ContainsQuicTag(connection_options, kB206)) {
@@ -392,7 +402,7 @@
QuicByteCount target_cwnd = GetTargetCongestionWindow(model_.cwnd_gain());
const QuicByteCount prior_cwnd = cwnd_;
- if (model_.full_bandwidth_reached()) {
+ 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_) {
diff --git a/quic/core/congestion_control/bbr2_simulator_test.cc b/quic/core/congestion_control/bbr2_simulator_test.cc
index 18a9829..4b59a55 100644
--- a/quic/core/congestion_control/bbr2_simulator_test.cc
+++ b/quic/core/congestion_control/bbr2_simulator_test.cc
@@ -921,6 +921,150 @@
sender_->ExportDebugState().bandwidth_hi, 0.91f);
}
+// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B204
+TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncreaseB204)) {
+ SetQuicReloadableFlag(quic_bbr2_startup_extra_acked, true);
+ SetConnectionOption(kB204);
+ 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.25);
+
+ // 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 B204
+// in the presence of ACK aggregation.
+TEST_F(Bbr2DefaultTopologyTest,
+ QUIC_SLOW_TEST(BandwidthIncreaseB204Aggregation)) {
+ SetQuicReloadableFlag(quic_bbr2_startup_extra_acked, true);
+ SetConnectionOption(kB204);
+ 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.50f);
+ 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 10% of full bandwidth is discovered.
+ EXPECT_APPROX_EQ(params.test_link.bandwidth,
+ sender_->ExportDebugState().bandwidth_hi, 0.95f);
+}
+
+// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B205
+TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncreaseB205)) {
+ SetQuicReloadableFlag(quic_bbr2_startup_extra_acked, true);
+ 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(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.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(Bbr2DefaultTopologyTest,
+ QUIC_SLOW_TEST(BandwidthIncreaseB205Aggregation)) {
+ SetQuicReloadableFlag(quic_bbr2_startup_extra_acked, true);
+ 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(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.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 the number of losses incurred by the startup phase in a situation when
// the buffer is less than BDP.
TEST_F(Bbr2DefaultTopologyTest, PacketLossOnSmallBufferStartup) {
diff --git a/quic/core/congestion_control/windowed_filter.h b/quic/core/congestion_control/windowed_filter.h
index 8949a9c..a777ac5 100644
--- a/quic/core/congestion_control/windowed_filter.h
+++ b/quic/core/congestion_control/windowed_filter.h
@@ -71,6 +71,7 @@
WindowedFilter(TimeDeltaT window_length, T zero_value, TimeT zero_time)
: window_length_(window_length),
zero_value_(zero_value),
+ zero_time_(zero_time),
estimates_{Sample(zero_value_, zero_time),
Sample(zero_value_, zero_time),
Sample(zero_value_, zero_time)} {}
@@ -138,6 +139,8 @@
Sample(new_sample, new_time);
}
+ void Clear() { Reset(zero_value_, zero_time_); }
+
T GetBest() const { return estimates_[0].sample; }
T GetSecondBest() const { return estimates_[1].sample; }
T GetThirdBest() const { return estimates_[2].sample; }
@@ -152,6 +155,7 @@
TimeDeltaT window_length_; // Time length of window.
T zero_value_; // Uninitialized value of T.
+ TimeT zero_time_; // Uninitialized value of TimeT.
Sample estimates_[3]; // Best estimate is element 0.
};
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index ed6bdae..f0bcd7f 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -166,6 +166,10 @@
const QuicTag kB203 = TAG('B', '2', '0', '3'); // Ignore inflight_hi until
// PROBE_UP is exited.
const QuicTag kB206 = TAG('B', '2', '0', '6'); // Exit STARTUP after 2 losses.
+const QuicTag kB204 = TAG('B', '2', '0', '4'); // Reduce extra acked when
+ // MaxBW incrases.
+const QuicTag kB205 = TAG('B', '2', '0', '5'); // Add extra acked to CWND in
+ // STARTUP.
const QuicTag kNTLP = TAG('N', 'T', 'L', 'P'); // No tail loss probe
const QuicTag k1TLP = TAG('1', 'T', 'L', 'P'); // 1 tail loss probe
const QuicTag k1RTO = TAG('1', 'R', 'T', 'O'); // Send 1 packet upon RTO
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 205ac02..acbe982 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -131,6 +131,8 @@
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_unified_iw_options, true)
// When true, the B203 connection option causes the Bbr2Sender to ignore inflight_hi during PROBE_UP and increase it when the bytes delivered without loss are higher.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_ignore_inflight_hi_in_probe_up, true)
+// When true, the B204 connection option enables extra acked in STARTUP, but also adds new logic to decrease it whenever max bandwidth increases.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_startup_extra_acked, true)
// When true, the BBQ0 connection option causes QUIC BBR2 to add bytes_acked to probe_up_acked if the connection hasn\'t been app-limited since inflight_hi was utilized.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_add_bytes_acked_after_inflight_hi_limited, true)
// When true, the BBR4 copt sets the extra_acked window to 20 RTTs and BBR5 sets it to 40 RTTs.