gfe-relnote: Add some bbr startup stats into QuicConnectionStats. Stats only. Not protected. PiperOrigin-RevId: 241339065 Change-Id: Idef0c196eb0ab6bbe202a33d79e65a88a43678fd
diff --git a/quic/core/congestion_control/bbr_sender.cc b/quic/core/congestion_control/bbr_sender.cc index a69dc33..e59aa8d 100644 --- a/quic/core/congestion_control/bbr_sender.cc +++ b/quic/core/congestion_control/bbr_sender.cc
@@ -10,6 +10,7 @@ #include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h" #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" #include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" @@ -77,14 +78,17 @@ BbrSender::DebugState::DebugState(const DebugState& state) = default; -BbrSender::BbrSender(const RttStats* rtt_stats, +BbrSender::BbrSender(QuicTime now, + const RttStats* rtt_stats, const QuicUnackedPacketMap* unacked_packets, QuicPacketCount initial_tcp_congestion_window, QuicPacketCount max_tcp_congestion_window, - QuicRandom* random) + QuicRandom* random, + QuicConnectionStats* stats) : rtt_stats_(rtt_stats), unacked_packets_(unacked_packets), random_(random), + stats_(stats), mode_(STARTUP), round_trip_count_(0), max_bandwidth_(kBandwidthWindowSize, QuicBandwidth::Zero(), 0), @@ -134,7 +138,11 @@ probe_rtt_disabled_if_app_limited_(false), app_limited_since_last_probe_rtt_(false), min_rtt_since_last_probe_rtt_(QuicTime::Delta::Infinite()) { - EnterStartupMode(); + if (stats_) { + stats_->slowstart_count = 0; + stats_->slowstart_start_time = QuicTime::Zero(); + } + EnterStartupMode(now); } BbrSender::~BbrSender() {} @@ -156,6 +164,11 @@ QuicPacketNumber packet_number, QuicByteCount bytes, HasRetransmittableData is_retransmittable) { + if (stats_ && InSlowStart()) { + ++stats_->slowstart_packets_sent; + stats_->slowstart_bytes_sent += bytes; + } + last_sent_packet_ = packet_number; if (bytes_in_flight == 0 && sampler_.is_app_limited()) { @@ -425,7 +438,12 @@ return min_congestion_window_; } -void BbrSender::EnterStartupMode() { +void BbrSender::EnterStartupMode(QuicTime now) { + if (stats_) { + ++stats_->slowstart_count; + DCHECK_EQ(stats_->slowstart_start_time, QuicTime::Zero()) << mode_; + stats_->slowstart_start_time = now; + } mode_ = STARTUP; pacing_gain_ = high_gain_; congestion_window_gain_ = high_cwnd_gain_; @@ -450,8 +468,14 @@ void BbrSender::DiscardLostPackets(const LostPacketVector& lost_packets) { for (const LostPacket& packet : lost_packets) { sampler_.OnPacketLost(packet.packet_number); - if (startup_rate_reduction_multiplier_ != 0 && mode_ == STARTUP) { - startup_bytes_lost_ += packet.bytes_lost; + if (mode_ == STARTUP) { + if (stats_) { + ++stats_->slowstart_packets_lost; + stats_->slowstart_bytes_lost += packet.bytes_lost; + } + if (startup_rate_reduction_multiplier_ != 0) { + startup_bytes_lost_ += packet.bytes_lost; + } } } } @@ -461,6 +485,9 @@ last_acked_packet > current_round_trip_end_) { round_trip_count_++; current_round_trip_end_ = last_sent_packet_; + if (stats_ && InSlowStart()) { + ++stats_->slowstart_num_rtts; + } return true; } @@ -603,6 +630,7 @@ void BbrSender::MaybeExitStartupOrDrain(QuicTime now) { if (mode_ == STARTUP && is_at_full_bandwidth_) { + OnExitStartup(now); mode_ = DRAIN; pacing_gain_ = drain_gain_; congestion_window_gain_ = high_cwnd_gain_; @@ -613,10 +641,25 @@ } } +void BbrSender::OnExitStartup(QuicTime now) { + DCHECK_EQ(mode_, STARTUP); + if (stats_) { + DCHECK_NE(stats_->slowstart_start_time, QuicTime::Zero()); + if (now > stats_->slowstart_start_time) { + stats_->slowstart_duration = + now - stats_->slowstart_start_time + stats_->slowstart_duration; + } + stats_->slowstart_start_time = QuicTime::Zero(); + } +} + void BbrSender::MaybeEnterOrExitProbeRtt(QuicTime now, bool is_round_start, bool min_rtt_expired) { if (min_rtt_expired && !exiting_quiescence_ && mode_ != PROBE_RTT) { + if (InSlowStart()) { + OnExitStartup(now); + } mode_ = PROBE_RTT; pacing_gain_ = 1; // Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight| @@ -644,7 +687,7 @@ if (now >= exit_probe_rtt_at_ && probe_rtt_round_passed_) { min_rtt_timestamp_ = now; if (!is_at_full_bandwidth_) { - EnterStartupMode(); + EnterStartupMode(now); } else { EnterProbeBandwidthMode(now); }
diff --git a/quic/core/congestion_control/bbr_sender.h b/quic/core/congestion_control/bbr_sender.h index cae47f9..405300f 100644 --- a/quic/core/congestion_control/bbr_sender.h +++ b/quic/core/congestion_control/bbr_sender.h
@@ -88,11 +88,13 @@ QuicPacketNumber end_of_app_limited_phase; }; - BbrSender(const RttStats* rtt_stats, + BbrSender(QuicTime now, + const RttStats* rtt_stats, const QuicUnackedPacketMap* unacked_packets, QuicPacketCount initial_tcp_congestion_window, QuicPacketCount max_tcp_congestion_window, - QuicRandom* random); + QuicRandom* random, + QuicConnectionStats* stats); BbrSender(const BbrSender&) = delete; BbrSender& operator=(const BbrSender&) = delete; ~BbrSender() override; @@ -192,7 +194,7 @@ bool ShouldExtendMinRttExpiry() const; // Enters the STARTUP mode. - void EnterStartupMode(); + void EnterStartupMode(QuicTime now); // Enters the PROBE_BW mode. void EnterProbeBandwidthMode(QuicTime now); @@ -236,7 +238,7 @@ // Determines the appropriate congestion window for the connection. void CalculateCongestionWindow(QuicByteCount bytes_acked, QuicByteCount excess_acked); - // Determines the approriate window that constrains the in-flight during + // Determines the appropriate window that constrains the in-flight during // recovery. void CalculateRecoveryWindow(QuicByteCount bytes_acked, QuicByteCount bytes_lost); @@ -245,9 +247,13 @@ // will be observed if present. bool IsPipeSufficientlyFull() const; + // Called right before exiting STARTUP. + void OnExitStartup(QuicTime now); + const RttStats* rtt_stats_; const QuicUnackedPacketMap* unacked_packets_; QuicRandom* random_; + QuicConnectionStats* stats_; Mode mode_;
diff --git a/quic/core/congestion_control/bbr_sender_test.cc b/quic/core/congestion_control/bbr_sender_test.cc index 205cf65..4de237f 100644 --- a/quic/core/congestion_control/bbr_sender_test.cc +++ b/quic/core/congestion_control/bbr_sender_test.cc
@@ -23,6 +23,10 @@ #include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h" #include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h" +using testing::AllOf; +using testing::Ge; +using testing::Le; + namespace quic { namespace test { @@ -127,11 +131,11 @@ endpoint->connection()->sent_packet_manager().GetRttStats(); // Ownership of the sender will be overtaken by the endpoint. BbrSender* sender = new BbrSender( - rtt_stats, + endpoint->connection()->clock()->Now(), rtt_stats, QuicSentPacketManagerPeer::GetUnackedPacketMap( QuicConnectionPeer::GetSentPacketManager(endpoint->connection())), kInitialCongestionWindowPackets, kDefaultMaxCongestionWindowPackets, - &random_); + &random_, QuicConnectionPeer::GetStats(endpoint->connection())); QuicConnectionPeer::SetSendAlgorithm(endpoint->connection(), sender); endpoint->RecordTrace(); return sender; @@ -367,11 +371,11 @@ // Decrease the CWND gain so extra CWND is required with stretch acks. FLAGS_quic_bbr_cwnd_gain = 1.0; sender_ = new BbrSender( - rtt_stats_, + bbr_sender_.connection()->clock()->Now(), rtt_stats_, QuicSentPacketManagerPeer::GetUnackedPacketMap( QuicConnectionPeer::GetSentPacketManager(bbr_sender_.connection())), kInitialCongestionWindowPackets, kDefaultMaxCongestionWindowPackets, - &random_); + &random_, QuicConnectionPeer::GetStats(bbr_sender_.connection())); QuicConnectionPeer::SetSendAlgorithm(bbr_sender_.connection(), sender_); // Enable Ack Decimation on the receiver. QuicConnectionPeer::SetAckMode(receiver_.connection(), @@ -1310,5 +1314,27 @@ EXPECT_GE(sender_->ExportDebugState().min_rtt_timestamp, probe_rtt_start); } +TEST_F(BbrSenderTest, StartupStats) { + CreateDefaultSetup(); + + DriveOutOfStartup(); + ASSERT_FALSE(sender_->InSlowStart()); + + const QuicConnectionStats& stats = bbr_sender_.connection()->GetStats(); + EXPECT_EQ(1, stats.slowstart_count); + EXPECT_THAT(stats.slowstart_num_rtts, AllOf(Ge(5), Le(15))); + EXPECT_THAT(stats.slowstart_packets_sent, AllOf(Ge(100), Le(1000))); + EXPECT_THAT(stats.slowstart_bytes_sent, AllOf(Ge(1e5), Le(1e6))); + EXPECT_LE(stats.slowstart_packets_lost, 10); + EXPECT_LE(stats.slowstart_bytes_lost, 1e4); + EXPECT_THAT(stats.slowstart_duration, + AllOf(Ge(QuicTime::Delta::FromMilliseconds(500)), + Le(QuicTime::Delta::FromMilliseconds(1500)))); + EXPECT_EQ(QuicTime::Zero(), stats.slowstart_start_time); + EXPECT_EQ(stats.slowstart_duration, + QuicConnectionPeer::GetSentPacketManager(bbr_sender_.connection()) + ->GetSlowStartDuration()); +} + } // namespace test } // namespace quic
diff --git a/quic/core/congestion_control/send_algorithm_interface.cc b/quic/core/congestion_control/send_algorithm_interface.cc index 07efa43..0b3c6c8 100644 --- a/quic/core/congestion_control/send_algorithm_interface.cc +++ b/quic/core/congestion_control/send_algorithm_interface.cc
@@ -30,9 +30,9 @@ switch (congestion_control_type) { case kGoogCC: // GoogCC is not supported by quic/core, fall back to BBR. case kBBR: - return new BbrSender(rtt_stats, unacked_packets, + return new BbrSender(clock->ApproximateNow(), rtt_stats, unacked_packets, initial_congestion_window, max_congestion_window, - random); + random, stats); case kPCC: if (GetQuicReloadableFlag(quic_enable_pcc3)) { return CreatePccSender(clock, rtt_stats, unacked_packets, random, stats,
diff --git a/quic/core/quic_connection_stats.cc b/quic/core/quic_connection_stats.cc index 5dea2ea..4d5c578 100644 --- a/quic/core/quic_connection_stats.cc +++ b/quic/core/quic_connection_stats.cc
@@ -20,9 +20,14 @@ bytes_spuriously_retransmitted(0), packets_spuriously_retransmitted(0), packets_lost(0), + slowstart_count(0), + slowstart_num_rtts(0), slowstart_packets_sent(0), + slowstart_bytes_sent(0), slowstart_packets_lost(0), slowstart_bytes_lost(0), + slowstart_duration(QuicTime::Delta::Zero()), + slowstart_start_time(QuicTime::Zero()), packets_dropped(0), crypto_retransmit_count(0), loss_timeout_count(0),
diff --git a/quic/core/quic_connection_stats.h b/quic/core/quic_connection_stats.h index 8123fb0..df2f7f8 100644 --- a/quic/core/quic_connection_stats.h +++ b/quic/core/quic_connection_stats.h
@@ -48,12 +48,22 @@ // Number of packets abandoned as lost by the loss detection algorithm. QuicPacketCount packets_lost; + // Number of times this connection went through the slow start phase. + uint32_t slowstart_count; + // Number of round trips spent in slow start. + uint32_t slowstart_num_rtts; // Number of packets sent in slow start. QuicPacketCount slowstart_packets_sent; + // Number of bytes sent in slow start. + QuicByteCount slowstart_bytes_sent; // Number of packets lost exiting slow start. QuicPacketCount slowstart_packets_lost; // Number of bytes lost exiting slow start. QuicByteCount slowstart_bytes_lost; + // Time spent in COMPLETED slow start phases. + QuicTime::Delta slowstart_duration; + // Start time of the last slow start phase. + QuicTime slowstart_start_time; QuicPacketCount packets_dropped; // Duplicate or less than least unacked. size_t crypto_retransmit_count;
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc index 72c5c0f..89757de 100644 --- a/quic/core/quic_sent_packet_manager.cc +++ b/quic/core/quic_sent_packet_manager.cc
@@ -13,6 +13,7 @@ #include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h" #include "net/third_party/quiche/src/quic/core/quic_connection_stats.h" #include "net/third_party/quiche/src/quic/core/quic_pending_retransmission.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_utils.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" @@ -1014,6 +1015,19 @@ return retransmission_delay; } +QuicTime::Delta QuicSentPacketManager::GetSlowStartDuration() const { + if (send_algorithm_->GetCongestionControlType() != kBBR) { + return QuicTime::Delta::Infinite(); + } + + if (!send_algorithm_->InSlowStart()) { + return stats_->slowstart_duration; + } + + return clock_->ApproximateNow() - stats_->slowstart_start_time + + stats_->slowstart_duration; +} + std::string QuicSentPacketManager::GetDebugState() const { return send_algorithm_->GetDebugState(); }
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h index 4310014..c3c227e 100644 --- a/quic/core/quic_sent_packet_manager.h +++ b/quic/core/quic_sent_packet_manager.h
@@ -235,6 +235,14 @@ return send_algorithm_->GetSlowStartThreshold() / kDefaultTCPMSS; } + // Return the total time spent in slow start so far. If the sender is + // currently in slow start, the return value will include the duration between + // the most recent entry to slow start and now. + // + // Only implemented for BBR. Return QuicTime::Delta::Infinite() for other + // congestion controllers. + QuicTime::Delta GetSlowStartDuration() const; + // Returns debugging information about the state of the congestion controller. std::string GetDebugState() const;
diff --git a/quic/core/quic_time.h b/quic/core/quic_time.h index c8a1c84..cd33649 100644 --- a/quic/core/quic_time.h +++ b/quic/core/quic_time.h
@@ -234,6 +234,12 @@ return !(lhs < rhs); } +// Override stream output operator for gtest or CHECK macros. +inline std::ostream& operator<<(std::ostream& output, const QuicTime t) { + output << t.ToDebuggingValue(); + return output; +} + // Non-member arithmetic operators for QuicTime::Delta. inline QuicTime::Delta operator+(QuicTime::Delta lhs, QuicTime::Delta rhs) { return QuicTime::Delta(lhs.time_offset_ + rhs.time_offset_);