gfe-relnote: Refactor how QuicConnectionStats.slowstart_duration is calculated in QUIC BBR1, and start populate it in QUIC BBR2. Not protected. (Worst case we get some incorrect values in transport connection stats) Similar to slowstart_duration, I will add drain_duration, probe_rtt_duration, probe_bw_(down|up|cruise)_duration, in a follow up CL. PiperOrigin-RevId: 282224334 Change-Id: I873c39dcd47eb03d22d245cdfbf13aabc1a814e6
diff --git a/quic/core/congestion_control/bbr2_drain.h b/quic/core/congestion_control/bbr2_drain.h index ccc0320..e083aeb 100644 --- a/quic/core/congestion_control/bbr2_drain.h +++ b/quic/core/congestion_control/bbr2_drain.h
@@ -18,6 +18,7 @@ using Bbr2ModeBase::Bbr2ModeBase; void Enter(const Bbr2CongestionEvent& congestion_event) override; + void Leave(const Bbr2CongestionEvent& /*congestion_event*/) override {} Bbr2Mode OnCongestionEvent( QuicByteCount prior_in_flight,
diff --git a/quic/core/congestion_control/bbr2_misc.h b/quic/core/congestion_control/bbr2_misc.h index 3e8a518..139b120 100644 --- a/quic/core/congestion_control/bbr2_misc.h +++ b/quic/core/congestion_control/bbr2_misc.h
@@ -473,7 +473,9 @@ virtual ~Bbr2ModeBase() = default; + // Called when entering/leaving this mode. virtual void Enter(const Bbr2CongestionEvent& congestion_event) = 0; + virtual void Leave(const Bbr2CongestionEvent& congestion_event) = 0; virtual Bbr2Mode OnCongestionEvent( QuicByteCount prior_in_flight,
diff --git a/quic/core/congestion_control/bbr2_probe_bw.h b/quic/core/congestion_control/bbr2_probe_bw.h index 429b6fe..d64bd65 100644 --- a/quic/core/congestion_control/bbr2_probe_bw.h +++ b/quic/core/congestion_control/bbr2_probe_bw.h
@@ -20,6 +20,7 @@ using Bbr2ModeBase::Bbr2ModeBase; void Enter(const Bbr2CongestionEvent& congestion_event) override; + void Leave(const Bbr2CongestionEvent& /*congestion_event*/) override {} Bbr2Mode OnCongestionEvent( QuicByteCount prior_in_flight,
diff --git a/quic/core/congestion_control/bbr2_probe_rtt.h b/quic/core/congestion_control/bbr2_probe_rtt.h index c112a33..80a9d93 100644 --- a/quic/core/congestion_control/bbr2_probe_rtt.h +++ b/quic/core/congestion_control/bbr2_probe_rtt.h
@@ -18,6 +18,7 @@ using Bbr2ModeBase::Bbr2ModeBase; void Enter(const Bbr2CongestionEvent& congestion_event) override; + void Leave(const Bbr2CongestionEvent& /*congestion_event*/) override {} Bbr2Mode OnCongestionEvent( QuicByteCount prior_in_flight,
diff --git a/quic/core/congestion_control/bbr2_sender.cc b/quic/core/congestion_control/bbr2_sender.cc index c90decd..73787a2 100644 --- a/quic/core/congestion_control/bbr2_sender.cc +++ b/quic/core/congestion_control/bbr2_sender.cc
@@ -57,11 +57,12 @@ QuicPacketCount initial_cwnd_in_packets, QuicPacketCount max_cwnd_in_packets, QuicRandom* random, - QuicConnectionStats* /*stats*/) + QuicConnectionStats* stats) : mode_(Bbr2Mode::STARTUP), rtt_stats_(rtt_stats), unacked_packets_(unacked_packets), random_(random), + connection_stats_(stats), params_(kDefaultMinimumCongestionWindow, max_cwnd_in_packets * kDefaultTCPMSS), model_(¶ms_, @@ -75,7 +76,7 @@ pacing_rate_(kInitialPacingGain * QuicBandwidth::FromBytesAndTimeDelta( cwnd_, rtt_stats->SmoothedOrInitialRtt())), - startup_(this, &model_), + startup_(this, &model_, now), drain_(this, &model_), probe_bw_(this, &model_), probe_rtt_(this, &model_), @@ -168,6 +169,7 @@ QUIC_DVLOG(2) << this << " Mode change: " << mode_ << " ==> " << next_mode << " @ " << event_time; + BBR2_MODE_DISPATCH(Leave(congestion_event)); mode_ = next_mode; BBR2_MODE_DISPATCH(Enter(congestion_event)); --mode_changes_allowed;
diff --git a/quic/core/congestion_control/bbr2_sender.h b/quic/core/congestion_control/bbr2_sender.h index bff38f6..f0a3b5f 100644 --- a/quic/core/congestion_control/bbr2_sender.h +++ b/quic/core/congestion_control/bbr2_sender.h
@@ -30,7 +30,7 @@ QuicPacketCount initial_cwnd_in_packets, QuicPacketCount max_cwnd_in_packets, QuicRandom* random, - QuicConnectionStats* /*stats*/); + QuicConnectionStats* stats); ~Bbr2Sender() override = default; @@ -157,6 +157,7 @@ const RttStats* const rtt_stats_; const QuicUnackedPacketMap* const unacked_packets_; QuicRandom* random_; + QuicConnectionStats* connection_stats_; const Bbr2Params params_;
diff --git a/quic/core/congestion_control/bbr2_simulator_test.cc b/quic/core/congestion_control/bbr2_simulator_test.cc index cab9cb8..005e11d 100644 --- a/quic/core/congestion_control/bbr2_simulator_test.cc +++ b/quic/core/congestion_control/bbr2_simulator_test.cc
@@ -24,6 +24,10 @@ #include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h" #include "net/third_party/quiche/src/quic/test_tools/simulator/traffic_policer.h" +using testing::AllOf; +using testing::Ge; +using testing::Le; + namespace quic { using CyclePhase = Bbr2ProbeBwMode::CyclePhase; @@ -676,6 +680,25 @@ EXPECT_LE(sender_loss_rate_in_packets(), 0.30); } +// TODO(wub): Add other slowstart stats to BBRv2. +TEST_F(Bbr2DefaultTopologyTest, StartupStats) { + DefaultTopologyParams params; + CreateNetwork(params); + + DriveOutOfStartup(params); + ASSERT_FALSE(sender_->InSlowStart()); + + const QuicConnectionStats& stats = sender_connection_stats(); + EXPECT_EQ(1u, stats.slowstart_count); + EXPECT_FALSE(stats.slowstart_duration.IsRunning()); + EXPECT_THAT(stats.slowstart_duration.GetTotalElapsedTime(), + AllOf(Ge(QuicTime::Delta::FromMilliseconds(500)), + Le(QuicTime::Delta::FromMilliseconds(1500)))); + EXPECT_EQ(stats.slowstart_duration.GetTotalElapsedTime(), + QuicConnectionPeer::GetSentPacketManager(sender_connection()) + ->GetSlowStartDuration()); +} + // All Bbr2MultiSenderTests uses the following network topology: // // Sender 0 (A Bbr2Sender)
diff --git a/quic/core/congestion_control/bbr2_startup.cc b/quic/core/congestion_control/bbr2_startup.cc index 5ff1050..187b349 100644 --- a/quic/core/congestion_control/bbr2_startup.cc +++ b/quic/core/congestion_control/bbr2_startup.cc
@@ -12,17 +12,30 @@ namespace quic { Bbr2StartupMode::Bbr2StartupMode(const Bbr2Sender* sender, - Bbr2NetworkModel* model) + Bbr2NetworkModel* model, + QuicTime now) : Bbr2ModeBase(sender, model), full_bandwidth_reached_(false), full_bandwidth_baseline_(QuicBandwidth::Zero()), rounds_without_bandwidth_growth_(0), - loss_events_in_round_(0) {} + loss_events_in_round_(0) { + // Clear some startup stats if |sender_->connection_stats_| has been used by + // another sender, which happens e.g. when QuicConnection switch send + // algorithms. + sender_->connection_stats_->slowstart_count = 1; + sender_->connection_stats_->slowstart_duration = QuicTimeAccumulator(); + sender_->connection_stats_->slowstart_duration.Start(now); +} void Bbr2StartupMode::Enter(const Bbr2CongestionEvent& /*congestion_event*/) { QUIC_BUG << "Bbr2StartupMode::Enter should not be called"; } +void Bbr2StartupMode::Leave(const Bbr2CongestionEvent& congestion_event) { + sender_->connection_stats_->slowstart_duration.Stop( + congestion_event.event_time); +} + Bbr2Mode Bbr2StartupMode::OnCongestionEvent( QuicByteCount /*prior_in_flight*/, QuicTime /*event_time*/,
diff --git a/quic/core/congestion_control/bbr2_startup.h b/quic/core/congestion_control/bbr2_startup.h index 489bd55..80539d9 100644 --- a/quic/core/congestion_control/bbr2_startup.h +++ b/quic/core/congestion_control/bbr2_startup.h
@@ -16,9 +16,12 @@ class Bbr2Sender; class QUIC_EXPORT_PRIVATE Bbr2StartupMode final : public Bbr2ModeBase { public: - Bbr2StartupMode(const Bbr2Sender* sender, Bbr2NetworkModel* model); + Bbr2StartupMode(const Bbr2Sender* sender, + Bbr2NetworkModel* model, + QuicTime now); void Enter(const Bbr2CongestionEvent& congestion_event) override; + void Leave(const Bbr2CongestionEvent& congestion_event) override; Bbr2Mode OnCongestionEvent( QuicByteCount prior_in_flight,
diff --git a/quic/core/congestion_control/bbr_sender.cc b/quic/core/congestion_control/bbr_sender.cc index 93ee5cb..4217048 100644 --- a/quic/core/congestion_control/bbr_sender.cc +++ b/quic/core/congestion_control/bbr_sender.cc
@@ -11,6 +11,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/core/quic_time_accumulator.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" @@ -136,8 +137,10 @@ app_limited_since_last_probe_rtt_(false), min_rtt_since_last_probe_rtt_(QuicTime::Delta::Infinite()) { if (stats_) { + // Clear some startup stats if |stats_| has been used by another sender, + // which happens e.g. when QuicConnection switch send algorithms. stats_->slowstart_count = 0; - stats_->slowstart_start_time = QuicTime::Zero(); + stats_->slowstart_duration = QuicTimeAccumulator(); } EnterStartupMode(now); } @@ -465,8 +468,7 @@ void BbrSender::EnterStartupMode(QuicTime now) { if (stats_) { ++stats_->slowstart_count; - DCHECK_EQ(stats_->slowstart_start_time, QuicTime::Zero()) << mode_; - stats_->slowstart_start_time = now; + stats_->slowstart_duration.Start(now); } mode_ = STARTUP; pacing_gain_ = high_gain_; @@ -671,12 +673,7 @@ 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(); + stats_->slowstart_duration.Stop(now); } }
diff --git a/quic/core/congestion_control/bbr_sender_test.cc b/quic/core/congestion_control/bbr_sender_test.cc index 60b303e..6acfe80 100644 --- a/quic/core/congestion_control/bbr_sender_test.cc +++ b/quic/core/congestion_control/bbr_sender_test.cc
@@ -1309,11 +1309,11 @@ EXPECT_THAT(stats.slowstart_bytes_sent, AllOf(Ge(100000u), Le(1000000u))); EXPECT_LE(stats.slowstart_packets_lost, 10u); EXPECT_LE(stats.slowstart_bytes_lost, 10000u); - EXPECT_THAT(stats.slowstart_duration, + EXPECT_FALSE(stats.slowstart_duration.IsRunning()); + EXPECT_THAT(stats.slowstart_duration.GetTotalElapsedTime(), AllOf(Ge(QuicTime::Delta::FromMilliseconds(500)), Le(QuicTime::Delta::FromMilliseconds(1500)))); - EXPECT_EQ(QuicTime::Zero(), stats.slowstart_start_time); - EXPECT_EQ(stats.slowstart_duration, + EXPECT_EQ(stats.slowstart_duration.GetTotalElapsedTime(), QuicConnectionPeer::GetSentPacketManager(bbr_sender_.connection()) ->GetSlowStartDuration()); }
diff --git a/quic/core/quic_connection_stats.cc b/quic/core/quic_connection_stats.cc index e3652b0..ff067bd 100644 --- a/quic/core/quic_connection_stats.cc +++ b/quic/core/quic_connection_stats.cc
@@ -26,8 +26,6 @@ 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), undecryptable_packets_received_before_handshake_complete(0), crypto_retransmit_count(0),
diff --git a/quic/core/quic_connection_stats.h b/quic/core/quic_connection_stats.h index a67d867..552e8a6 100644 --- a/quic/core/quic_connection_stats.h +++ b/quic/core/quic_connection_stats.h
@@ -11,6 +11,7 @@ #include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_time_accumulator.h" #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" namespace quic { @@ -60,10 +61,8 @@ 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; + // Time spent in slow start. Populated for BBRv1 and BBRv2. + QuicTimeAccumulator slowstart_duration; QuicPacketCount packets_dropped; // Duplicate or less than least unacked.
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc index ba3c081..bd0343b 100644 --- a/quic/core/quic_sent_packet_manager.cc +++ b/quic/core/quic_sent_packet_manager.cc
@@ -1077,16 +1077,12 @@ } QuicTime::Delta QuicSentPacketManager::GetSlowStartDuration() const { - if (send_algorithm_->GetCongestionControlType() != kBBR) { - return QuicTime::Delta::Infinite(); + if (send_algorithm_->GetCongestionControlType() == kBBR || + send_algorithm_->GetCongestionControlType() == kBBRv2) { + return stats_->slowstart_duration.GetTotalElapsedTime( + clock_->ApproximateNow()); } - - if (!send_algorithm_->InSlowStart()) { - return stats_->slowstart_duration; - } - - return clock_->ApproximateNow() - stats_->slowstart_start_time + - stats_->slowstart_duration; + return QuicTime::Delta::Infinite(); } std::string QuicSentPacketManager::GetDebugState() const {
diff --git a/quic/core/quic_time_accumulator.h b/quic/core/quic_time_accumulator.h new file mode 100644 index 0000000..772e5ff --- /dev/null +++ b/quic/core/quic_time_accumulator.h
@@ -0,0 +1,66 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_CORE_QUIC_TIME_ACCUMULATOR_H_ +#define QUICHE_QUIC_CORE_QUIC_TIME_ACCUMULATOR_H_ + +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +// QuicTimeAccumulator accumulates elapsed times between Start(s) and Stop(s). +class QUIC_EXPORT_PRIVATE QuicTimeAccumulator { + static constexpr QuicTime kNotRunningSentinel = QuicTime::Infinite(); + + public: + // True if Started and not Stopped. + bool IsRunning() const { return last_start_time_ != kNotRunningSentinel; } + + void Start(QuicTime now) { + DCHECK(!IsRunning()); + last_start_time_ = now; + DCHECK(IsRunning()); + } + + void Stop(QuicTime now) { + DCHECK(IsRunning()); + if (now > last_start_time_) { + total_elapsed_ = total_elapsed_ + (now - last_start_time_); + } + last_start_time_ = kNotRunningSentinel; + DCHECK(!IsRunning()); + } + + // Get total elapsed time between COMPLETED Start/Stop pairs. + QuicTime::Delta GetTotalElapsedTime() const { return total_elapsed_; } + + // Get total elapsed time between COMPLETED Start/Stop pairs, plus, if it is + // running, the elapsed time between |last_start_time_| and |now|. + QuicTime::Delta GetTotalElapsedTime(QuicTime now) const { + if (!IsRunning()) { + return total_elapsed_; + } + if (now <= last_start_time_) { + return total_elapsed_; + } + return total_elapsed_ + (now - last_start_time_); + } + + private: + // + // |last_start_time_| + // | + // V + // Start => Stop => Start => Stop => Start + // | | | | + // |___________| + |___________| = |total_elapsed_| + QuicTime::Delta total_elapsed_ = QuicTime::Delta::Zero(); + QuicTime last_start_time_ = kNotRunningSentinel; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_TIME_ACCUMULATOR_H_
diff --git a/quic/core/quic_time_accumulator_test.cc b/quic/core/quic_time_accumulator_test.cc new file mode 100644 index 0000000..e7bc43a --- /dev/null +++ b/quic/core/quic_time_accumulator_test.cc
@@ -0,0 +1,82 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/quic_time_accumulator.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" + +namespace quic { +namespace test { + +TEST(QuicTimeAccumulator, DefaultConstruct) { + MockClock clock; + clock.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + + QuicTimeAccumulator acc; + EXPECT_FALSE(acc.IsRunning()); + + clock.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + EXPECT_EQ(QuicTime::Delta::Zero(), acc.GetTotalElapsedTime()); + EXPECT_EQ(QuicTime::Delta::Zero(), acc.GetTotalElapsedTime(clock.Now())); +} + +TEST(QuicTimeAccumulator, StartStop) { + MockClock clock; + clock.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + + QuicTimeAccumulator acc; + acc.Start(clock.Now()); + EXPECT_TRUE(acc.IsRunning()); + + clock.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + acc.Stop(clock.Now()); + EXPECT_FALSE(acc.IsRunning()); + + clock.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), acc.GetTotalElapsedTime()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), + acc.GetTotalElapsedTime(clock.Now())); + + acc.Start(clock.Now()); + clock.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), acc.GetTotalElapsedTime()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(15), + acc.GetTotalElapsedTime(clock.Now())); + + clock.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), acc.GetTotalElapsedTime()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20), + acc.GetTotalElapsedTime(clock.Now())); + + acc.Stop(clock.Now()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20), acc.GetTotalElapsedTime()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20), + acc.GetTotalElapsedTime(clock.Now())); +} + +TEST(QuicTimeAccumulator, ClockStepBackwards) { + MockClock clock; + clock.AdvanceTime(QuicTime::Delta::FromMilliseconds(100)); + + QuicTimeAccumulator acc; + acc.Start(clock.Now()); + + clock.AdvanceTime(QuicTime::Delta::FromMilliseconds(-10)); + acc.Stop(clock.Now()); + EXPECT_EQ(QuicTime::Delta::Zero(), acc.GetTotalElapsedTime()); + EXPECT_EQ(QuicTime::Delta::Zero(), acc.GetTotalElapsedTime(clock.Now())); + + acc.Start(clock.Now()); + clock.AdvanceTime(QuicTime::Delta::FromMilliseconds(50)); + acc.Stop(clock.Now()); + + acc.Start(clock.Now()); + clock.AdvanceTime(QuicTime::Delta::FromMilliseconds(-80)); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(50), acc.GetTotalElapsedTime()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(50), + acc.GetTotalElapsedTime(clock.Now())); +} + +} // namespace test +} // namespace quic