Adds monitor interval class for PCC.
The PCC congestion control algorithm divides time into consecutive intervals called monitor intervals. This CL adds a class that is used by PCC to measure the performance of sending at a certain rate during one monitor interval. Bug: webrtc:9434 Change-Id: Ia0447e224067d4ca807bcc6fd8083f9083385b91 Reviewed-on: https://webrtc-review.googlesource.com/87140 Commit-Queue: Anastasia Koloskova <koloskova@webrtc.org> Reviewed-by: Sebastian Jansson <srte@webrtc.org> Reviewed-by: Björn Terelius <terelius@webrtc.org> Cr-Commit-Position: refs/heads/master@{#24368}
This commit is contained in:
parent
18a86bfcc1
commit
9171e78ccd
@ -8,6 +8,17 @@
|
||||
|
||||
import("../../../webrtc.gni")
|
||||
|
||||
rtc_static_library("monitor_interval") {
|
||||
sources = [
|
||||
"monitor_interval.cc",
|
||||
"monitor_interval.h",
|
||||
]
|
||||
deps = [
|
||||
"../../../api/transport:network_control",
|
||||
"../../../rtc_base:rtc_base_approved",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_static_library("rtt_tracker") {
|
||||
sources = [
|
||||
"rtt_tracker.cc",
|
||||
@ -23,9 +34,11 @@ if (rtc_include_tests) {
|
||||
rtc_source_set("pcc_unittests") {
|
||||
testonly = true
|
||||
sources = [
|
||||
"monitor_interval_unittest.cc",
|
||||
"rtt_tracker_unittest.cc",
|
||||
]
|
||||
deps = [
|
||||
":monitor_interval",
|
||||
":rtt_tracker",
|
||||
"../../../api/transport:network_control_test",
|
||||
"../../../api/units:data_rate",
|
||||
|
||||
134
modules/congestion_controller/pcc/monitor_interval.cc
Normal file
134
modules/congestion_controller/pcc/monitor_interval.cc
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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/pcc/monitor_interval.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace pcc {
|
||||
|
||||
PccMonitorInterval::PccMonitorInterval(DataRate target_sending_rate,
|
||||
Timestamp start_time,
|
||||
TimeDelta duration)
|
||||
: target_sending_rate_(target_sending_rate),
|
||||
start_time_(start_time),
|
||||
interval_duration_(duration),
|
||||
received_packets_size_(DataSize::Zero()),
|
||||
feedback_collection_done_(false) {}
|
||||
|
||||
PccMonitorInterval::~PccMonitorInterval() = default;
|
||||
|
||||
PccMonitorInterval::PccMonitorInterval(const PccMonitorInterval& other) =
|
||||
default;
|
||||
|
||||
void PccMonitorInterval::OnPacketsFeedback(
|
||||
const std::vector<PacketResult>& packets_results) {
|
||||
for (const PacketResult& packet_result : packets_results) {
|
||||
if (!packet_result.sent_packet.has_value() ||
|
||||
packet_result.sent_packet->send_time <= start_time_) {
|
||||
continue;
|
||||
}
|
||||
// Here we assume that if some packets are reordered with packets sent
|
||||
// after the end of the monitor interval, then they are lost. (Otherwise
|
||||
// it is not clear how long should we wait for packets feedback to arrive).
|
||||
if (packet_result.sent_packet->send_time >
|
||||
start_time_ + interval_duration_) {
|
||||
feedback_collection_done_ = true;
|
||||
return;
|
||||
}
|
||||
if (packet_result.receive_time.IsInfinite()) {
|
||||
lost_packets_sent_time_.push_back(packet_result.sent_packet->send_time);
|
||||
} else {
|
||||
received_packets_.push_back(
|
||||
{packet_result.receive_time - packet_result.sent_packet->send_time,
|
||||
packet_result.sent_packet->send_time});
|
||||
received_packets_size_ += packet_result.sent_packet->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For the formula used in computations see formula for "slope" in the second
|
||||
// method:
|
||||
// https://www.johndcook.com/blog/2008/10/20/comparing-two-ways-to-fit-a-line-to-data/
|
||||
double PccMonitorInterval::ComputeDelayGradient(
|
||||
double delay_gradient_threshold) const {
|
||||
// Early return to prevent division by 0 in case all packets are sent at the
|
||||
// same time.
|
||||
if (received_packets_.empty() || received_packets_.front().sent_time ==
|
||||
received_packets_.back().sent_time) {
|
||||
return 0;
|
||||
}
|
||||
double sum_times = 0;
|
||||
double sum_delays = 0;
|
||||
for (const ReceivedPacket& packet : received_packets_) {
|
||||
double time_delta_us =
|
||||
(packet.sent_time - received_packets_[0].sent_time).us();
|
||||
double delay = packet.delay.us();
|
||||
sum_times += time_delta_us;
|
||||
sum_delays += delay;
|
||||
}
|
||||
double sum_squared_scaled_time_deltas = 0;
|
||||
double sum_scaled_time_delta_dot_delay = 0;
|
||||
for (const ReceivedPacket& packet : received_packets_) {
|
||||
double time_delta_us =
|
||||
(packet.sent_time - received_packets_[0].sent_time).us();
|
||||
double delay = packet.delay.us();
|
||||
double scaled_time_delta_us =
|
||||
time_delta_us - sum_times / received_packets_.size();
|
||||
sum_squared_scaled_time_deltas +=
|
||||
scaled_time_delta_us * scaled_time_delta_us;
|
||||
sum_scaled_time_delta_dot_delay += scaled_time_delta_us * delay;
|
||||
}
|
||||
double rtt_gradient =
|
||||
sum_scaled_time_delta_dot_delay / sum_squared_scaled_time_deltas;
|
||||
if (std::abs(rtt_gradient) < delay_gradient_threshold)
|
||||
rtt_gradient = 0;
|
||||
return rtt_gradient;
|
||||
}
|
||||
|
||||
bool PccMonitorInterval::IsFeedbackCollectionDone() const {
|
||||
return feedback_collection_done_;
|
||||
}
|
||||
|
||||
Timestamp PccMonitorInterval::GetEndTime() const {
|
||||
return start_time_ + interval_duration_;
|
||||
}
|
||||
|
||||
double PccMonitorInterval::GetLossRate() const {
|
||||
size_t packets_lost = lost_packets_sent_time_.size();
|
||||
size_t packets_received = received_packets_.size();
|
||||
if (packets_lost == 0)
|
||||
return 0;
|
||||
return static_cast<double>(packets_lost) / (packets_lost + packets_received);
|
||||
}
|
||||
|
||||
DataRate PccMonitorInterval::GetTargetSendingRate() const {
|
||||
return target_sending_rate_;
|
||||
}
|
||||
|
||||
DataRate PccMonitorInterval::GetTransmittedPacketsRate() const {
|
||||
if (received_packets_.empty()) {
|
||||
return target_sending_rate_;
|
||||
}
|
||||
Timestamp receive_time_of_first_packet =
|
||||
received_packets_.front().sent_time + received_packets_.front().delay;
|
||||
Timestamp receive_time_of_last_packet =
|
||||
received_packets_.back().sent_time + received_packets_.back().delay;
|
||||
if (receive_time_of_first_packet == receive_time_of_last_packet) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "All packets in monitor interval were received at the same time.";
|
||||
return target_sending_rate_;
|
||||
}
|
||||
return received_packets_size_ /
|
||||
(receive_time_of_last_packet - receive_time_of_first_packet);
|
||||
}
|
||||
|
||||
} // namespace pcc
|
||||
} // namespace webrtc
|
||||
68
modules/congestion_controller/pcc/monitor_interval.h
Normal file
68
modules/congestion_controller/pcc/monitor_interval.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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_PCC_MONITOR_INTERVAL_H_
|
||||
#define MODULES_CONGESTION_CONTROLLER_PCC_MONITOR_INTERVAL_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "api/transport/network_control.h"
|
||||
#include "api/transport/network_types.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace pcc {
|
||||
|
||||
// PCC divides time into consecutive monitor intervals which are used to test
|
||||
// consequences for performance of sending at a certain rate.
|
||||
class PccMonitorInterval {
|
||||
public:
|
||||
PccMonitorInterval(DataRate target_sending_rate,
|
||||
Timestamp start_time,
|
||||
TimeDelta duration);
|
||||
~PccMonitorInterval();
|
||||
PccMonitorInterval(const PccMonitorInterval& other);
|
||||
void OnPacketsFeedback(const std::vector<PacketResult>& packets_results);
|
||||
// Returns true if got complete information about packets.
|
||||
// Notice, this only happens when received feedback about the first packet
|
||||
// which were sent after the end of the monitor interval. If such event
|
||||
// doesn't occur, we don't mind anyway and stay in the same state.
|
||||
bool IsFeedbackCollectionDone() const;
|
||||
Timestamp GetEndTime() const;
|
||||
|
||||
double GetLossRate() const;
|
||||
// Estimates the gradient using linear regression on the 2-dimensional
|
||||
// dataset (sampled packets delay, time of sampling).
|
||||
double ComputeDelayGradient(double delay_gradient_threshold) const;
|
||||
DataRate GetTargetSendingRate() const;
|
||||
// How fast receiving side gets packets.
|
||||
DataRate GetTransmittedPacketsRate() const;
|
||||
|
||||
private:
|
||||
struct ReceivedPacket {
|
||||
TimeDelta delay;
|
||||
Timestamp sent_time;
|
||||
};
|
||||
// Target bitrate used to generate and pace the outgoing packets.
|
||||
// Actually sent bitrate might not match the target exactly.
|
||||
DataRate target_sending_rate_;
|
||||
// Start time is not included into interval while end time is included.
|
||||
Timestamp start_time_;
|
||||
TimeDelta interval_duration_;
|
||||
// Vectors below updates while receiving feedback.
|
||||
std::vector<ReceivedPacket> received_packets_;
|
||||
std::vector<Timestamp> lost_packets_sent_time_;
|
||||
DataSize received_packets_size_;
|
||||
bool feedback_collection_done_;
|
||||
};
|
||||
|
||||
} // namespace pcc
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_CONGESTION_CONTROLLER_PCC_MONITOR_INTERVAL_H_
|
||||
186
modules/congestion_controller/pcc/monitor_interval_unittest.cc
Normal file
186
modules/congestion_controller/pcc/monitor_interval_unittest.cc
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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/pcc/monitor_interval.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace pcc {
|
||||
namespace test {
|
||||
namespace {
|
||||
const DataRate kTargetSendingRate = DataRate::kbps(300);
|
||||
const Timestamp kStartTime = Timestamp::us(0);
|
||||
const TimeDelta kPacketsDelta = TimeDelta::ms(1);
|
||||
const TimeDelta kIntervalDuration = TimeDelta::ms(100);
|
||||
const TimeDelta kDefaultDelay = TimeDelta::ms(100);
|
||||
const DataSize kDefaultPacketSize = DataSize::bytes(100);
|
||||
constexpr double kDelayGradientThreshold = 0.01;
|
||||
|
||||
std::vector<PacketResult> CreatePacketResults(
|
||||
const std::vector<Timestamp>& packets_send_times,
|
||||
const std::vector<Timestamp>& packets_received_times = {},
|
||||
const std::vector<DataSize>& packets_sizes = {}) {
|
||||
std::vector<PacketResult> packet_results;
|
||||
for (size_t i = 0; i < packets_send_times.size(); ++i) {
|
||||
SentPacket sent_packet;
|
||||
sent_packet.send_time = packets_send_times[i];
|
||||
if (packets_sizes.empty()) {
|
||||
sent_packet.size = kDefaultPacketSize;
|
||||
} else {
|
||||
sent_packet.size = packets_sizes[i];
|
||||
}
|
||||
PacketResult packet_result;
|
||||
packet_result.sent_packet = sent_packet;
|
||||
if (packets_received_times.empty()) {
|
||||
packet_result.receive_time = packets_send_times[i] + kDefaultDelay;
|
||||
} else {
|
||||
packet_result.receive_time = packets_received_times[i];
|
||||
}
|
||||
packet_results.push_back(packet_result);
|
||||
}
|
||||
return packet_results;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(PccMonitorIntervalTest, InitialValuesAreEqualToOnesSetInConstructor) {
|
||||
PccMonitorInterval interval{kTargetSendingRate, kStartTime,
|
||||
kIntervalDuration};
|
||||
EXPECT_EQ(interval.IsFeedbackCollectionDone(), false);
|
||||
EXPECT_EQ(interval.GetEndTime(), kStartTime + kIntervalDuration);
|
||||
EXPECT_EQ(interval.GetTargetSendingRate(), kTargetSendingRate);
|
||||
}
|
||||
|
||||
TEST(PccMonitorIntervalTest, IndicatesDoneWhenFeedbackReceivedAfterInterval) {
|
||||
PccMonitorInterval interval{kTargetSendingRate, kStartTime,
|
||||
kIntervalDuration};
|
||||
interval.OnPacketsFeedback(CreatePacketResults({kStartTime}));
|
||||
EXPECT_EQ(interval.IsFeedbackCollectionDone(), false);
|
||||
interval.OnPacketsFeedback(
|
||||
CreatePacketResults({kStartTime, kStartTime + kIntervalDuration}));
|
||||
EXPECT_EQ(interval.IsFeedbackCollectionDone(), false);
|
||||
interval.OnPacketsFeedback(CreatePacketResults(
|
||||
{kStartTime + kIntervalDuration, kStartTime + 2 * kIntervalDuration}));
|
||||
EXPECT_EQ(interval.IsFeedbackCollectionDone(), true);
|
||||
}
|
||||
|
||||
TEST(PccMonitorIntervalTest, LossRateIsOneThirdIfLostOnePacketOutOfThree) {
|
||||
PccMonitorInterval interval{kTargetSendingRate, kStartTime,
|
||||
kIntervalDuration};
|
||||
std::vector<Timestamp> start_times = {
|
||||
kStartTime, kStartTime + 0.1 * kIntervalDuration,
|
||||
kStartTime + 0.5 * kIntervalDuration, kStartTime + kIntervalDuration,
|
||||
kStartTime + 2 * kIntervalDuration};
|
||||
std::vector<Timestamp> end_times = {
|
||||
kStartTime + 2 * kIntervalDuration, kStartTime + 2 * kIntervalDuration,
|
||||
Timestamp::Infinity(), kStartTime + 2 * kIntervalDuration,
|
||||
kStartTime + 4 * kIntervalDuration};
|
||||
std::vector<DataSize> packet_sizes = {
|
||||
kDefaultPacketSize, 2 * kDefaultPacketSize, 3 * kDefaultPacketSize,
|
||||
4 * kDefaultPacketSize, 5 * kDefaultPacketSize};
|
||||
std::vector<PacketResult> packet_results =
|
||||
CreatePacketResults(start_times, end_times, packet_sizes);
|
||||
interval.OnPacketsFeedback(packet_results);
|
||||
EXPECT_EQ(interval.IsFeedbackCollectionDone(), true);
|
||||
|
||||
EXPECT_DOUBLE_EQ(interval.GetLossRate(), 1. / 3);
|
||||
}
|
||||
|
||||
TEST(PccMonitorIntervalTest, DelayGradientIsZeroIfNoChangeInPacketDelay) {
|
||||
PccMonitorInterval monitor_interval(kTargetSendingRate, kStartTime,
|
||||
kIntervalDuration);
|
||||
monitor_interval.OnPacketsFeedback(CreatePacketResults(
|
||||
{kStartTime + kPacketsDelta, kStartTime + 2 * kPacketsDelta,
|
||||
kStartTime + 3 * kPacketsDelta, kStartTime + 2 * kIntervalDuration},
|
||||
{kStartTime + kDefaultDelay, Timestamp::Infinity(),
|
||||
kStartTime + kDefaultDelay + 2 * kPacketsDelta, Timestamp::Infinity()},
|
||||
{}));
|
||||
// Delay gradient should be zero, because both received packets have the
|
||||
// same one way delay.
|
||||
EXPECT_DOUBLE_EQ(
|
||||
monitor_interval.ComputeDelayGradient(kDelayGradientThreshold), 0);
|
||||
}
|
||||
|
||||
TEST(PccMonitorIntervalTest,
|
||||
DelayGradientIsZeroWhenOnePacketSentInMonitorInterval) {
|
||||
PccMonitorInterval monitor_interval(kTargetSendingRate, kStartTime,
|
||||
kIntervalDuration);
|
||||
monitor_interval.OnPacketsFeedback(CreatePacketResults(
|
||||
{kStartTime + kPacketsDelta, kStartTime + 2 * kIntervalDuration},
|
||||
{kStartTime + kDefaultDelay, kStartTime + 3 * kIntervalDuration}, {}));
|
||||
// Only one received packet belongs to the monitor_interval, delay gradient
|
||||
// should be zero in this case.
|
||||
EXPECT_DOUBLE_EQ(
|
||||
monitor_interval.ComputeDelayGradient(kDelayGradientThreshold), 0);
|
||||
}
|
||||
|
||||
TEST(PccMonitorIntervalTest, DelayGradientIsOne) {
|
||||
PccMonitorInterval monitor_interval(kTargetSendingRate, kStartTime,
|
||||
kIntervalDuration);
|
||||
monitor_interval.OnPacketsFeedback(CreatePacketResults(
|
||||
{kStartTime + kPacketsDelta, kStartTime + 2 * kPacketsDelta,
|
||||
kStartTime + 3 * kPacketsDelta, kStartTime + 3 * kIntervalDuration},
|
||||
{kStartTime + kDefaultDelay, Timestamp::Infinity(),
|
||||
kStartTime + 4 * kPacketsDelta + kDefaultDelay,
|
||||
kStartTime + 3 * kIntervalDuration},
|
||||
{}));
|
||||
EXPECT_DOUBLE_EQ(
|
||||
monitor_interval.ComputeDelayGradient(kDelayGradientThreshold), 1);
|
||||
}
|
||||
|
||||
TEST(PccMonitorIntervalTest, DelayGradientIsMinusOne) {
|
||||
PccMonitorInterval monitor_interval(kTargetSendingRate, kStartTime,
|
||||
kIntervalDuration);
|
||||
monitor_interval.OnPacketsFeedback(CreatePacketResults(
|
||||
{kStartTime + kPacketsDelta, kStartTime + 2 * kPacketsDelta,
|
||||
kStartTime + 5 * kPacketsDelta, kStartTime + 2 * kIntervalDuration},
|
||||
{kStartTime + kDefaultDelay, Timestamp::Infinity(),
|
||||
kStartTime + kDefaultDelay, kStartTime + 3 * kIntervalDuration},
|
||||
{}));
|
||||
EXPECT_DOUBLE_EQ(
|
||||
monitor_interval.ComputeDelayGradient(kDelayGradientThreshold), -1);
|
||||
}
|
||||
|
||||
TEST(PccMonitorIntervalTest,
|
||||
DelayGradientIsZeroIfItSmallerWhenGradientThreshold) {
|
||||
PccMonitorInterval monitor_interval(kTargetSendingRate, kStartTime,
|
||||
kIntervalDuration);
|
||||
monitor_interval.OnPacketsFeedback(CreatePacketResults(
|
||||
{kStartTime + kPacketsDelta, kStartTime + kPacketsDelta,
|
||||
kStartTime + 102 * kPacketsDelta, kStartTime + 2 * kIntervalDuration},
|
||||
{kStartTime + kDefaultDelay, Timestamp::Infinity(),
|
||||
kStartTime + kDefaultDelay + kPacketsDelta,
|
||||
kStartTime + 3 * kIntervalDuration},
|
||||
{}));
|
||||
// Delay gradient is less than 0.01 hence should be treated as zero.
|
||||
EXPECT_DOUBLE_EQ(
|
||||
monitor_interval.ComputeDelayGradient(kDelayGradientThreshold), 0);
|
||||
}
|
||||
|
||||
TEST(PccMonitorIntervalTest,
|
||||
DelayGradientIsZeroWhenAllPacketsSentAtTheSameTime) {
|
||||
PccMonitorInterval monitor_interval(kTargetSendingRate, kStartTime,
|
||||
kIntervalDuration);
|
||||
monitor_interval.OnPacketsFeedback(CreatePacketResults(
|
||||
{kStartTime + kPacketsDelta, kStartTime + kPacketsDelta,
|
||||
kStartTime + kPacketsDelta, kStartTime + 2 * kIntervalDuration},
|
||||
{kStartTime + kDefaultDelay, Timestamp::Infinity(),
|
||||
kStartTime + kDefaultDelay + kPacketsDelta,
|
||||
kStartTime + 3 * kIntervalDuration},
|
||||
{}));
|
||||
// If all packets were sent at the same time, then delay gradient should be
|
||||
// zero.
|
||||
EXPECT_DOUBLE_EQ(
|
||||
monitor_interval.ComputeDelayGradient(kDelayGradientThreshold), 0);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace pcc
|
||||
} // namespace webrtc
|
||||
Loading…
x
Reference in New Issue
Block a user