gfe-relnote: For QUIC BBR v1 and v2, only produce one bandwidth/rtt sample per congestion event. Protected by --gfe2_reloadable_flag_quic_one_bw_sample_per_ack_event.
PiperOrigin-RevId: 286899555
Change-Id: I48ddd4ca5c6988e29ef2fb45b9cc89e12da2bcd5
diff --git a/quic/core/congestion_control/bandwidth_sampler.cc b/quic/core/congestion_control/bandwidth_sampler.cc
index 8ad5459..21705a5 100644
--- a/quic/core/congestion_control/bandwidth_sampler.cc
+++ b/quic/core/congestion_control/bandwidth_sampler.cc
@@ -34,6 +34,16 @@
if (aggregation_epoch_bytes_ <=
GetQuicFlag(FLAGS_quic_ack_aggregation_bandwidth_threshold) *
expected_bytes_acked) {
+ QUIC_DVLOG(3) << "Starting a new aggregation epoch because "
+ "aggregation_epoch_bytes_ "
+ << aggregation_epoch_bytes_
+ << " is smaller than expected. "
+ "quic_ack_aggregation_bandwidth_threshold:"
+ << GetQuicFlag(FLAGS_quic_ack_aggregation_bandwidth_threshold)
+ << ", expected_bytes_acked:" << expected_bytes_acked
+ << ", bandwidth_estimate:" << bandwidth_estimate
+ << ", aggregation_duration:"
+ << (ack_time - aggregation_epoch_start_time_);
// Reset to start measuring a new aggregation epoch.
aggregation_epoch_bytes_ = bytes_acked;
aggregation_epoch_start_time_ = ack_time;
@@ -46,6 +56,13 @@
// Compute how many extra bytes were delivered vs max bandwidth.
QuicByteCount extra_bytes_acked =
aggregation_epoch_bytes_ - expected_bytes_acked;
+ QUIC_DVLOG(3) << "Updating MaxAckHeight. ack_time:" << ack_time
+ << ", round trip count:" << round_trip_count
+ << ", bandwidth_estimate:" << bandwidth_estimate
+ << ", bytes_acked:" << bytes_acked
+ << ", 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);
return extra_bytes_acked;
}
@@ -69,6 +86,9 @@
QUIC_RELOADABLE_FLAG_COUNT(
quic_bw_sampler_remove_packets_once_per_congestion_event2);
}
+ if (one_bw_sample_per_ack_event_) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_one_bw_sample_per_ack_event);
+ }
}
BandwidthSampler::~BandwidthSampler() {}
@@ -127,6 +147,85 @@
"in it.";
}
+BandwidthSamplerInterface::CongestionEventSample
+BandwidthSampler::OnCongestionEvent(QuicTime ack_time,
+ const AckedPacketVector& acked_packets,
+ const LostPacketVector& lost_packets,
+ QuicBandwidth max_bandwidth,
+ QuicBandwidth est_bandwidth_upper_bound,
+ QuicRoundTripCount round_trip_count) {
+ DCHECK(one_bw_sample_per_ack_event());
+
+ CongestionEventSample event_sample;
+
+ SendTimeState last_lost_packet_send_state;
+
+ for (const LostPacket& packet : lost_packets) {
+ SendTimeState send_state =
+ OnPacketLost(packet.packet_number, packet.bytes_lost);
+ if (send_state.is_valid) {
+ last_lost_packet_send_state = send_state;
+ }
+ }
+
+ if (acked_packets.empty()) {
+ // Only populate send state for a loss-only event.
+ event_sample.last_packet_send_state = last_lost_packet_send_state;
+ return event_sample;
+ }
+
+ SendTimeState last_acked_packet_send_state;
+ for (const auto& packet : acked_packets) {
+ BandwidthSample sample =
+ OnPacketAcknowledged(ack_time, packet.packet_number);
+ if (!sample.state_at_send.is_valid) {
+ continue;
+ }
+
+ last_acked_packet_send_state = sample.state_at_send;
+
+ if (!sample.rtt.IsZero()) {
+ event_sample.sample_rtt = std::min(event_sample.sample_rtt, sample.rtt);
+ }
+ if (sample.bandwidth > event_sample.sample_max_bandwidth) {
+ event_sample.sample_max_bandwidth = sample.bandwidth;
+ event_sample.sample_is_app_limited = sample.state_at_send.is_app_limited;
+ }
+ const QuicByteCount inflight_sample =
+ total_bytes_acked() - last_acked_packet_send_state.total_bytes_acked;
+ if (inflight_sample > event_sample.sample_max_inflight) {
+ event_sample.sample_max_inflight = inflight_sample;
+ }
+ }
+
+ if (!last_lost_packet_send_state.is_valid) {
+ event_sample.last_packet_send_state = last_acked_packet_send_state;
+ } else if (!last_acked_packet_send_state.is_valid) {
+ event_sample.last_packet_send_state = last_lost_packet_send_state;
+ } else {
+ // If two packets are inflight and an alarm is armed to lose a packet and it
+ // wakes up late, then the first of two in flight packets could have been
+ // acknowledged before the wakeup, which re-evaluates loss detection, and
+ // could declare the later of the two lost. However, this is an edge case
+ // that should not happen in the test environments, hence the DCHECK.
+ DCHECK(lost_packets.back().packet_number <
+ acked_packets.back().packet_number)
+ << "Largest lost packet should be less than largest acked packet: "
+ << lost_packets.back().packet_number << " vs. "
+ << acked_packets.back().packet_number;
+ event_sample.last_packet_send_state =
+ lost_packets.back().packet_number > acked_packets.back().packet_number
+ ? last_lost_packet_send_state
+ : last_acked_packet_send_state;
+ }
+
+ max_bandwidth = std::max(max_bandwidth, event_sample.sample_max_bandwidth);
+ event_sample.extra_acked = OnAckEventEnd(
+ std::min(est_bandwidth_upper_bound, max_bandwidth), round_trip_count);
+
+ return event_sample;
+}
+
QuicByteCount BandwidthSampler::OnAckEventEnd(
QuicBandwidth bandwidth_estimate,
QuicRoundTripCount round_trip_count) {
diff --git a/quic/core/congestion_control/bandwidth_sampler.h b/quic/core/congestion_control/bandwidth_sampler.h
index d2e0c7b..d93af36 100644
--- a/quic/core/congestion_control/bandwidth_sampler.h
+++ b/quic/core/congestion_control/bandwidth_sampler.h
@@ -14,6 +14,7 @@
#include "net/third_party/quiche/src/quic/core/quic_types.h"
#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
namespace quic {
@@ -151,10 +152,47 @@
// Notifies the sampler that the |packet_number| is acknowledged. Returns a
// bandwidth sample. If no bandwidth sample is available,
// QuicBandwidth::Zero() is returned.
+ // TODO(wub): Remove when deprecating --quic_one_bw_sample_per_ack_event.
virtual BandwidthSample OnPacketAcknowledged(
QuicTime ack_time,
QuicPacketNumber packet_number) = 0;
+ struct QUIC_NO_EXPORT CongestionEventSample {
+ // The maximum bandwidth sample from all acked packets.
+ // QuicBandwidth::Zero() if no samples are available.
+ QuicBandwidth sample_max_bandwidth = QuicBandwidth::Zero();
+ // Whether |sample_max_bandwidth| is from a app-limited sample.
+ bool sample_is_app_limited = false;
+ // The minimum rtt sample from all acked packets.
+ // QuicTime::Delta::Infinite() if no samples are available.
+ QuicTime::Delta sample_rtt = QuicTime::Delta::Infinite();
+ // For each packet p in acked packets, this is the max value of INFLIGHT(p),
+ // where INFLIGHT(p) is the number of bytes acked while p is inflight.
+ QuicByteCount sample_max_inflight = 0;
+ // The send state of the largest packet in acked_packets, unless it is
+ // empty. If acked_packets is empty, it's the send state of the largest
+ // packet in lost_packets.
+ SendTimeState last_packet_send_state;
+ // The number of extra bytes acked from this ack event, compared to what is
+ // expected from the flow's bandwidth. Larger value means more ack
+ // aggregation.
+ QuicByteCount extra_acked = 0;
+ };
+ // Notifies the sampler that at |ack_time|, all packets in |acked_packets|
+ // have been acked, and all packets in |lost_packets| have been lost.
+ // See the comments in CongestionEventSample for the return value.
+ // |max_bandwidth| is the windowed maximum observed bandwidth.
+ // |est_bandwidth_upper_bound| is an upper bound of estimated bandwidth used
+ // to calculate extra_acked.
+ // Only used when --quic_one_bw_sample_per_ack_event=true.
+ virtual CongestionEventSample OnCongestionEvent(
+ QuicTime ack_time,
+ const AckedPacketVector& acked_packets,
+ const LostPacketVector& lost_packets,
+ QuicBandwidth max_bandwidth,
+ QuicBandwidth est_bandwidth_upper_bound,
+ QuicRoundTripCount round_trip_count) = 0;
+
// Informs the sampler that a packet is considered lost and it should no
// longer keep track of it.
virtual SendTimeState OnPacketLost(QuicPacketNumber packet_number,
@@ -272,6 +310,13 @@
HasRetransmittableData has_retransmittable_data) override;
BandwidthSample OnPacketAcknowledged(QuicTime ack_time,
QuicPacketNumber packet_number) override;
+ CongestionEventSample OnCongestionEvent(
+ QuicTime ack_time,
+ const AckedPacketVector& acked_packets,
+ const LostPacketVector& lost_packets,
+ QuicBandwidth max_bandwidth,
+ QuicBandwidth est_bandwidth_upper_bound,
+ QuicRoundTripCount round_trip_count) override;
QuicByteCount OnAckEventEnd(QuicBandwidth bandwidth_estimate,
QuicRoundTripCount round_trip_count);
SendTimeState OnPacketLost(QuicPacketNumber packet_number,
@@ -308,6 +353,10 @@
return remove_packets_once_per_congestion_event_;
}
+ bool one_bw_sample_per_ack_event() const {
+ return one_bw_sample_per_ack_event_;
+ }
+
private:
friend class test::BandwidthSamplerPeer;
@@ -430,6 +479,12 @@
// Latched value of quic_bw_sampler_remove_packets_once_per_congestion_event2.
const bool remove_packets_once_per_congestion_event_ = GetQuicReloadableFlag(
quic_bw_sampler_remove_packets_once_per_congestion_event2);
+
+ // Latched value of quic_bw_sampler_remove_packets_once_per_congestion_event2
+ // and quic_one_bw_sample_per_ack_event.
+ const bool one_bw_sample_per_ack_event_ =
+ remove_packets_once_per_congestion_event_ &&
+ GetQuicReloadableFlag(quic_one_bw_sample_per_ack_event);
};
} // namespace quic
diff --git a/quic/core/congestion_control/bandwidth_sampler_test.cc b/quic/core/congestion_control/bandwidth_sampler_test.cc
index fc51db2..a5a2682 100644
--- a/quic/core/congestion_control/bandwidth_sampler_test.cc
+++ b/quic/core/congestion_control/bandwidth_sampler_test.cc
@@ -3,6 +3,8 @@
// found in the LICENSE file.
#include "net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h"
+#include <cstdint>
+#include <set>
#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
#include "net/third_party/quiche/src/quic/core/quic_time.h"
@@ -36,7 +38,10 @@
protected:
BandwidthSamplerTest()
: sampler_(nullptr, /*max_height_tracker_window_length=*/0),
- bytes_in_flight_(0) {
+ bytes_in_flight_(0),
+ max_bandwidth_(QuicBandwidth::Zero()),
+ est_bandwidth_upper_bound_(QuicBandwidth::Infinite()),
+ round_trip_count_(0) {
// Ensure that the clock does not start at zero.
clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
}
@@ -44,6 +49,9 @@
MockClock clock_;
BandwidthSampler sampler_;
QuicByteCount bytes_in_flight_;
+ QuicBandwidth max_bandwidth_; // Max observed bandwidth from acks.
+ QuicBandwidth est_bandwidth_upper_bound_;
+ QuicRoundTripCount round_trip_count_; // Needed to calculate extra_acked.
QuicByteCount PacketsToBytes(QuicPacketCount packet_count) {
return packet_count * kRegularPacketSize;
@@ -68,8 +76,31 @@
QuicByteCount size = BandwidthSamplerPeer::GetPacketSize(
sampler_, QuicPacketNumber(packet_number));
bytes_in_flight_ -= size;
- return sampler_.OnPacketAcknowledged(clock_.Now(),
- QuicPacketNumber(packet_number));
+ if (!sampler_.one_bw_sample_per_ack_event()) {
+ return sampler_.OnPacketAcknowledged(clock_.Now(),
+ QuicPacketNumber(packet_number));
+ }
+ BandwidthSampler::CongestionEventSample sample = sampler_.OnCongestionEvent(
+ clock_.Now(), {MakeAckedPacket(packet_number)}, {}, max_bandwidth_,
+ est_bandwidth_upper_bound_, round_trip_count_);
+ max_bandwidth_ = std::max(max_bandwidth_, sample.sample_max_bandwidth);
+ BandwidthSample bandwidth_sample;
+ bandwidth_sample.bandwidth = sample.sample_max_bandwidth;
+ bandwidth_sample.rtt = sample.sample_rtt;
+ bandwidth_sample.state_at_send = sample.last_packet_send_state;
+ return bandwidth_sample;
+ }
+
+ AckedPacket MakeAckedPacket(uint64_t packet_number) const {
+ QuicByteCount size = BandwidthSamplerPeer::GetPacketSize(
+ sampler_, QuicPacketNumber(packet_number));
+ return AckedPacket(QuicPacketNumber(packet_number), size, clock_.Now());
+ }
+
+ LostPacket MakeLostPacket(uint64_t packet_number) const {
+ return LostPacket(QuicPacketNumber(packet_number),
+ BandwidthSamplerPeer::GetPacketSize(
+ sampler_, QuicPacketNumber(packet_number)));
}
// Acknowledge receipt of a packet and expect it to be not app-limited.
@@ -80,14 +111,48 @@
return sample.bandwidth;
}
+ BandwidthSampler::CongestionEventSample OnCongestionEvent(
+ std::set<uint64_t> acked_packet_numbers,
+ std::set<uint64_t> lost_packet_numbers) {
+ AckedPacketVector acked_packets;
+ for (auto it = acked_packet_numbers.begin();
+ it != acked_packet_numbers.end(); ++it) {
+ acked_packets.push_back(MakeAckedPacket(*it));
+ bytes_in_flight_ -= acked_packets.back().bytes_acked;
+ }
+
+ LostPacketVector lost_packets;
+ for (auto it = lost_packet_numbers.begin(); it != lost_packet_numbers.end();
+ ++it) {
+ lost_packets.push_back(MakeLostPacket(*it));
+ bytes_in_flight_ -= lost_packets.back().bytes_lost;
+ }
+
+ BandwidthSampler::CongestionEventSample sample = sampler_.OnCongestionEvent(
+ clock_.Now(), acked_packets, lost_packets, max_bandwidth_,
+ est_bandwidth_upper_bound_, round_trip_count_);
+ max_bandwidth_ = std::max(max_bandwidth_, sample.sample_max_bandwidth);
+ return sample;
+ }
+
SendTimeState LosePacket(uint64_t packet_number) {
QuicByteCount size = BandwidthSamplerPeer::GetPacketSize(
sampler_, QuicPacketNumber(packet_number));
bytes_in_flight_ -= size;
- SendTimeState send_time_state =
- sampler_.OnPacketLost(QuicPacketNumber(packet_number), size);
- EXPECT_TRUE(send_time_state.is_valid);
- return send_time_state;
+ if (!sampler_.one_bw_sample_per_ack_event()) {
+ SendTimeState send_time_state =
+ sampler_.OnPacketLost(QuicPacketNumber(packet_number), size);
+ EXPECT_TRUE(send_time_state.is_valid);
+ return send_time_state;
+ }
+ LostPacket lost_packet(QuicPacketNumber(packet_number), size);
+ BandwidthSampler::CongestionEventSample sample = sampler_.OnCongestionEvent(
+ clock_.Now(), {}, {lost_packet}, max_bandwidth_,
+ est_bandwidth_upper_bound_, round_trip_count_);
+ EXPECT_TRUE(sample.last_packet_send_state.is_valid);
+ EXPECT_EQ(sample.sample_max_bandwidth, QuicBandwidth::Zero());
+ EXPECT_EQ(sample.sample_rtt, QuicTime::Delta::Infinite());
+ return sample.last_packet_send_state;
}
// Sends one packet and acks it. Then, send 20 packets. Finally, send
@@ -500,6 +565,119 @@
EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
}
+TEST_F(BandwidthSamplerTest, CongestionEventSampleDefaultValues) {
+ // Make sure a default constructed CongestionEventSample has the correct
+ // initial values for BandwidthSampler::OnCongestionEvent() to work.
+ BandwidthSampler::CongestionEventSample sample;
+
+ DCHECK_EQ(QuicBandwidth::Zero(), sample.sample_max_bandwidth);
+ DCHECK(!sample.sample_is_app_limited);
+ DCHECK_EQ(QuicTime::Delta::Infinite(), sample.sample_rtt);
+ DCHECK_EQ(0u, sample.sample_max_inflight);
+ DCHECK_EQ(0u, sample.extra_acked);
+}
+
+// 1) Send 2 packets, 2) Ack both in 1 event, 3) Repeat.
+TEST_F(BandwidthSamplerTest, TwoAckedPacketsPerEvent) {
+ if (!sampler_.one_bw_sample_per_ack_event()) {
+ return;
+ }
+
+ QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(10);
+ QuicBandwidth sending_rate = QuicBandwidth::FromBytesAndTimeDelta(
+ kRegularPacketSize, time_between_packets);
+
+ for (uint64_t i = 1; i < 21; i++) {
+ SendPacket(i);
+ clock_.AdvanceTime(time_between_packets);
+ if (i % 2 != 0) {
+ continue;
+ }
+
+ BandwidthSampler::CongestionEventSample sample =
+ OnCongestionEvent({i - 1, i}, {});
+ EXPECT_EQ(sending_rate, sample.sample_max_bandwidth);
+ EXPECT_EQ(time_between_packets, sample.sample_rtt);
+ EXPECT_EQ(2 * kRegularPacketSize, sample.sample_max_inflight);
+ EXPECT_TRUE(sample.last_packet_send_state.is_valid);
+ EXPECT_EQ(2 * kRegularPacketSize,
+ sample.last_packet_send_state.bytes_in_flight);
+ EXPECT_EQ(i * kRegularPacketSize,
+ sample.last_packet_send_state.total_bytes_sent);
+ EXPECT_EQ((i - 2) * kRegularPacketSize,
+ sample.last_packet_send_state.total_bytes_acked);
+ EXPECT_EQ(0u, sample.last_packet_send_state.total_bytes_lost);
+ sampler_.RemoveObsoletePackets(QuicPacketNumber(i - 2));
+ }
+}
+
+TEST_F(BandwidthSamplerTest, LoseEveryOtherPacket) {
+ if (!sampler_.one_bw_sample_per_ack_event()) {
+ return;
+ }
+
+ QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(10);
+ QuicBandwidth sending_rate = QuicBandwidth::FromBytesAndTimeDelta(
+ kRegularPacketSize, time_between_packets);
+
+ for (uint64_t i = 1; i < 21; i++) {
+ SendPacket(i);
+ clock_.AdvanceTime(time_between_packets);
+ if (i % 2 != 0) {
+ continue;
+ }
+
+ // Ack packet i and lose i-1.
+ BandwidthSampler::CongestionEventSample sample =
+ OnCongestionEvent({i}, {i - 1});
+ // Losing 50% packets means sending rate is twice the bandwidth.
+ EXPECT_EQ(sending_rate, sample.sample_max_bandwidth * 2);
+ EXPECT_EQ(time_between_packets, sample.sample_rtt);
+ EXPECT_EQ(kRegularPacketSize, sample.sample_max_inflight);
+ EXPECT_TRUE(sample.last_packet_send_state.is_valid);
+ EXPECT_EQ(2 * kRegularPacketSize,
+ sample.last_packet_send_state.bytes_in_flight);
+ EXPECT_EQ(i * kRegularPacketSize,
+ sample.last_packet_send_state.total_bytes_sent);
+ EXPECT_EQ((i - 2) * kRegularPacketSize / 2,
+ sample.last_packet_send_state.total_bytes_acked);
+ EXPECT_EQ((i - 2) * kRegularPacketSize / 2,
+ sample.last_packet_send_state.total_bytes_lost);
+ sampler_.RemoveObsoletePackets(QuicPacketNumber(i - 2));
+ }
+}
+
+TEST_F(BandwidthSamplerTest, AckHeightRespectBandwidthEstimateUpperBound) {
+ if (!sampler_.one_bw_sample_per_ack_event()) {
+ return;
+ }
+
+ QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(10);
+ QuicBandwidth first_packet_sending_rate =
+ QuicBandwidth::FromBytesAndTimeDelta(kRegularPacketSize,
+ time_between_packets);
+
+ // Send and ack packet 1.
+ SendPacket(1);
+ clock_.AdvanceTime(time_between_packets);
+ BandwidthSampler::CongestionEventSample sample = OnCongestionEvent({1}, {});
+ EXPECT_EQ(first_packet_sending_rate, sample.sample_max_bandwidth);
+ EXPECT_EQ(first_packet_sending_rate, max_bandwidth_);
+
+ // Send and ack packet 2, 3 and 4.
+ round_trip_count_++;
+ est_bandwidth_upper_bound_ = first_packet_sending_rate * 0.9;
+ SendPacket(2);
+ SendPacket(3);
+ SendPacket(4);
+ clock_.AdvanceTime(time_between_packets);
+ sample = OnCongestionEvent({2, 3, 4}, {});
+ EXPECT_EQ(first_packet_sending_rate * 3, sample.sample_max_bandwidth);
+ EXPECT_EQ(max_bandwidth_, sample.sample_max_bandwidth);
+
+ EXPECT_LT(2 * kRegularPacketSize, sample.extra_acked);
+}
+
class MaxAckHeightTrackerTest : public QuicTest {
protected:
MaxAckHeightTrackerTest() : tracker_(/*initial_filter_window=*/10) {}
diff --git a/quic/core/congestion_control/bbr2_misc.cc b/quic/core/congestion_control/bbr2_misc.cc
index 419238c..07fb352 100644
--- a/quic/core/congestion_control/bbr2_misc.cc
+++ b/quic/core/congestion_control/bbr2_misc.cc
@@ -98,6 +98,11 @@
const AckedPacketVector& acked_packets,
const LostPacketVector& lost_packets,
Bbr2CongestionEvent* congestion_event) {
+ if (one_bw_sample_per_ack_event()) {
+ OnCongestionEventStartNew(event_time, acked_packets, lost_packets,
+ congestion_event);
+ return;
+ }
const QuicByteCount prior_bytes_acked = total_bytes_acked();
const QuicByteCount prior_bytes_lost = total_bytes_lost();
@@ -191,6 +196,87 @@
AdaptLowerBounds(*congestion_event);
}
+void Bbr2NetworkModel::OnCongestionEventStartNew(
+ QuicTime event_time,
+ const AckedPacketVector& acked_packets,
+ const LostPacketVector& lost_packets,
+ Bbr2CongestionEvent* congestion_event) {
+ DCHECK(one_bw_sample_per_ack_event());
+ const QuicByteCount prior_bytes_acked = total_bytes_acked();
+ const QuicByteCount prior_bytes_lost = total_bytes_lost();
+
+ congestion_event->event_time = event_time;
+ congestion_event->end_of_round_trip =
+ acked_packets.empty() ? false
+ : round_trip_counter_.OnPacketsAcked(
+ acked_packets.rbegin()->packet_number);
+
+ BandwidthSamplerInterface::CongestionEventSample sample =
+ bandwidth_sampler_.OnCongestionEvent(event_time, acked_packets,
+ lost_packets, MaxBandwidth(),
+ bandwidth_lo(), RoundTripCount());
+
+ if (sample.last_packet_send_state.is_valid) {
+ congestion_event->last_packet_send_state = sample.last_packet_send_state;
+ congestion_event->last_sample_is_app_limited =
+ sample.last_packet_send_state.is_app_limited;
+ }
+
+ if (!sample.sample_is_app_limited ||
+ sample.sample_max_bandwidth > MaxBandwidth()) {
+ congestion_event->sample_max_bandwidth = sample.sample_max_bandwidth;
+ max_bandwidth_filter_.Update(congestion_event->sample_max_bandwidth);
+ }
+
+ if (!sample.sample_rtt.IsInfinite()) {
+ congestion_event->sample_min_rtt = sample.sample_rtt;
+ min_rtt_filter_.Update(congestion_event->sample_min_rtt, event_time);
+ }
+
+ congestion_event->bytes_acked = total_bytes_acked() - prior_bytes_acked;
+ congestion_event->bytes_lost = total_bytes_lost() - prior_bytes_lost;
+
+ if (congestion_event->prior_bytes_in_flight >=
+ congestion_event->bytes_acked + congestion_event->bytes_lost) {
+ congestion_event->bytes_in_flight =
+ congestion_event->prior_bytes_in_flight -
+ congestion_event->bytes_acked - congestion_event->bytes_lost;
+ } else {
+ QUIC_LOG_FIRST_N(ERROR, 1)
+ << "prior_bytes_in_flight:" << congestion_event->prior_bytes_in_flight
+ << " is smaller than the sum of bytes_acked:"
+ << congestion_event->bytes_acked
+ << " and bytes_lost:" << congestion_event->bytes_lost;
+ congestion_event->bytes_in_flight = 0;
+ }
+
+ bytes_lost_in_round_ += congestion_event->bytes_lost;
+
+ // |bandwidth_latest_| and |inflight_latest_| only increased within a round.
+ if (sample.sample_max_bandwidth > bandwidth_latest_) {
+ bandwidth_latest_ = sample.sample_max_bandwidth;
+ }
+
+ if (sample.sample_max_inflight > inflight_latest_) {
+ inflight_latest_ = sample.sample_max_inflight;
+ }
+
+ if (!congestion_event->end_of_round_trip) {
+ return;
+ }
+
+ // Per round-trip updates.
+ AdaptLowerBounds(*congestion_event);
+
+ if (!sample.sample_max_bandwidth.IsZero()) {
+ bandwidth_latest_ = sample.sample_max_bandwidth;
+ }
+
+ if (sample.sample_max_inflight > 0) {
+ inflight_latest_ = sample.sample_max_inflight;
+ }
+}
+
void Bbr2NetworkModel::AdaptLowerBounds(
const Bbr2CongestionEvent& congestion_event) {
if (!congestion_event.end_of_round_trip ||
@@ -220,10 +306,12 @@
QuicPacketNumber least_unacked_packet,
const Bbr2CongestionEvent& congestion_event) {
if (congestion_event.end_of_round_trip) {
- const auto& last_acked_sample = congestion_event.last_acked_sample;
- if (last_acked_sample.bandwidth_sample.state_at_send.is_valid) {
- bandwidth_latest_ = last_acked_sample.bandwidth_sample.bandwidth;
- inflight_latest_ = last_acked_sample.inflight_sample;
+ if (!one_bw_sample_per_ack_event()) {
+ const auto& last_acked_sample = congestion_event.last_acked_sample;
+ if (last_acked_sample.bandwidth_sample.state_at_send.is_valid) {
+ bandwidth_latest_ = last_acked_sample.bandwidth_sample.bandwidth;
+ inflight_latest_ = last_acked_sample.inflight_sample;
+ }
}
bytes_lost_in_round_ = 0;
@@ -270,7 +358,10 @@
bool Bbr2NetworkModel::IsInflightTooHigh(
const Bbr2CongestionEvent& congestion_event) const {
- const SendTimeState& send_state = SendStateOfLargestPacket(congestion_event);
+ const SendTimeState& send_state =
+ one_bw_sample_per_ack_event()
+ ? congestion_event.last_packet_send_state
+ : SendStateOfLargestPacket(congestion_event);
if (!send_state.is_valid) {
// Not enough information.
return false;
diff --git a/quic/core/congestion_control/bbr2_misc.h b/quic/core/congestion_control/bbr2_misc.h
index 0c5a1eb..b97184b 100644
--- a/quic/core/congestion_control/bbr2_misc.h
+++ b/quic/core/congestion_control/bbr2_misc.h
@@ -247,6 +247,8 @@
// Whether acked_packets indicates the end of a round trip.
bool end_of_round_trip = false;
+ // TODO(wub): After deprecating --quic_one_bw_sample_per_ack_event, use
+ // last_packet_send_state.is_app_limited instead of this field.
// Whether the last bandwidth sample from acked_packets is app limited.
// false if acked_packets is empty.
bool last_sample_is_app_limited = false;
@@ -261,6 +263,13 @@
// Maximum bandwidth of all bandwidth samples from acked_packets.
QuicBandwidth sample_max_bandwidth = QuicBandwidth::Zero();
+ // The send state of the largest packet in acked_packets, unless it is empty.
+ // If acked_packets is empty, it's the send state of the largest packet in
+ // lost_packets.
+ SendTimeState last_packet_send_state;
+
+ // TODO(wub): Remove |last_acked_sample| and |last_lost_sample| when
+ // deprecating --quic_one_bw_sample_per_ack_event.
// Send time state of the largest-numbered packet in this event.
// SendTimeState send_time_state;
struct {
@@ -276,6 +285,7 @@
} last_lost_sample;
};
+// TODO(wub): Remove this when deprecating --quic_one_bw_sample_per_ack_event.
QUIC_EXPORT_PRIVATE const SendTimeState& SendStateOfLargestPacket(
const Bbr2CongestionEvent& congestion_event);
@@ -300,6 +310,12 @@
const AckedPacketVector& acked_packets,
const LostPacketVector& lost_packets,
Bbr2CongestionEvent* congestion_event);
+ // The new version of OnCongestionEventStart.
+ // Called only when --quic_one_bw_sample_per_ack_event=true.
+ void OnCongestionEventStartNew(QuicTime event_time,
+ const AckedPacketVector& acked_packets,
+ const LostPacketVector& lost_packets,
+ Bbr2CongestionEvent* congestion_event);
void OnCongestionEventFinish(QuicPacketNumber least_unacked_packet,
const Bbr2CongestionEvent& congestion_event);
@@ -416,6 +432,10 @@
float pacing_gain() const { return pacing_gain_; }
void set_pacing_gain(float pacing_gain) { pacing_gain_ = pacing_gain; }
+ bool one_bw_sample_per_ack_event() const {
+ return bandwidth_sampler_.one_bw_sample_per_ack_event();
+ }
+
private:
const Bbr2Params& Params() const { return *params_; }
const Bbr2Params* const params_;
diff --git a/quic/core/congestion_control/bbr2_probe_bw.cc b/quic/core/congestion_control/bbr2_probe_bw.cc
index ec9235b..d419de2 100644
--- a/quic/core/congestion_control/bbr2_probe_bw.cc
+++ b/quic/core/congestion_control/bbr2_probe_bw.cc
@@ -148,7 +148,10 @@
Bbr2ProbeBwMode::AdaptUpperBoundsResult Bbr2ProbeBwMode::MaybeAdaptUpperBounds(
const Bbr2CongestionEvent& congestion_event) {
- const SendTimeState& send_state = SendStateOfLargestPacket(congestion_event);
+ const SendTimeState& send_state =
+ model_->one_bw_sample_per_ack_event()
+ ? congestion_event.last_packet_send_state
+ : SendStateOfLargestPacket(congestion_event);
if (!send_state.is_valid) {
QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
<< ": NOT_ADAPTED_INVALID_SAMPLE";
diff --git a/quic/core/congestion_control/bbr_sender.cc b/quic/core/congestion_control/bbr_sender.cc
index 4973744..4a583bb 100644
--- a/quic/core/congestion_control/bbr_sender.cc
+++ b/quic/core/congestion_control/bbr_sender.cc
@@ -389,23 +389,65 @@
const AckedPacketVector& acked_packets,
const LostPacketVector& lost_packets) {
const QuicByteCount total_bytes_acked_before = sampler_.total_bytes_acked();
+ const QuicByteCount total_bytes_lost_before = sampler_.total_bytes_lost();
bool is_round_start = false;
bool min_rtt_expired = false;
-
- DiscardLostPackets(lost_packets);
-
- // Input the new data into the BBR model of the connection.
QuicByteCount excess_acked = 0;
- if (!acked_packets.empty()) {
- QuicPacketNumber last_acked_packet = acked_packets.rbegin()->packet_number;
- is_round_start = UpdateRoundTripCounter(last_acked_packet);
- min_rtt_expired = UpdateBandwidthAndMinRtt(event_time, acked_packets);
- UpdateRecoveryState(last_acked_packet, !lost_packets.empty(),
- is_round_start);
+ QuicByteCount bytes_lost = 0;
- excess_acked =
- sampler_.OnAckEventEnd(max_bandwidth_.GetBest(), round_trip_count_);
+ if (!sampler_.one_bw_sample_per_ack_event()) {
+ DiscardLostPackets(lost_packets);
+
+ // Input the new data into the BBR model of the connection.
+ if (!acked_packets.empty()) {
+ QuicPacketNumber last_acked_packet =
+ acked_packets.rbegin()->packet_number;
+ is_round_start = UpdateRoundTripCounter(last_acked_packet);
+
+ min_rtt_expired = UpdateBandwidthAndMinRtt(event_time, acked_packets);
+ UpdateRecoveryState(last_acked_packet, !lost_packets.empty(),
+ is_round_start);
+
+ excess_acked =
+ sampler_.OnAckEventEnd(max_bandwidth_.GetBest(), round_trip_count_);
+ }
+ } else {
+ if (!acked_packets.empty()) {
+ QuicPacketNumber last_acked_packet =
+ acked_packets.rbegin()->packet_number;
+ is_round_start = UpdateRoundTripCounter(last_acked_packet);
+ UpdateRecoveryState(last_acked_packet, !lost_packets.empty(),
+ is_round_start);
+ }
+
+ BandwidthSamplerInterface::CongestionEventSample sample =
+ sampler_.OnCongestionEvent(
+ event_time, acked_packets, lost_packets, max_bandwidth_.GetBest(),
+ QuicBandwidth::Infinite(), round_trip_count_);
+ if (sample.last_packet_send_state.is_valid) {
+ last_sample_is_app_limited_ =
+ sample.last_packet_send_state.is_app_limited;
+ has_non_app_limited_sample_ |= !last_sample_is_app_limited_;
+ }
+ if (!sample.sample_is_app_limited ||
+ sample.sample_max_bandwidth > max_bandwidth_.GetBest()) {
+ max_bandwidth_.Update(sample.sample_max_bandwidth, round_trip_count_);
+ }
+ if (!sample.sample_rtt.IsInfinite()) {
+ min_rtt_expired = MaybeUpdateMinRtt(event_time, sample.sample_rtt);
+ }
+ if (mode_ == STARTUP) {
+ bytes_lost = sampler_.total_bytes_lost() - total_bytes_lost_before;
+ if (stats_) {
+ stats_->slowstart_packets_lost += lost_packets.size();
+ stats_->slowstart_bytes_lost += bytes_lost;
+ }
+ if (startup_rate_reduction_multiplier_ != 0) {
+ startup_bytes_lost_ += bytes_lost;
+ }
+ }
+ excess_acked = sample.extra_acked;
}
// Handle logic specific to PROBE_BW mode.
@@ -425,9 +467,10 @@
// Calculate number of packets acked and lost.
QuicByteCount bytes_acked =
sampler_.total_bytes_acked() - total_bytes_acked_before;
- QuicByteCount bytes_lost = 0;
- for (const auto& packet : lost_packets) {
- bytes_lost += packet.bytes_lost;
+ if (!sampler_.one_bw_sample_per_ack_event()) {
+ for (const auto& packet : lost_packets) {
+ bytes_lost += packet.bytes_lost;
+ }
}
// After the model is updated, recalculate the pacing rate and congestion
@@ -494,6 +537,7 @@
}
void BbrSender::DiscardLostPackets(const LostPacketVector& lost_packets) {
+ DCHECK(!sampler_.one_bw_sample_per_ack_event());
for (const LostPacket& packet : lost_packets) {
sampler_.OnPacketLost(packet.packet_number, packet.bytes_lost);
if (mode_ == STARTUP) {
@@ -525,6 +569,7 @@
bool BbrSender::UpdateBandwidthAndMinRtt(
QuicTime now,
const AckedPacketVector& acked_packets) {
+ DCHECK(!sampler_.one_bw_sample_per_ack_event());
QuicTime::Delta sample_min_rtt = QuicTime::Delta::Infinite();
for (const auto& packet : acked_packets) {
BandwidthSample bandwidth_sample =
@@ -552,6 +597,12 @@
if (sample_min_rtt.IsInfinite()) {
return false;
}
+
+ return MaybeUpdateMinRtt(now, sample_min_rtt);
+}
+
+bool BbrSender::MaybeUpdateMinRtt(QuicTime now,
+ QuicTime::Delta sample_min_rtt) {
min_rtt_since_last_probe_rtt_ =
std::min(min_rtt_since_last_probe_rtt_, sample_min_rtt);
diff --git a/quic/core/congestion_control/bbr_sender.h b/quic/core/congestion_control/bbr_sender.h
index dc512cf..9a811fa 100644
--- a/quic/core/congestion_control/bbr_sender.h
+++ b/quic/core/congestion_control/bbr_sender.h
@@ -190,6 +190,7 @@
// Returns true if the current min_rtt should be kept and we should not enter
// PROBE_RTT immediately.
bool ShouldExtendMinRttExpiry() const;
+ bool MaybeUpdateMinRtt(QuicTime now, QuicTime::Delta sample_min_rtt);
// Enters the STARTUP mode.
void EnterStartupMode(QuicTime now);