gfe-relnote: For QUIC BBR/BBRv2, move max ack height tracking to BandwidthSampler. BBR change protected by --gfe2_reloadable_flag_quic_track_ack_height_in_bandwidth_sampler. BBRv2 protected by existing --gfe2_reloadable_flag_quic_default_to_bbr_v2. PiperOrigin-RevId: 262018910 Change-Id: I392d82737a85bdf7de6de5918f0b9ea43a653329
diff --git a/quic/core/congestion_control/bandwidth_sampler.cc b/quic/core/congestion_control/bandwidth_sampler.cc index 11a625c..39c7051 100644 --- a/quic/core/congestion_control/bandwidth_sampler.cc +++ b/quic/core/congestion_control/bandwidth_sampler.cc
@@ -6,13 +6,42 @@ #include <algorithm> +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" namespace quic { -BandwidthSampler::BandwidthSampler() + +QuicByteCount MaxAckHeightTracker::Update(QuicBandwidth bandwidth_estimate, + QuicRoundTripCount round_trip_count, + QuicTime ack_time, + QuicByteCount bytes_acked) { + // 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_); + // 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_ <= expected_bytes_acked) { + // Reset to start measuring a new aggregation epoch. + aggregation_epoch_bytes_ = bytes_acked; + aggregation_epoch_start_time_ = ack_time; + return 0; + } + + aggregation_epoch_bytes_ += bytes_acked; + + // Compute how many extra bytes were delivered vs max bandwidth. + QuicByteCount extra_bytes_acked = + aggregation_epoch_bytes_ - expected_bytes_acked; + max_ack_height_filter_.Update(extra_bytes_acked, round_trip_count); + return extra_bytes_acked; +} + +BandwidthSampler::BandwidthSampler( + QuicRoundTripCount max_height_tracker_window_length) : total_bytes_sent_(0), total_bytes_acked_(0), total_bytes_lost_(0), @@ -21,7 +50,9 @@ last_acked_packet_ack_time_(QuicTime::Zero()), is_app_limited_(false), connection_state_map_(), - max_tracked_packets_(GetQuicFlag(FLAGS_quic_max_tracked_packet_count)) {} + max_tracked_packets_(GetQuicFlag(FLAGS_quic_max_tracked_packet_count)), + max_ack_height_tracker_(max_height_tracker_window_length), + total_bytes_acked_after_last_ack_event_(0) {} BandwidthSampler::~BandwidthSampler() {} @@ -69,6 +100,22 @@ "in it."; } +QuicByteCount BandwidthSampler::OnAckEventEnd( + QuicBandwidth bandwidth_estimate, + QuicRoundTripCount round_trip_count) { + const QuicByteCount newly_acked_bytes = + total_bytes_acked_ - total_bytes_acked_after_last_ack_event_; + + if (newly_acked_bytes == 0) { + return 0; + } + total_bytes_acked_after_last_ack_event_ = total_bytes_acked_; + + return max_ack_height_tracker_.Update(bandwidth_estimate, round_trip_count, + last_acked_packet_ack_time_, + newly_acked_bytes); +} + BandwidthSample BandwidthSampler::OnPacketAcknowledged( QuicTime ack_time, QuicPacketNumber packet_number) {
diff --git a/quic/core/congestion_control/bandwidth_sampler.h b/quic/core/congestion_control/bandwidth_sampler.h index 9a58cf6..6af16b7 100644 --- a/quic/core/congestion_control/bandwidth_sampler.h +++ b/quic/core/congestion_control/bandwidth_sampler.h
@@ -5,6 +5,8 @@ #ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_BANDWIDTH_SAMPLER_H_ #define QUICHE_QUIC_CORE_CONGESTION_CONTROL_BANDWIDTH_SAMPLER_H_ +#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/windowed_filter.h" #include "net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h" #include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" @@ -75,6 +77,41 @@ : bandwidth(QuicBandwidth::Zero()), rtt(QuicTime::Delta::Zero()) {} }; +class QUIC_EXPORT_PRIVATE MaxAckHeightTracker { + public: + explicit MaxAckHeightTracker(QuicRoundTripCount initial_filter_window) + : max_ack_height_filter_(initial_filter_window, 0, 0) {} + + QuicByteCount Get() const { return max_ack_height_filter_.GetBest(); } + + QuicByteCount Update(QuicBandwidth bandwidth_estimate, + QuicRoundTripCount round_trip_count, + QuicTime ack_time, + QuicByteCount bytes_acked); + + void SetFilterWindowLength(QuicRoundTripCount length) { + max_ack_height_filter_.SetWindowLength(length); + } + + void Reset(QuicByteCount new_height, QuicRoundTripCount new_time) { + max_ack_height_filter_.Reset(new_height, new_time); + } + + private: + // Tracks the maximum number of bytes acked faster than the estimated + // bandwidth. + typedef WindowedFilter<QuicByteCount, + MaxFilter<QuicByteCount>, + QuicRoundTripCount, + QuicRoundTripCount> + MaxAckHeightFilter; + MaxAckHeightFilter max_ack_height_filter_; + + // The time this aggregation started and the number of bytes acked during it. + QuicTime aggregation_epoch_start_time_ = QuicTime::Zero(); + QuicByteCount aggregation_epoch_bytes_ = 0; +}; + // An interface common to any class that can provide bandwidth samples from the // information per individual acknowledged packet. class QUIC_EXPORT_PRIVATE BandwidthSamplerInterface { @@ -204,7 +241,8 @@ // connection is app-limited, the approach works in other cases too. class QUIC_EXPORT_PRIVATE BandwidthSampler : public BandwidthSamplerInterface { public: - BandwidthSampler(); + explicit BandwidthSampler( + QuicRoundTripCount max_height_tracker_window_length); ~BandwidthSampler() override; void OnPacketSent(QuicTime sent_time, @@ -214,6 +252,8 @@ HasRetransmittableData has_retransmittable_data) override; BandwidthSample OnPacketAcknowledged(QuicTime ack_time, QuicPacketNumber packet_number) override; + QuicByteCount OnAckEventEnd(QuicBandwidth bandwidth_estimate, + QuicRoundTripCount round_trip_count); SendTimeState OnPacketLost(QuicPacketNumber packet_number) override; void OnAppLimited() override; @@ -228,6 +268,17 @@ QuicPacketNumber end_of_app_limited_phase() const override; + QuicByteCount max_ack_height() const { return max_ack_height_tracker_.Get(); } + + void SetMaxAckHeightTrackerWindowLength(QuicRoundTripCount length) { + max_ack_height_tracker_.SetFilterWindowLength(length); + } + + void ResetMaxAckHeightTracker(QuicByteCount new_height, + QuicRoundTripCount new_time) { + max_ack_height_tracker_.Reset(new_height, new_time); + } + private: friend class test::BandwidthSamplerPeer; @@ -333,6 +384,9 @@ QuicTime ack_time, QuicPacketNumber packet_number, const ConnectionStateOnSentPacket& sent_packet); + + MaxAckHeightTracker max_ack_height_tracker_; + QuicByteCount total_bytes_acked_after_last_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 750b14e..9e36bdc 100644 --- a/quic/core/congestion_control/bandwidth_sampler_test.cc +++ b/quic/core/congestion_control/bandwidth_sampler_test.cc
@@ -32,7 +32,8 @@ // A test fixture with utility methods for BandwidthSampler tests. class BandwidthSamplerTest : public QuicTest { protected: - BandwidthSamplerTest() : bytes_in_flight_(0) { + BandwidthSamplerTest() + : sampler_(/*max_height_tracker_window_length=*/0), bytes_in_flight_(0) { // Ensure that the clock does not start at zero. clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); }
diff --git a/quic/core/congestion_control/bbr2_misc.cc b/quic/core/congestion_control/bbr2_misc.cc index 56e7270..e2fc8f0 100644 --- a/quic/core/congestion_control/bbr2_misc.cc +++ b/quic/core/congestion_control/bbr2_misc.cc
@@ -77,42 +77,14 @@ return last_acked_sample.bandwidth_sample.state_at_send; } -QuicByteCount Bbr2MaxAckHeightTracker::Update( - const QuicBandwidth& bandwidth_estimate, - QuicRoundTripCount round_trip_count, - QuicTime ack_time, - QuicByteCount bytes_acked) { - // TODO(wub): Find out whether TCP adds bytes_acked before or after the check. - aggregation_epoch_bytes_ += bytes_acked; - - // 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_); - // 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_ <= expected_bytes_acked) { - // Reset to start measuring a new aggregation epoch. - aggregation_epoch_bytes_ = bytes_acked; - aggregation_epoch_start_time_ = ack_time; - return 0; - } - - // Compute how many extra bytes were delivered vs max bandwidth. - QuicByteCount extra_bytes_acked = - aggregation_epoch_bytes_ - expected_bytes_acked; - max_ack_height_filter_.Update(extra_bytes_acked, round_trip_count); - return extra_bytes_acked; -} - Bbr2NetworkModel::Bbr2NetworkModel(const Bbr2Params* params, QuicTime::Delta initial_rtt, QuicTime initial_rtt_timestamp, float cwnd_gain, float pacing_gain) : params_(params), + bandwidth_sampler_(params->initial_max_ack_height_filter_window), min_rtt_filter_(initial_rtt, initial_rtt_timestamp), - max_ack_height_tracker_(params->initial_max_ack_height_filter_window), cwnd_gain_(cwnd_gain), pacing_gain_(pacing_gain) {} @@ -198,8 +170,7 @@ congestion_event->bytes_lost = total_bytes_lost() - prior_bytes_lost; bytes_lost_in_round_ += congestion_event->bytes_lost; - max_ack_height_tracker_.Update(BandwidthEstimate(), RoundTripCount(), - event_time, congestion_event->bytes_acked); + bandwidth_sampler_.OnAckEventEnd(BandwidthEstimate(), RoundTripCount()); if (!congestion_event->end_of_round_trip) { return;
diff --git a/quic/core/congestion_control/bbr2_misc.h b/quic/core/congestion_control/bbr2_misc.h index d38163b..303e299 100644 --- a/quic/core/congestion_control/bbr2_misc.h +++ b/quic/core/congestion_control/bbr2_misc.h
@@ -20,8 +20,6 @@ namespace quic { -typedef uint64_t QuicRoundTripCount; - template <typename T> class QUIC_EXPORT_PRIVATE Limits { public: @@ -272,32 +270,6 @@ QUIC_EXPORT_PRIVATE const SendTimeState& SendStateOfLargestPacket( const Bbr2CongestionEvent& congestion_event); -class QUIC_EXPORT_PRIVATE Bbr2MaxAckHeightTracker { - public: - explicit Bbr2MaxAckHeightTracker(QuicRoundTripCount initial_filter_window) - : max_ack_height_filter_(initial_filter_window, 0, 0) {} - - QuicByteCount Get() const { return max_ack_height_filter_.GetBest(); } - - QuicByteCount Update(const QuicBandwidth& bandwidth_estimate, - QuicRoundTripCount round_trip_count, - QuicTime ack_time, - QuicByteCount bytes_acked); - - private: - // Tracks the maximum number of bytes acked faster than the sending rate. - typedef WindowedFilter<QuicByteCount, - MaxFilter<QuicByteCount>, - QuicRoundTripCount, - QuicRoundTripCount> - MaxAckHeightFilter; - MaxAckHeightFilter max_ack_height_filter_; - - // The time this aggregation started and the number of bytes acked during it. - QuicTime aggregation_epoch_start_time_ = QuicTime::Zero(); - QuicByteCount aggregation_epoch_bytes_ = 0; -}; - // Bbr2NetworkModel takes low level congestion signals(packets sent/acked/lost) // as input and produces BBRv2 model parameters like inflight_(hi|lo), // bandwidth_(hi|lo), bandwidth and rtt estimates, etc. @@ -353,7 +325,9 @@ QuicBandwidth MaxBandwidth() const { return max_bandwidth_filter_.Get(); } - QuicByteCount MaxAckHeight() const { return max_ack_height_tracker_.Get(); } + QuicByteCount MaxAckHeight() const { + return bandwidth_sampler_.max_ack_height(); + } bool MaybeExpireMinRtt(const Bbr2CongestionEvent& congestion_event); @@ -433,8 +407,6 @@ Bbr2MaxBandwidthFilter max_bandwidth_filter_; MinRttFilter min_rtt_filter_; - Bbr2MaxAckHeightTracker max_ack_height_tracker_; - // Bytes lost in the current round. Updated once per congestion event. QuicByteCount bytes_lost_in_round_ = 0;
diff --git a/quic/core/congestion_control/bbr_sender.cc b/quic/core/congestion_control/bbr_sender.cc index 65a637c..bea0d00 100644 --- a/quic/core/congestion_control/bbr_sender.cc +++ b/quic/core/congestion_control/bbr_sender.cc
@@ -90,6 +90,7 @@ random_(random), stats_(stats), mode_(STARTUP), + sampler_(kBandwidthWindowSize), round_trip_count_(0), max_bandwidth_(kBandwidthWindowSize, QuicBandwidth::Zero(), 0), max_ack_height_(kBandwidthWindowSize, 0, 0), @@ -136,7 +137,12 @@ probe_rtt_skipped_if_similar_rtt_(false), probe_rtt_disabled_if_app_limited_(false), app_limited_since_last_probe_rtt_(false), - min_rtt_since_last_probe_rtt_(QuicTime::Delta::Infinite()) { + min_rtt_since_last_probe_rtt_(QuicTime::Delta::Infinite()), + quic_track_ack_height_in_bandwidth_sampler_( + GetQuicReloadableFlag(quic_track_ack_height_in_bandwidth_sampler)) { + if (quic_track_ack_height_in_bandwidth_sampler_) { + QUIC_RELOADABLE_FLAG_COUNT(quic_track_ack_height_in_bandwidth_sampler); + } if (stats_) { stats_->slowstart_count = 0; stats_->slowstart_start_time = QuicTime::Zero(); @@ -284,10 +290,18 @@ startup_rate_reduction_multiplier_ = 2; } if (config.HasClientRequestedIndependentOption(kBBR4, perspective)) { - max_ack_height_.SetWindowLength(2 * kBandwidthWindowSize); + if (quic_track_ack_height_in_bandwidth_sampler_) { + sampler_.SetMaxAckHeightTrackerWindowLength(2 * kBandwidthWindowSize); + } else { + max_ack_height_.SetWindowLength(2 * kBandwidthWindowSize); + } } if (config.HasClientRequestedIndependentOption(kBBR5, perspective)) { - max_ack_height_.SetWindowLength(4 * kBandwidthWindowSize); + if (quic_track_ack_height_in_bandwidth_sampler_) { + sampler_.SetMaxAckHeightTrackerWindowLength(4 * kBandwidthWindowSize); + } else { + max_ack_height_.SetWindowLength(4 * kBandwidthWindowSize); + } } if (GetQuicReloadableFlag(quic_bbr_less_probe_rtt) && config.HasClientRequestedIndependentOption(kBBR6, perspective)) { @@ -405,7 +419,10 @@ const QuicByteCount bytes_acked = sampler_.total_bytes_acked() - total_bytes_acked_before; - excess_acked = UpdateAckAggregationBytes(event_time, bytes_acked); + excess_acked = quic_track_ack_height_in_bandwidth_sampler_ + ? sampler_.OnAckEventEnd(max_bandwidth_.GetBest(), + round_trip_count_) + : UpdateAckAggregationBytes(event_time, bytes_acked); } // Handle logic specific to PROBE_BW mode. @@ -647,7 +664,11 @@ rounds_without_bandwidth_gain_ = 0; if (expire_ack_aggregation_in_startup_) { // Expire old excess delivery measurements now that bandwidth increased. - max_ack_height_.Reset(0, round_trip_count_); + if (quic_track_ack_height_in_bandwidth_sampler_) { + sampler_.ResetMaxAckHeightTracker(0, round_trip_count_); + } else { + max_ack_height_.Reset(0, round_trip_count_); + } } return; } @@ -847,7 +868,9 @@ GetTargetCongestionWindow(congestion_window_gain_); if (is_at_full_bandwidth_) { // Add the max recently measured ack aggregation to CWND. - target_window += max_ack_height_.GetBest(); + target_window += quic_track_ack_height_in_bandwidth_sampler_ + ? sampler_.max_ack_height() + : max_ack_height_.GetBest(); } else if (enable_ack_aggregation_during_startup_) { // Add the most recent excess acked. Because CWND never decreases in // STARTUP, this will automatically create a very localized max filter.
diff --git a/quic/core/congestion_control/bbr_sender.h b/quic/core/congestion_control/bbr_sender.h index a4d9925..b74b783 100644 --- a/quic/core/congestion_control/bbr_sender.h +++ b/quic/core/congestion_control/bbr_sender.h
@@ -25,8 +25,6 @@ class RttStats; -typedef uint64_t QuicRoundTripCount; - // BbrSender implements BBR congestion control algorithm. BBR aims to estimate // the current available Bottleneck Bandwidth and RTT (hence the name), and // regulates the pacing rate and the size of the congestion window based on @@ -401,6 +399,8 @@ bool probe_rtt_disabled_if_app_limited_; bool app_limited_since_last_probe_rtt_; QuicTime::Delta min_rtt_since_last_probe_rtt_; + // Latched value of --quic_track_ack_height_in_bandwidth_sampler. + const bool quic_track_ack_height_in_bandwidth_sampler_; }; QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
diff --git a/quic/core/congestion_control/send_algorithm_interface.h b/quic/core/congestion_control/send_algorithm_interface.h index 69f7455..dab8fc2 100644 --- a/quic/core/congestion_control/send_algorithm_interface.h +++ b/quic/core/congestion_control/send_algorithm_interface.h
@@ -24,6 +24,8 @@ namespace quic { +typedef uint64_t QuicRoundTripCount; + class CachedNetworkParameters; class RttStats;