mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 05:40:42 +01:00
Add rtt estimate EventBasedExponentialMovingAverage to Connection
This patch estimates the connection RTT using EventBasedExponentialMovingAverage. The half time is set to 500 but can be modified using field trials. This new metric is currently unused, but will be used for exploration of of whether it can be used instead of the existing metric. Bug: webrtc:11140 Change-Id: I9db93e9b9eb932e3cd18935cd4ce0d90fc1cb293 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/161000 Commit-Queue: Jonas Oreland <jonaso@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29944}
This commit is contained in:
parent
6532fc6024
commit
bfcb6c3f13
8 changed files with 121 additions and 8 deletions
|
@ -98,6 +98,7 @@ rtc_library("rtc_p2p") {
|
||||||
"../logging:ice_log",
|
"../logging:ice_log",
|
||||||
"../rtc_base",
|
"../rtc_base",
|
||||||
"../rtc_base:checks",
|
"../rtc_base:checks",
|
||||||
|
"../rtc_base:rtc_numerics",
|
||||||
"../rtc_base/experiments:field_trial_parser",
|
"../rtc_base/experiments:field_trial_parser",
|
||||||
"//third_party/abseil-cpp/absl/memory",
|
"//third_party/abseil-cpp/absl/memory",
|
||||||
|
|
||||||
|
|
|
@ -128,6 +128,8 @@ const int DEFAULT_RTT = 3000; // 3 seconds
|
||||||
const int MINIMUM_RTT = 100; // 0.1 seconds
|
const int MINIMUM_RTT = 100; // 0.1 seconds
|
||||||
const int MAXIMUM_RTT = 60000; // 60 seconds
|
const int MAXIMUM_RTT = 60000; // 60 seconds
|
||||||
|
|
||||||
|
const int DEFAULT_RTT_ESTIMATE_HALF_TIME_MS = 500;
|
||||||
|
|
||||||
// Computes our estimate of the RTT given the current estimate.
|
// Computes our estimate of the RTT given the current estimate.
|
||||||
inline int ConservativeRTTEstimate(int rtt) {
|
inline int ConservativeRTTEstimate(int rtt) {
|
||||||
return rtc::SafeClamp(2 * rtt, MINIMUM_RTT, MAXIMUM_RTT);
|
return rtc::SafeClamp(2 * rtt, MINIMUM_RTT, MAXIMUM_RTT);
|
||||||
|
@ -138,6 +140,9 @@ const int RTT_RATIO = 3; // 3 : 1
|
||||||
|
|
||||||
constexpr int64_t kMinExtraPingDelayMs = 100;
|
constexpr int64_t kMinExtraPingDelayMs = 100;
|
||||||
|
|
||||||
|
// Default field trials.
|
||||||
|
const cricket::IceFieldTrials kDefaultFieldTrials;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace cricket {
|
namespace cricket {
|
||||||
|
@ -267,7 +272,9 @@ Connection::Connection(Port* port,
|
||||||
last_ping_response_received_(0),
|
last_ping_response_received_(0),
|
||||||
reported_(false),
|
reported_(false),
|
||||||
state_(IceCandidatePairState::WAITING),
|
state_(IceCandidatePairState::WAITING),
|
||||||
time_created_ms_(rtc::TimeMillis()) {
|
time_created_ms_(rtc::TimeMillis()),
|
||||||
|
field_trials_(&kDefaultFieldTrials),
|
||||||
|
rtt_estimate_(DEFAULT_RTT_ESTIMATE_HALF_TIME_MS) {
|
||||||
// All of our connections start in WAITING state.
|
// All of our connections start in WAITING state.
|
||||||
// TODO(mallinath) - Start connections from STATE_FROZEN.
|
// TODO(mallinath) - Start connections from STATE_FROZEN.
|
||||||
// Wire up to send stun packets
|
// Wire up to send stun packets
|
||||||
|
@ -391,6 +398,11 @@ int Connection::receiving_timeout() const {
|
||||||
return receiving_timeout_.value_or(WEAK_CONNECTION_RECEIVE_TIMEOUT);
|
return receiving_timeout_.value_or(WEAK_CONNECTION_RECEIVE_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Connection::SetIceFieldTrials(const IceFieldTrials* field_trials) {
|
||||||
|
field_trials_ = field_trials;
|
||||||
|
rtt_estimate_.SetHalfTime(field_trials->rtt_estimate_halftime_ms);
|
||||||
|
}
|
||||||
|
|
||||||
void Connection::OnSendStunPacket(const void* data,
|
void Connection::OnSendStunPacket(const void* data,
|
||||||
size_t size,
|
size_t size,
|
||||||
StunRequest* req) {
|
StunRequest* req) {
|
||||||
|
@ -741,11 +753,13 @@ void Connection::ReceivedPingResponse(
|
||||||
acked_nomination_ = nomination.value();
|
acked_nomination_ = nomination.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t now = rtc::TimeMillis();
|
||||||
total_round_trip_time_ms_ += rtt;
|
total_round_trip_time_ms_ += rtt;
|
||||||
current_round_trip_time_ms_ = static_cast<uint32_t>(rtt);
|
current_round_trip_time_ms_ = static_cast<uint32_t>(rtt);
|
||||||
|
rtt_estimate_.AddSample(now, rtt);
|
||||||
|
|
||||||
pings_since_last_response_.clear();
|
pings_since_last_response_.clear();
|
||||||
last_ping_response_received_ = rtc::TimeMillis();
|
last_ping_response_received_ = now;
|
||||||
UpdateReceiving(last_ping_response_received_);
|
UpdateReceiving(last_ping_response_received_);
|
||||||
set_write_state(STATE_WRITABLE);
|
set_write_state(STATE_WRITABLE);
|
||||||
set_state(IceCandidatePairState::SUCCEEDED);
|
set_state(IceCandidatePairState::SUCCEEDED);
|
||||||
|
|
|
@ -20,11 +20,13 @@
|
||||||
#include "logging/rtc_event_log/ice_logger.h"
|
#include "logging/rtc_event_log/ice_logger.h"
|
||||||
#include "p2p/base/candidate_pair_interface.h"
|
#include "p2p/base/candidate_pair_interface.h"
|
||||||
#include "p2p/base/connection_info.h"
|
#include "p2p/base/connection_info.h"
|
||||||
|
#include "p2p/base/p2p_transport_channel_ice_field_trials.h"
|
||||||
#include "p2p/base/stun_request.h"
|
#include "p2p/base/stun_request.h"
|
||||||
#include "p2p/base/transport_description.h"
|
#include "p2p/base/transport_description.h"
|
||||||
#include "rtc_base/async_packet_socket.h"
|
#include "rtc_base/async_packet_socket.h"
|
||||||
#include "rtc_base/message_handler.h"
|
#include "rtc_base/message_handler.h"
|
||||||
#include "rtc_base/network.h"
|
#include "rtc_base/network.h"
|
||||||
|
#include "rtc_base/numerics/event_based_exponential_moving_average.h"
|
||||||
#include "rtc_base/rate_tracker.h"
|
#include "rtc_base/rate_tracker.h"
|
||||||
|
|
||||||
namespace cricket {
|
namespace cricket {
|
||||||
|
@ -302,6 +304,11 @@ class Connection : public CandidatePairInterface,
|
||||||
Port* PortForTest() { return port_; }
|
Port* PortForTest() { return port_; }
|
||||||
const Port* PortForTest() const { return port_; }
|
const Port* PortForTest() const { return port_; }
|
||||||
|
|
||||||
|
void SetIceFieldTrials(const IceFieldTrials* field_trials);
|
||||||
|
const rtc::EventBasedExponentialMovingAverage& GetRttEstimate() const {
|
||||||
|
return rtt_estimate_;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
enum { MSG_DELETE = 0, MSG_FIRST_AVAILABLE };
|
enum { MSG_DELETE = 0, MSG_FIRST_AVAILABLE };
|
||||||
|
|
||||||
|
@ -414,6 +421,9 @@ class Connection : public CandidatePairInterface,
|
||||||
absl::optional<webrtc::IceCandidatePairDescription> log_description_;
|
absl::optional<webrtc::IceCandidatePairDescription> log_description_;
|
||||||
webrtc::IceEventLog* ice_event_log_ = nullptr;
|
webrtc::IceEventLog* ice_event_log_ = nullptr;
|
||||||
|
|
||||||
|
const IceFieldTrials* field_trials_;
|
||||||
|
rtc::EventBasedExponentialMovingAverage rtt_estimate_;
|
||||||
|
|
||||||
friend class Port;
|
friend class Port;
|
||||||
friend class ConnectionRequest;
|
friend class ConnectionRequest;
|
||||||
friend class P2PTransportChannel;
|
friend class P2PTransportChannel;
|
||||||
|
|
|
@ -207,6 +207,7 @@ void P2PTransportChannel::AddConnection(Connection* connection) {
|
||||||
had_connection_ = true;
|
had_connection_ = true;
|
||||||
|
|
||||||
connection->set_ice_event_log(&ice_event_log_);
|
connection->set_ice_event_log(&ice_event_log_);
|
||||||
|
connection->SetIceFieldTrials(&field_trials_);
|
||||||
LogCandidatePairConfig(connection,
|
LogCandidatePairConfig(connection,
|
||||||
webrtc::IceCandidatePairConfigType::kAdded);
|
webrtc::IceCandidatePairConfigType::kAdded);
|
||||||
|
|
||||||
|
@ -646,7 +647,8 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) {
|
||||||
"max_outstanding_pings", &field_trials_.max_outstanding_pings,
|
"max_outstanding_pings", &field_trials_.max_outstanding_pings,
|
||||||
"initial_select_dampening", &field_trials_.initial_select_dampening,
|
"initial_select_dampening", &field_trials_.initial_select_dampening,
|
||||||
"initial_select_dampening_ping_received",
|
"initial_select_dampening_ping_received",
|
||||||
&field_trials_.initial_select_dampening_ping_received)
|
&field_trials_.initial_select_dampening_ping_received,
|
||||||
|
"rtt_estimate_halftime_ms", &field_trials_.rtt_estimate_halftime_ms)
|
||||||
->Parse(webrtc::field_trial::FindFullName("WebRTC-IceFieldTrials"));
|
->Parse(webrtc::field_trial::FindFullName("WebRTC-IceFieldTrials"));
|
||||||
|
|
||||||
if (field_trials_.skip_relay_to_non_relay_connections) {
|
if (field_trials_.skip_relay_to_non_relay_connections) {
|
||||||
|
|
|
@ -31,6 +31,10 @@ struct IceFieldTrials {
|
||||||
// maximum this delay. This will make media slower, but will
|
// maximum this delay. This will make media slower, but will
|
||||||
// give us chance to find a better connection before starting.
|
// give us chance to find a better connection before starting.
|
||||||
absl::optional<int> initial_select_dampening_ping_received;
|
absl::optional<int> initial_select_dampening_ping_received;
|
||||||
|
|
||||||
|
// Decay rate for RTT estimate using EventBasedExponentialMovingAverage
|
||||||
|
// expressed as halving time.
|
||||||
|
int rtt_estimate_halftime_ms = 500;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cricket
|
} // namespace cricket
|
||||||
|
|
|
@ -28,14 +28,30 @@ namespace rtc {
|
||||||
// a sample gets exponentially less weight so that it's 50%
|
// a sample gets exponentially less weight so that it's 50%
|
||||||
// after |half_time| time units has passed.
|
// after |half_time| time units has passed.
|
||||||
EventBasedExponentialMovingAverage::EventBasedExponentialMovingAverage(
|
EventBasedExponentialMovingAverage::EventBasedExponentialMovingAverage(
|
||||||
int half_time)
|
int half_time) {
|
||||||
: tau_(static_cast<double>(half_time) / log(2)) {}
|
SetHalfTime(half_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventBasedExponentialMovingAverage::SetHalfTime(int half_time) {
|
||||||
|
tau_ = static_cast<double>(half_time) / log(2);
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventBasedExponentialMovingAverage::Reset() {
|
||||||
|
value_ = std::nan("uninit");
|
||||||
|
sample_variance_ = std::numeric_limits<double>::infinity();
|
||||||
|
estimator_variance_ = 1;
|
||||||
|
last_observation_timestamp_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
void EventBasedExponentialMovingAverage::AddSample(int64_t now, int sample) {
|
void EventBasedExponentialMovingAverage::AddSample(int64_t now, int sample) {
|
||||||
if (!last_observation_timestamp_.has_value()) {
|
if (!last_observation_timestamp_.has_value()) {
|
||||||
value_ = sample;
|
value_ = sample;
|
||||||
} else {
|
} else {
|
||||||
RTC_DCHECK(now > *last_observation_timestamp_);
|
// TODO(webrtc:11140): This should really be > (e.g not >=)
|
||||||
|
// but some pesky tests run with simulated clock and let
|
||||||
|
// samples arrive simultaneously!
|
||||||
|
RTC_DCHECK(now >= *last_observation_timestamp_);
|
||||||
// Variance gets computed after second sample.
|
// Variance gets computed after second sample.
|
||||||
int64_t age = now - *last_observation_timestamp_;
|
int64_t age = now - *last_observation_timestamp_;
|
||||||
double e = exp(-age / tau_);
|
double e = exp(-age / tau_);
|
||||||
|
|
|
@ -49,8 +49,15 @@ class EventBasedExponentialMovingAverage {
|
||||||
// [ X +/- m ].
|
// [ X +/- m ].
|
||||||
double GetConfidenceInterval() const;
|
double GetConfidenceInterval() const;
|
||||||
|
|
||||||
|
// Reset
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
// Update the half_time.
|
||||||
|
// NOTE: resets estimate too.
|
||||||
|
void SetHalfTime(int half_time);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const double tau_;
|
double tau_;
|
||||||
double value_ = std::nan("uninit");
|
double value_ = std::nan("uninit");
|
||||||
double sample_variance_ = std::numeric_limits<double>::infinity();
|
double sample_variance_ = std::numeric_limits<double>::infinity();
|
||||||
// This is the ratio between variance of the estimate and variance of samples.
|
// This is the ratio between variance of the estimate and variance of samples.
|
||||||
|
|
|
@ -92,7 +92,7 @@ TEST(EventBasedExponentialMovingAverageTest, Almost100) {
|
||||||
|
|
||||||
// Test that getting a value at X and another at X+1
|
// Test that getting a value at X and another at X+1
|
||||||
// is almost the same as getting another at X and a value at X+1.
|
// is almost the same as getting another at X and a value at X+1.
|
||||||
TEST(EventBasedExponentialMovingAverageTest, SameTime) {
|
TEST(EventBasedExponentialMovingAverageTest, AlmostSameTime) {
|
||||||
int64_t time = 23;
|
int64_t time = 23;
|
||||||
constexpr int value = 100;
|
constexpr int value = 100;
|
||||||
|
|
||||||
|
@ -165,4 +165,63 @@ TEST(EventBasedExponentialMovingAverageTest, NonUniformSamplesHalftime100) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(EventBasedExponentialMovingAverageTest, Reset) {
|
||||||
|
constexpr int64_t time = 23;
|
||||||
|
constexpr int value = 100;
|
||||||
|
|
||||||
|
EventBasedExponentialMovingAverage average(100);
|
||||||
|
EXPECT_TRUE(std::isnan(average.GetAverage()));
|
||||||
|
EXPECT_EQ(std::numeric_limits<double>::infinity(), average.GetVariance());
|
||||||
|
EXPECT_EQ(std::numeric_limits<double>::infinity(),
|
||||||
|
average.GetConfidenceInterval());
|
||||||
|
|
||||||
|
average.AddSample(time + 0, value);
|
||||||
|
average.AddSample(time + 100, value);
|
||||||
|
average.AddSample(time + 101, 0);
|
||||||
|
EXPECT_FALSE(std::isnan(average.GetAverage()));
|
||||||
|
|
||||||
|
average.Reset();
|
||||||
|
EXPECT_TRUE(std::isnan(average.GetAverage()));
|
||||||
|
EXPECT_EQ(std::numeric_limits<double>::infinity(), average.GetVariance());
|
||||||
|
EXPECT_EQ(std::numeric_limits<double>::infinity(),
|
||||||
|
average.GetConfidenceInterval());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that SetHalfTime modifies behavior and resets average.
|
||||||
|
TEST(EventBasedExponentialMovingAverageTest, SetHalfTime) {
|
||||||
|
constexpr int64_t time = 23;
|
||||||
|
constexpr int value = 100;
|
||||||
|
|
||||||
|
EventBasedExponentialMovingAverage average(100);
|
||||||
|
|
||||||
|
average.AddSample(time + 0, value);
|
||||||
|
average.AddSample(time + 100, 0);
|
||||||
|
EXPECT_NEAR(66.7, average.GetAverage(), kError);
|
||||||
|
|
||||||
|
average.SetHalfTime(1000);
|
||||||
|
EXPECT_TRUE(std::isnan(average.GetAverage()));
|
||||||
|
EXPECT_EQ(std::numeric_limits<double>::infinity(), average.GetVariance());
|
||||||
|
EXPECT_EQ(std::numeric_limits<double>::infinity(),
|
||||||
|
average.GetConfidenceInterval());
|
||||||
|
|
||||||
|
average.AddSample(time + 0, value);
|
||||||
|
average.AddSample(time + 100, 0);
|
||||||
|
EXPECT_NEAR(51.7, average.GetAverage(), kError);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EventBasedExponentialMovingAverageTest, SimultaneousSamples) {
|
||||||
|
constexpr int64_t time = 23;
|
||||||
|
constexpr int value = 100;
|
||||||
|
|
||||||
|
EventBasedExponentialMovingAverage average(100);
|
||||||
|
|
||||||
|
average.AddSample(time, value);
|
||||||
|
// This should really NOT be supported,
|
||||||
|
// i.e 2 samples with same timestamp.
|
||||||
|
// But there are tests running with simulated clock
|
||||||
|
// that produce this.
|
||||||
|
// TODO(webrtc:11140) : Fix those tests and remove this!
|
||||||
|
average.AddSample(time, value);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
|
Loading…
Reference in a new issue