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_);