diff --git a/modules/congestion_controller/bbr/BUILD.gn b/modules/congestion_controller/bbr/BUILD.gn index ce39abdb97..6b8bd15073 100644 --- a/modules/congestion_controller/bbr/BUILD.gn +++ b/modules/congestion_controller/bbr/BUILD.gn @@ -8,6 +8,17 @@ import("../../../webrtc.gni") +rtc_source_set("data_transfer_tracker") { + sources = [ + "data_transfer_tracker.cc", + "data_transfer_tracker.h", + ] + deps = [ + "../../../rtc_base:checks", + "../../../rtc_base:rtc_base_approved", + "../network_control", + ] +} rtc_source_set("rtt_stats") { sources = [ "rtt_stats.cc", @@ -18,14 +29,15 @@ rtc_source_set("rtt_stats") { "../network_control", ] } - if (rtc_include_tests) { rtc_source_set("bbr_unittests") { testonly = true sources = [ + "data_transfer_tracker_unittest.cc", "rtt_stats_unittest.cc", ] deps = [ + ":data_transfer_tracker", ":rtt_stats", "../../../test:test_support", "../network_control", diff --git a/modules/congestion_controller/bbr/data_transfer_tracker.cc b/modules/congestion_controller/bbr/data_transfer_tracker.cc new file mode 100644 index 0000000000..502d3f1972 --- /dev/null +++ b/modules/congestion_controller/bbr/data_transfer_tracker.cc @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/congestion_controller/bbr/data_transfer_tracker.h" + +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace bbr { + +DataTransferTracker::DataTransferTracker() {} + +DataTransferTracker::~DataTransferTracker() {} + +void DataTransferTracker::AddSample(DataSize size_delta, + Timestamp send_time, + Timestamp ack_time) { + size_sum_ += size_delta; + if (!samples_.empty()) { + RTC_DCHECK_GE(send_time, samples_.back().send_time); + RTC_DCHECK_GE(ack_time, samples_.back().ack_time); + } + if (!samples_.empty() && ack_time == samples_.back().ack_time) { + samples_.back().send_time = send_time; + samples_.back().size_sum = size_sum_; + } else { + Sample new_sample; + new_sample.ack_time = ack_time; + new_sample.send_time = send_time; + new_sample.size_delta = size_delta; + new_sample.size_sum = size_sum_; + samples_.push_back(new_sample); + } +} + +void DataTransferTracker::ClearOldSamples(Timestamp excluding_end) { + while (!samples_.empty() && samples_.front().ack_time < excluding_end) { + samples_.pop_front(); + } +} + +DataTransferTracker::Result DataTransferTracker::GetRatesByAckTime( + Timestamp covered_start, + Timestamp including_end) { + Result res; + // Last sample before covered_start. + const Sample* window_begin = nullptr; + // Sample at end time or first sample after end time- + const Sample* window_end = nullptr; + // To handle the case when the first sample is after covered_start. + if (samples_.front().ack_time < including_end) + window_begin = &samples_.front(); + // To handle the case when the last sample is before including_end. + if (samples_.back().ack_time > covered_start) + window_end = &samples_.back(); + for (const auto& sample : samples_) { + if (sample.ack_time < covered_start) { + window_begin = &sample; + } else if (sample.ack_time >= including_end) { + window_end = &sample; + break; + } + } + if (window_begin != nullptr && window_end != nullptr) { + res.acked_data = window_end->size_sum - window_begin->size_sum; + res.send_timespan = window_end->send_time - window_begin->send_time; + res.ack_timespan = window_end->ack_time - window_begin->ack_time; + } else { + res.acked_data = DataSize::Zero(); + res.ack_timespan = including_end - covered_start; + res.send_timespan = TimeDelta::Zero(); + } + return res; +} + +} // namespace bbr +} // namespace webrtc diff --git a/modules/congestion_controller/bbr/data_transfer_tracker.h b/modules/congestion_controller/bbr/data_transfer_tracker.h new file mode 100644 index 0000000000..fe80fa72a9 --- /dev/null +++ b/modules/congestion_controller/bbr/data_transfer_tracker.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef MODULES_CONGESTION_CONTROLLER_BBR_DATA_TRANSFER_TRACKER_H_ +#define MODULES_CONGESTION_CONTROLLER_BBR_DATA_TRANSFER_TRACKER_H_ + +#include +#include "modules/congestion_controller/network_control/include/network_units.h" + +namespace webrtc { +namespace bbr { +class DataTransferTracker { + public: + struct Result { + TimeDelta ack_timespan; + TimeDelta send_timespan; + DataSize acked_data; + }; + DataTransferTracker(); + ~DataTransferTracker(); + void AddSample(DataSize size_delta, Timestamp send_time, Timestamp ack_time); + void ClearOldSamples(Timestamp excluding_end); + + // Get the average data rate in the window that starts with the last ack which + // comes before covered_start and ends at the first ack that comes after or at + // including_end. + Result GetRatesByAckTime(Timestamp covered_start, Timestamp including_end); + + private: + struct Sample { + Timestamp ack_time; + Timestamp send_time; + DataSize size_delta; + DataSize size_sum; + }; + std::deque samples_; + DataSize size_sum_ = DataSize::Zero(); +}; +} // namespace bbr +} // namespace webrtc +#endif // MODULES_CONGESTION_CONTROLLER_BBR_DATA_TRANSFER_TRACKER_H_ diff --git a/modules/congestion_controller/bbr/data_transfer_tracker_unittest.cc b/modules/congestion_controller/bbr/data_transfer_tracker_unittest.cc new file mode 100644 index 0000000000..e48f0f5c86 --- /dev/null +++ b/modules/congestion_controller/bbr/data_transfer_tracker_unittest.cc @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/congestion_controller/bbr/data_transfer_tracker.h" +#include "modules/congestion_controller/network_control/include/network_units.h" +#include "test/gtest.h" + +namespace webrtc { +namespace bbr { +namespace test { +namespace { +struct ResultForTest { + int64_t ack_span_ms; + int64_t send_span_ms; + int64_t acked_bytes; +}; +class DataTransferTrackerForTest : public DataTransferTracker { + public: + void AddSample(int bytes, int send_time_ms, int ack_time_ms) { + DataTransferTracker::AddSample(DataSize::bytes(bytes), + Timestamp::ms(send_time_ms), + Timestamp::ms(ack_time_ms)); + } + + void ClearOldSamples(int excluding_end_ms) { + DataTransferTracker::ClearOldSamples(Timestamp::ms(excluding_end_ms)); + } + ResultForTest GetRatesByAckTime(int covered_start_ms, int including_end_ms) { + auto result = DataTransferTracker::GetRatesByAckTime( + Timestamp::ms(covered_start_ms), Timestamp::ms(including_end_ms)); + return ResultForTest{result.ack_timespan.ms(), result.send_timespan.ms(), + result.acked_data.bytes()}; + } +}; + +} // namespace + +TEST(DataTransferTrackerTest, TracksData) { + DataTransferTrackerForTest calc; + // Since we dont have any previous reference for the first packet, it won't be + // counted. + calc.AddSample(5555, 100000, 100100); + calc.AddSample(1000, 100020, 100120); + calc.AddSample(1000, 100040, 100140); + calc.AddSample(1000, 100060, 100160); + + auto result = calc.GetRatesByAckTime(100000, 100200); + EXPECT_EQ(result.acked_bytes, 3000); + EXPECT_EQ(result.ack_span_ms, 60); + EXPECT_EQ(result.send_span_ms, 60); +} + +TEST(DataTransferTrackerTest, CoversStartTime) { + DataTransferTrackerForTest calc; + calc.AddSample(5555, 100000, 100100); + calc.AddSample(1000, 100020, 100120); + calc.AddSample(1000, 100040, 100140); + calc.AddSample(1000, 100060, 100160); + calc.AddSample(1000, 100080, 100180); + + auto result = calc.GetRatesByAckTime(100140, 100200); + EXPECT_EQ(result.acked_bytes, 3000); + EXPECT_EQ(result.ack_span_ms, 60); + EXPECT_EQ(result.send_span_ms, 60); +} + +TEST(DataTransferTrackerTest, IncludesEndExcludesPastEnd) { + DataTransferTrackerForTest calc; + calc.AddSample(5555, 100000, 100100); + calc.AddSample(1000, 100020, 100120); + calc.AddSample(1000, 100040, 100140); + calc.AddSample(1000, 100060, 100160); + calc.AddSample(1000, 100080, 100180); + + auto result = calc.GetRatesByAckTime(100120, 100160); + EXPECT_EQ(result.acked_bytes, 3000); + EXPECT_EQ(result.ack_span_ms, 60); + EXPECT_EQ(result.send_span_ms, 60); +} + +TEST(DataTransferTrackerTest, AccumulatesDuplicates) { + DataTransferTrackerForTest calc; + calc.AddSample(5555, 100000, 100100); + // Two packets at same time, should be accumulated. + calc.AddSample(1000, 100020, 100120); + calc.AddSample(1000, 100020, 100120); + calc.AddSample(1000, 100060, 100160); + // Two packets at same time, should be accumulated. + calc.AddSample(1000, 100100, 100200); + calc.AddSample(1000, 100100, 100200); + calc.AddSample(1000, 100120, 100220); + + auto result = calc.GetRatesByAckTime(100120, 100200); + EXPECT_EQ(result.acked_bytes, 5000); + EXPECT_EQ(result.ack_span_ms, 100); + EXPECT_EQ(result.send_span_ms, 100); +} + +TEST(DataTransferTrackerTest, RemovesOldData) { + DataTransferTrackerForTest calc; + calc.AddSample(5555, 100000, 100100); + calc.AddSample(1000, 100020, 100120); + calc.AddSample(1000, 100040, 100140); + calc.AddSample(1000, 100060, 100160); + calc.AddSample(1000, 100080, 100180); + { + auto result = calc.GetRatesByAckTime(100120, 100200); + EXPECT_EQ(result.acked_bytes, 4000); + EXPECT_EQ(result.ack_span_ms, 80); + EXPECT_EQ(result.send_span_ms, 80); + } + // Note that this operation means that the packet acked at 100140 will not be + // counted any more, just used as time reference. + calc.ClearOldSamples(100140); + { + auto result = calc.GetRatesByAckTime(100120, 100200); + EXPECT_EQ(result.acked_bytes, 2000); + EXPECT_EQ(result.ack_span_ms, 40); + EXPECT_EQ(result.send_span_ms, 40); + } +} +} // namespace test +} // namespace bbr +} // namespace webrtc