Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Unified Diff: webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.cc

Issue 2990163002: Almost full implementation of BBR's core. (Closed)
Patch Set: Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.cc
diff --git a/webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.cc b/webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.cc
index 11979bf4468d1b2291fe92233dcd6573fdfad3f8..b205517b7be93764c8780d05f524091763c8d64a 100644
--- a/webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.cc
+++ b/webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.cc
@@ -12,6 +12,7 @@
#include "webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.h"
#include <stdlib.h>
+#include <algorithm>
#include "webrtc/modules/remote_bitrate_estimator/test/estimators/congestion_window.h"
#include "webrtc/modules/remote_bitrate_estimator/test/estimators/max_bandwidth_filter.h"
@@ -21,7 +22,7 @@ namespace webrtc {
namespace testing {
namespace bwe {
namespace {
-const int kFeedbackIntervalsMs = 3;
+const int kFeedbackIntervalsMs = 5;
// BBR uses this value to double sending rate each round trip. Design document
// suggests using this value.
const float kHighGain = 2.885f;
@@ -35,7 +36,10 @@ const int kMaxRoundsWithoutGrowth = 3;
// Pacing gain values for Probe Bandwidth mode.
const float kPacingGain[] = {1.25, 0.75, 1, 1, 1, 1, 1, 1};
const size_t kGainCycleLength = sizeof(kPacingGain) / sizeof(kPacingGain[0]);
-// The least amount of rounds PROBE_RTT mode should last.
+// Number of rounds for bandwidth estimate to expire.
+const size_t kBandwidthWindowFilterSize = kGainCycleLength + 2;
+const int kMinimumCongestionWindow = 4000;
philipel 2017/08/04 12:08:06 kMinimumCongestionWindow should not be defined in
gnish1 2017/08/07 10:34:28 Done.
+// Least amount number of rounds PROBE_RTT should last.
const int kProbeRttDurationRounds = 1;
// The least amount of milliseconds PROBE_RTT mode should last.
const int kProbeRttDurationMs = 200;
@@ -49,6 +53,20 @@ const float kCruisingCongestionWindowGain = 1.5f;
// Expiration time for min_rtt sample, which is set to 10 seconds according to
// BBR design doc.
const int64_t kMinRttFilterSizeMs = 10000;
+// Pacing gain specific for Recovery mode. Chosen by experiments in simulation
+// tool.
+const float kRecoveryPacingGain = 0.5f;
+// Congestion window gain specific for Recovery mode. Chosen by experiments in
+// simulation tool.
+const float kRecoveryCongestionWindowGain = 1.5f;
+// Number of rounds over which average rtt is stored for Recovery mode.
+const size_t kPastRttsFilterSize = 1;
+// Threshold to assume average rtt has increased for a round. Chosen by
+// experiments in simulation tool.
+const float kRttIncreaseThreshold = 3;
+// Threshold to assume average rtt has decreased for a round. Chosen by
+// experiments in simulation tool.
+const float kRttDecreaseThreshold = 1.5f;
} // namespace
BbrBweSender::BbrBweSender(Clock* clock)
@@ -60,15 +78,33 @@ BbrBweSender::BbrBweSender(Clock* clock)
congestion_window_(new CongestionWindow()),
rand_(new Random(time(NULL))),
round_count_(0),
- last_packet_sent_(0),
round_trip_end_(0),
full_bandwidth_reached_(false),
cycle_start_time_ms_(0),
cycle_index_(0),
+ bytes_acked_(0),
prior_in_flight_(0),
probe_rtt_start_time_ms_(0),
- minimum_congestion_window_start_time_ms_(),
- minimum_congestion_window_start_round_(0) {
+ minimum_congestion_window_start_time_ms_(0),
+ minimum_congestion_window_start_round_(0),
+ bytes_sent_(0),
+ last_packet_sent_sequence_number_(0),
+ last_packet_acked_sequence_number_(0),
+ last_packet_ack_time_(0),
+ last_packet_send_time_(0),
+ pacing_rate_(0),
+ last_high_gain_send_time_(-1),
+ first_high_gain_sent_before_(-1),
+ last_high_gain_sent_before_(-1),
+ first_high_gain_ack_time_(-1),
+ last_high_gain_ack_time_(-1),
+ first_high_gain_acked_before_(-1),
+ last_high_gain_acked_before_(-1),
+ first_high_gain_seq_num_(-1),
+ last_high_gain_seq_num_(-1),
+ high_gain_over_(false),
+ packet_stats_(),
+ past_rtts_() {
// Initially enter Startup mode.
EnterStartup();
}
@@ -79,44 +115,130 @@ int BbrBweSender::GetFeedbackIntervalMs() const {
return kFeedbackIntervalsMs;
}
+void BbrBweSender::CalculatePacingRate() {
+ pacing_rate_ =
+ max_bandwidth_filter_->max_bandwidth_estimate_bps() * pacing_gain_;
+}
+
+void BbrBweSender::HandleLoss(uint64_t last_acked_packet,
philipel 2017/08/04 12:08:07 So, in the case of loss we immediately mark the lo
gnish1 2017/08/07 10:34:28 Done.
+ uint64_t recently_acked_packet) {
+ // Logic specific to wrapping sequence numbers.
+ if (!last_acked_packet)
+ return;
+ for (uint64_t i = last_acked_packet + 1; i < recently_acked_packet; i++)
philipel 2017/08/04 12:08:06 Are |last_acked_packet| and |recently_acked_packet
gnish1 2017/08/07 10:34:28 Done.
+ congestion_window_->AckReceived(packet_stats_[i].payload_size);
+}
+
+void BbrBweSender::AddToPastRtts(int64_t rtt_sample_ms) {
+ uint64_t last_round = 0;
+ if (!past_rtts_.empty())
+ last_round = past_rtts_.rbegin()->round;
philipel 2017/08/04 12:08:06 ... = past_rtts_.back().round;
gnish1 2017/08/07 10:34:27 Done.
+
+ // Try to add the sample to the last round.
+ if (last_round == round_count_ && !past_rtts_.empty()) {
+ BbrBweSender::AverageRtt& last_round_sample = *(past_rtts_.rbegin());
philipel 2017/08/04 12:08:06 BbrBweSender::AverageRtt* = &past_rtts_.back(); W
gnish1 2017/08/07 10:34:28 Done.
+ last_round_sample.sum_of_rtts += rtt_sample_ms;
+ last_round_sample.quantity++;
+ } else {
+ // If the sample belongs to a new round, keep number of rounds in the window
+ // equal to |kPastRttsFilterSize|.
+ if (past_rtts_.size() == kPastRttsFilterSize)
+ past_rtts_.pop_front();
+ past_rtts_.push_back(
+ BbrBweSender::AverageRtt(rtt_sample_ms, 1, round_count_));
+ }
+}
+
void BbrBweSender::GiveFeedback(const FeedbackPacket& feedback) {
+ int64_t now = clock_->TimeInMilliseconds();
+ last_packet_ack_time_ = now;
+ prior_in_flight_ = bytes_acked_;
const BbrBweFeedback& fb = static_cast<const BbrBweFeedback&>(feedback);
// feedback_vector holds values of acknowledged packets' sequence numbers.
const std::vector<uint64_t>& feedback_vector = fb.packet_feedback_vector();
- // Check if new round started for the connection. Round is the period of time
- // from sending packet to its acknowledgement.
+ // Go through all the packets acked, update variables/containers accordingly.
+ for (const auto& f : feedback_vector) {
philipel 2017/08/04 12:08:06 We use auto for situations where it is both obviou
gnish1 2017/08/07 10:34:28 Done.
+ // Completing packet information with a recently received ack.
+ PacketStats& packet = packet_stats_[f];
philipel 2017/08/04 12:08:06 PacketStats*
gnish1 2017/08/07 10:34:28 Done.
+ bytes_acked_ += packet.payload_size;
+ packet.data_sent = bytes_sent_;
+ packet.send_time = last_packet_send_time_;
+ packet.data_acked = bytes_acked_;
+ packet.ack_time = now;
+ // Logic specific to applying "bucket" to high gain, in order to have
+ // quicker ramp-up. We check if we started receiving acks for the packets
+ // sent during high gain phase.
+ if (packet.sequence_number == first_high_gain_seq_num_) {
+ first_high_gain_ack_time_ = now;
+ first_high_gain_acked_before_ = bytes_acked_;
+ }
+ // If the last packet of high gain phase has been acked, high gain phase is
+ // over.
+ if (packet.sequence_number == last_high_gain_seq_num_) {
+ last_high_gain_ack_time_ = now;
+ last_high_gain_acked_before_ = bytes_acked_;
+ high_gain_over_ = true;
+ }
+ // Notify pacer that an ack was received, to adjust data inflight.
+ // TODO(gnish): Add implementation for BitrateObserver class, to notify
+ // pacer about incoming acks.
+ congestion_window_->AckReceived(packet.payload_size);
+ HandleLoss(last_packet_acked_sequence_number_, packet.sequence_number);
+ last_packet_acked_sequence_number_ = packet.sequence_number;
+ // Logic for wrapping sequence numbers. If round started with packet number
+ // x, it can never end on y, if x > y. That could happen when sequence
+ // numbers are wrapped after some point.
+ if (packet.sequence_number == 0)
+ round_trip_end_ = 0;
+ }
+ // Check if new round started for the connection.
bool new_round_started = false;
if (!feedback_vector.empty()) {
- uint64_t last_acked_packet = *feedback_vector.rbegin();
- if (last_acked_packet > round_trip_end_) {
+ if (last_packet_acked_sequence_number_ > round_trip_end_) {
new_round_started = true;
round_count_++;
- round_trip_end_ = last_packet_sent_;
+ round_trip_end_ = last_packet_sent_sequence_number_;
}
}
+ // Try to enter and exit recovery only after a round has ended.
+ if (new_round_started && full_bandwidth_reached_) {
+ TryEnteringRecovery(); // Comment this line to disable entering Recovery
+ // mode.
+ TryExitingRecovery();
philipel 2017/08/04 12:08:06 I think having |new_round_started| and |full_bandw
gnish1 2017/08/07 10:34:28 Done.
+ }
+ bool min_rtt_expired = false;
+ min_rtt_expired =
+ UpdateBandwidthAndMinRtt(now, feedback_vector, bytes_acked_);
if (new_round_started && !full_bandwidth_reached_) {
full_bandwidth_reached_ = max_bandwidth_filter_->FullBandwidthReached(
kStartupGrowthTarget, kMaxRoundsWithoutGrowth);
}
- int now_ms = clock_->TimeInMilliseconds();
switch (mode_) {
break;
case STARTUP:
TryExitingStartup();
break;
case DRAIN:
- TryExitingDrain(now_ms);
+ TryExitingDrain(now);
break;
case PROBE_BW:
- TryUpdatingCyclePhase(now_ms);
+ TryUpdatingCyclePhase(now);
break;
case PROBE_RTT:
- TryExitingProbeRtt(now_ms, 0);
+ TryExitingProbeRtt(now, round_count_, min_rtt_expired);
+ break;
+ case RECOVERY:
break;
}
- TryEnteringProbeRtt(now_ms);
- // TODO(gnish): implement functions updating congestion window and pacing rate
- // controllers.
+ TryEnteringProbeRtt(now, min_rtt_expired);
+ CalculatePacingRate();
+ // Make sure we don't get stuck when pacing_rate is 0, because of simulation
+ // tool specifics.
+ if (!pacing_rate_)
+ pacing_rate_ = 100;
+ BWE_TEST_LOGGING_PLOT(1, "SendRate", now, pacing_rate_ / 1000);
+ // TODO(gnish): Add implementation for BitrateObserver class to update pacing
+ // rate for the pacer and the encoder.
}
size_t BbrBweSender::TargetCongestionWindow(float gain) {
@@ -127,8 +249,78 @@ size_t BbrBweSender::TargetCongestionWindow(float gain) {
return target_congestion_window;
}
-bool BbrBweSender::UpdateBandwidthAndMinRtt() {
- return false;
+rtc::Optional<int64_t> BbrBweSender::CalculateBandwidthSample(
+ size_t data_sent,
+ int64_t time_elapsed_1,
+ size_t data_acked,
+ int64_t time_elapsed_2) {
+ rtc::Optional<int64_t> bandwidth_sample;
+ if (time_elapsed_1 > 0)
+ *bandwidth_sample = data_sent * 8000 / time_elapsed_1;
+ rtc::Optional<int64_t> ack_rate;
+ if (time_elapsed_2 > 0)
+ *ack_rate = data_acked * 8000 / time_elapsed_2;
+ // If send rate couldn't be calculated automaticaly set |bandwidth_sample| to
+ // ack_rate.
+ if (!bandwidth_sample)
+ bandwidth_sample = ack_rate;
+ if (bandwidth_sample && ack_rate)
+ *bandwidth_sample = std::min(*bandwidth_sample, *ack_rate);
+ return bandwidth_sample;
+}
+
+void BbrBweSender::AddSampleForHighGain() {
+ if (!high_gain_over_)
+ return;
+ high_gain_over_ = false;
+ // Calculate data sent/acked and time elapsed only for packets sent during
+ // high gain phase.
+ size_t data_sent = last_high_gain_sent_before_ - first_high_gain_sent_before_;
+ int64_t time_elapsed_1 =
+ last_high_gain_send_time_ - *first_high_gain_send_time_;
+ size_t data_acked =
+ last_high_gain_acked_before_ - first_high_gain_acked_before_;
+ int64_t time_elapsed_2 = last_high_gain_ack_time_ - first_high_gain_ack_time_;
+ rtc::Optional<int64_t> bandwidth_sample = CalculateBandwidthSample(
+ data_sent, time_elapsed_1, data_acked, time_elapsed_2);
+ if (bandwidth_sample)
+ max_bandwidth_filter_->AddBandwidthSample(*bandwidth_sample, round_count_,
+ kBandwidthWindowFilterSize);
+ first_high_gain_send_time_.reset();
+}
+
+bool BbrBweSender::UpdateBandwidthAndMinRtt(
+ int64_t now,
philipel 2017/08/04 12:08:06 now_ms
gnish1 2017/08/07 10:34:28 Done.
+ const std::vector<uint64_t>& feedback_vector,
+ int64_t bytes_acked) {
+ rtc::Optional<int64_t> min_rtt_sample_ms;
+ for (const auto& f : feedback_vector) {
philipel 2017/08/04 12:08:06 for (uint64_t f: feedback_vector)
gnish1 2017/08/07 10:34:28 Done.
+ PacketStats packet = packet_stats_[f];
+ size_t data_sent = packet.data_sent - packet.data_sent_before;
+ int64_t time_elapsed_1 = packet.send_time - packet.send_time_before;
+ size_t data_acked = packet.data_acked - packet.data_acked_before;
+ int64_t time_elapsed_2 = packet.ack_time - packet.ack_time_before;
+ rtc::Optional<int64_t> bandwidth_sample = CalculateBandwidthSample(
+ data_sent, time_elapsed_1, data_acked, time_elapsed_2);
+ if (bandwidth_sample)
+ max_bandwidth_filter_->AddBandwidthSample(*bandwidth_sample, round_count_,
+ kBandwidthWindowFilterSize);
+ AddSampleForHighGain(); // Comment to disable bucket for high gain.
+ if (!min_rtt_sample_ms)
+ *min_rtt_sample_ms = packet.ack_time - packet.send_time_before;
+ else
+ *min_rtt_sample_ms = std::min(*min_rtt_sample_ms,
+ packet.ack_time - packet.send_time_before);
+ AddToPastRtts(packet.ack_time - packet.send_time_before);
+ BWE_TEST_LOGGING_PLOT(1, "MinRtt", now,
+ packet.ack_time - packet.send_time_before);
+ }
+ if (!min_rtt_sample_ms)
+ return false;
+ bool min_rtt_expired =
+ min_rtt_filter_->min_rtt_expired(now, kMinRttFilterSizeMs);
+ min_rtt_filter_->add_rtt_sample(*min_rtt_sample_ms, now, min_rtt_expired);
+ return min_rtt_expired;
}
void BbrBweSender::EnterStartup() {
@@ -189,9 +381,8 @@ void BbrBweSender::TryUpdatingCyclePhase(int64_t now_ms) {
}
}
-void BbrBweSender::TryEnteringProbeRtt(int64_t now_ms) {
- if (min_rtt_filter_->min_rtt_expired(now_ms, kMinRttFilterSizeMs) &&
- mode_ != PROBE_RTT) {
+void BbrBweSender::TryEnteringProbeRtt(int64_t now_ms, bool min_rtt_expired) {
philipel 2017/08/04 12:08:06 This looks like a revert of the changes you made i
gnish1 2017/08/07 10:34:28 Done.
+ if (min_rtt_expired && mode_ != PROBE_RTT) {
mode_ = PROBE_RTT;
pacing_gain_ = 1;
probe_rtt_start_time_ms_ = now_ms;
@@ -199,13 +390,14 @@ void BbrBweSender::TryEnteringProbeRtt(int64_t now_ms) {
}
}
-// minimum_congestion_window_start_time_'s value is set to the first moment when
-// data inflight was less then kMinimumCongestionWindowBytes, we should make
+// |minimum_congestion_window_start_time_|'s value is set to the first moment
+// when data inflight was less then |kMinimumCongestionWindow|, we should make
// sure that BBR has been in PROBE_RTT mode for at least one round or 200ms.
-void BbrBweSender::TryExitingProbeRtt(int64_t now_ms, int64_t round) {
+void BbrBweSender::TryExitingProbeRtt(int64_t now_ms,
+ int64_t round,
+ bool min_rtt_expired) {
philipel 2017/08/04 12:08:06 Undo this change.
gnish1 2017/08/07 10:34:28 Done.
if (!minimum_congestion_window_start_time_ms_) {
- if (congestion_window_->data_inflight() <=
- CongestionWindow::kMinimumCongestionWindowBytes) {
+ if (congestion_window_->data_inflight() <= kMinimumCongestionWindow) {
philipel 2017/08/04 12:08:06 Undo.
gnish1 2017/08/07 10:34:28 Done.
*minimum_congestion_window_start_time_ms_ = now_ms;
minimum_congestion_window_start_round_ = round;
}
@@ -218,13 +410,70 @@ void BbrBweSender::TryExitingProbeRtt(int64_t now_ms, int64_t round) {
}
}
+void BbrBweSender::TryEnteringRecovery() {
+ // If we are already in Recovery don't try to enter.
+ if (mode_ == RECOVERY)
+ return;
+ uint64_t increased_rtt_round_counter = 0;
+ // If average rtt for past |kPastRttsFilterSize| rounds has been more than
+ // some multiplier of min_rtt_ms enter Recovery.
+ for (const auto i : past_rtts_) {
philipel 2017/08/04 12:08:06 Don't use auto.
gnish1 2017/08/07 10:34:28 Done.
+ if (i.sum_of_rtts / (int64_t)i.quantity >=
+ *min_rtt_filter_->min_rtt_ms() * kRttIncreaseThreshold)
+ increased_rtt_round_counter++;
+ }
+ if (increased_rtt_round_counter < kPastRttsFilterSize)
+ return;
+ mode_ = RECOVERY;
+ pacing_gain_ = kRecoveryPacingGain;
+ congestion_window_gain_ = kRecoveryCongestionWindowGain;
+}
+
+void BbrBweSender::TryExitingRecovery() {
+ if (mode_ != RECOVERY)
+ return;
+ // If average rtt for the past round has decreased sufficiently exit Recovery.
+ if (!past_rtts_.empty()) {
+ auto last_round_sample = past_rtts_.rbegin();
+ if (last_round_sample->sum_of_rtts / last_round_sample->quantity <=
+ *min_rtt_filter_->min_rtt_ms() * kRttDecreaseThreshold) {
+ EnterProbeBw(clock_->TimeInMilliseconds());
+ }
+ }
+}
+
int64_t BbrBweSender::TimeUntilNextProcess() {
- return 100;
+ return 50;
}
void BbrBweSender::OnPacketsSent(const Packets& packets) {
- last_packet_sent_ =
- static_cast<const MediaPacket*>(packets.back())->sequence_number();
+ for (Packet* packet : packets) {
+ if (packet->GetPacketType() == Packet::kMedia) {
+ MediaPacket* media_packet = static_cast<MediaPacket*>(packet);
+ bytes_sent_ += media_packet->payload_size();
+ PacketStats packet_stats = PacketStats(
+ media_packet->sequence_number(), 0,
+ media_packet->sender_timestamp_ms(), 0, last_packet_ack_time_,
+ media_packet->payload_size(), 0, bytes_sent_, 0, bytes_acked_);
+ packet_stats_[media_packet->sequence_number()] = packet_stats;
+ last_packet_send_time_ = media_packet->sender_timestamp_ms();
+ last_packet_sent_sequence_number_ = media_packet->sequence_number();
+ // If this is the first packet sent for high gain phase, save data for it.
+ if (!first_high_gain_send_time_ && pacing_gain_ > 1) {
+ *first_high_gain_send_time_ = last_packet_send_time_;
+ first_high_gain_sent_before_ = bytes_sent_;
+ first_high_gain_seq_num_ = media_packet->sequence_number();
+ }
+ // This condition ensures that |last_high_gain_seq_num_| will contain a
+ // sequence number of the last packet sent during high gain phase.
+ if (pacing_gain_ > 1) {
+ last_high_gain_send_time_ = last_packet_send_time_;
+ last_high_gain_sent_before_ = bytes_sent_;
+ last_high_gain_seq_num_ = media_packet->sequence_number();
+ }
+ congestion_window_->PacketSent(media_packet->payload_size());
+ }
+ }
}
void BbrBweSender::Process() {}

Powered by Google App Engine
This is Rietveld 408576698