mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-16 07:10:38 +01:00
dcsctp: Use integer math in RTO calculations
Following Congestion avoidance and control by V. Jacobson at https://dl.acm.org/doi/10.1145/52324.52356, use integer math instead of floating point. Not that it matters, but it results in some code size savings, and is more efficient. Due to not using floating point math, some golden values in test cases were rounded a bit differently. Bug: webrtc:12614 Change-Id: I0b7d54b8fd9ce7156e6b2582437ef5720f8838ea Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/231229 Reviewed-by: Florent Castelli <orphis@webrtc.org> Commit-Queue: Victor Boivie <boivie@webrtc.org> Cr-Commit-Position: refs/heads/main@{#34956}
This commit is contained in:
parent
211bf7b253
commit
3ed2b8d0b4
3 changed files with 37 additions and 45 deletions
|
@ -9,17 +9,12 @@
|
||||||
*/
|
*/
|
||||||
#include "net/dcsctp/tx/retransmission_timeout.h"
|
#include "net/dcsctp/tx/retransmission_timeout.h"
|
||||||
|
|
||||||
#include <cmath>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "net/dcsctp/public/dcsctp_options.h"
|
#include "net/dcsctp/public/dcsctp_options.h"
|
||||||
|
|
||||||
namespace dcsctp {
|
namespace dcsctp {
|
||||||
namespace {
|
|
||||||
// https://tools.ietf.org/html/rfc4960#section-15
|
|
||||||
constexpr double kRtoAlpha = 0.125;
|
|
||||||
constexpr double kRtoBeta = 0.25;
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
RetransmissionTimeout::RetransmissionTimeout(const DcSctpOptions& options)
|
RetransmissionTimeout::RetransmissionTimeout(const DcSctpOptions& options)
|
||||||
: min_rto_(*options.rto_min),
|
: min_rto_(*options.rto_min),
|
||||||
|
@ -28,42 +23,39 @@ RetransmissionTimeout::RetransmissionTimeout(const DcSctpOptions& options)
|
||||||
rto_(*options.rto_initial) {}
|
rto_(*options.rto_initial) {}
|
||||||
|
|
||||||
void RetransmissionTimeout::ObserveRTT(DurationMs measured_rtt) {
|
void RetransmissionTimeout::ObserveRTT(DurationMs measured_rtt) {
|
||||||
double rtt = *measured_rtt;
|
int32_t rtt = *measured_rtt;
|
||||||
|
|
||||||
// Unrealistic values will be skipped. If a wrongly measured (or otherwise
|
// Unrealistic values will be skipped. If a wrongly measured (or otherwise
|
||||||
// corrupt) value was processed, it could change the state in a way that would
|
// corrupt) value was processed, it could change the state in a way that would
|
||||||
// take a very long time to recover.
|
// take a very long time to recover.
|
||||||
if (rtt < 0.0 || rtt > max_rtt_) {
|
if (rtt < 0 || rtt > max_rtt_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// From https://tools.ietf.org/html/rfc4960#section-6.3.1, but avoiding
|
||||||
|
// floating point math by implementing algorithm from "V. Jacobson: Congestion
|
||||||
|
// avoidance and control", but adapted for SCTP.
|
||||||
if (first_measurement_) {
|
if (first_measurement_) {
|
||||||
// https://tools.ietf.org/html/rfc4960#section-6.3.1
|
scaled_srtt_ = rtt << kRttShift;
|
||||||
// "When the first RTT measurement R is made, set
|
scaled_rtt_var_ = (rtt / 2) << kRttVarShift;
|
||||||
// SRTT <- R,
|
|
||||||
// RTTVAR <- R/2, and
|
|
||||||
// RTO <- SRTT + 4 * RTTVAR."
|
|
||||||
srtt_ = rtt;
|
|
||||||
rttvar_ = rtt * 0.5;
|
|
||||||
rto_ = srtt_ + 4 * rttvar_;
|
|
||||||
first_measurement_ = false;
|
first_measurement_ = false;
|
||||||
} else {
|
} else {
|
||||||
// https://tools.ietf.org/html/rfc4960#section-6.3.1
|
rtt -= (scaled_srtt_ >> kRttShift);
|
||||||
// "When a new RTT measurement R' is made, set
|
scaled_srtt_ += rtt;
|
||||||
// RTTVAR <- (1 - RTO.Beta) * RTTVAR + RTO.Beta * |SRTT - R'|
|
if (rtt < 0) {
|
||||||
// SRTT <- (1 - RTO.Alpha) * SRTT + RTO.Alpha * R'
|
rtt = -rtt;
|
||||||
// RTO <- SRTT + 4 * RTTVAR."
|
}
|
||||||
rttvar_ = (1 - kRtoBeta) * rttvar_ + kRtoBeta * std::abs(srtt_ - rtt);
|
rtt -= (scaled_rtt_var_ >> kRttVarShift);
|
||||||
srtt_ = (1 - kRtoAlpha) * srtt_ + kRtoAlpha * rtt;
|
scaled_rtt_var_ += rtt;
|
||||||
rto_ = srtt_ + 4 * rttvar_;
|
|
||||||
}
|
}
|
||||||
|
rto_ = (scaled_srtt_ >> kRttShift) + scaled_rtt_var_;
|
||||||
|
|
||||||
// If the RTO becomes smaller or equal to RTT, expiration timers will be
|
// If the RTO becomes smaller or equal to RTT, expiration timers will be
|
||||||
// scheduled at the same time as packets are expected. Only happens in
|
// scheduled at the same time as packets are expected. Only happens in
|
||||||
// extremely stable RTTs, i.e. in simulations.
|
// extremely stable RTTs, i.e. in simulations.
|
||||||
rto_ = std::fmax(rto_, rtt + 1);
|
rto_ = std::max(rto_, rtt + 1);
|
||||||
|
|
||||||
// Clamp RTO between min and max.
|
// Clamp RTO between min and max.
|
||||||
rto_ = std::fmin(std::fmax(rto_, min_rto_), max_rto_);
|
rto_ = std::min(std::max(rto_, min_rto_), max_rto_);
|
||||||
}
|
}
|
||||||
} // namespace dcsctp
|
} // namespace dcsctp
|
||||||
|
|
|
@ -27,6 +27,8 @@ namespace dcsctp {
|
||||||
// a lot, which is an indicator of a bad connection.
|
// a lot, which is an indicator of a bad connection.
|
||||||
class RetransmissionTimeout {
|
class RetransmissionTimeout {
|
||||||
public:
|
public:
|
||||||
|
static constexpr int kRttShift = 3;
|
||||||
|
static constexpr int kRttVarShift = 2;
|
||||||
explicit RetransmissionTimeout(const DcSctpOptions& options);
|
explicit RetransmissionTimeout(const DcSctpOptions& options);
|
||||||
|
|
||||||
// To be called when a RTT has been measured, to update the RTO value.
|
// To be called when a RTT has been measured, to update the RTO value.
|
||||||
|
@ -36,22 +38,20 @@ class RetransmissionTimeout {
|
||||||
DurationMs rto() const { return DurationMs(rto_); }
|
DurationMs rto() const { return DurationMs(rto_); }
|
||||||
|
|
||||||
// Returns the smoothed RTT value, in milliseconds.
|
// Returns the smoothed RTT value, in milliseconds.
|
||||||
DurationMs srtt() const { return DurationMs(srtt_); }
|
DurationMs srtt() const { return DurationMs(scaled_srtt_ >> kRttShift); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Note that all intermediate state calculation is done in the floating point
|
const int32_t min_rto_;
|
||||||
// domain, to maintain precision.
|
const int32_t max_rto_;
|
||||||
const double min_rto_;
|
const int32_t max_rtt_;
|
||||||
const double max_rto_;
|
|
||||||
const double max_rtt_;
|
|
||||||
// If this is the first measurement
|
// If this is the first measurement
|
||||||
bool first_measurement_ = true;
|
bool first_measurement_ = true;
|
||||||
// Smoothed Round-Trip Time
|
// Smoothed Round-Trip Time, shifted by kRttShift
|
||||||
double srtt_ = 0.0;
|
int32_t scaled_srtt_ = 0;
|
||||||
// Round-Trip Time Variation
|
// Round-Trip Time Variation, shifted by kRttVarShift
|
||||||
double rttvar_ = 0.0;
|
int32_t scaled_rtt_var_ = 0;
|
||||||
// Retransmission Timeout
|
// Retransmission Timeout
|
||||||
double rto_;
|
int32_t rto_;
|
||||||
};
|
};
|
||||||
} // namespace dcsctp
|
} // namespace dcsctp
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,7 @@ TEST(RetransmissionTimeoutTest, CalculatesRtoForStableRtt) {
|
||||||
rto_.ObserveRTT(DurationMs(125));
|
rto_.ObserveRTT(DurationMs(125));
|
||||||
EXPECT_EQ(*rto_.rto(), 233);
|
EXPECT_EQ(*rto_.rto(), 233);
|
||||||
rto_.ObserveRTT(DurationMs(127));
|
rto_.ObserveRTT(DurationMs(127));
|
||||||
EXPECT_EQ(*rto_.rto(), 208);
|
EXPECT_EQ(*rto_.rto(), 209);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RetransmissionTimeoutTest, CalculatesRtoForUnstableRtt) {
|
TEST(RetransmissionTimeoutTest, CalculatesRtoForUnstableRtt) {
|
||||||
|
@ -116,21 +116,21 @@ TEST(RetransmissionTimeoutTest, WillStabilizeAfterAWhile) {
|
||||||
rto_.ObserveRTT(DurationMs(124));
|
rto_.ObserveRTT(DurationMs(124));
|
||||||
EXPECT_EQ(*rto_.rto(), 800);
|
EXPECT_EQ(*rto_.rto(), 800);
|
||||||
rto_.ObserveRTT(DurationMs(122));
|
rto_.ObserveRTT(DurationMs(122));
|
||||||
EXPECT_EQ(*rto_.rto(), 709);
|
EXPECT_EQ(*rto_.rto(), 710);
|
||||||
rto_.ObserveRTT(DurationMs(123));
|
rto_.ObserveRTT(DurationMs(123));
|
||||||
EXPECT_EQ(*rto_.rto(), 630);
|
EXPECT_EQ(*rto_.rto(), 631);
|
||||||
rto_.ObserveRTT(DurationMs(124));
|
rto_.ObserveRTT(DurationMs(124));
|
||||||
EXPECT_EQ(*rto_.rto(), 561);
|
EXPECT_EQ(*rto_.rto(), 562);
|
||||||
rto_.ObserveRTT(DurationMs(122));
|
rto_.ObserveRTT(DurationMs(122));
|
||||||
EXPECT_EQ(*rto_.rto(), 504);
|
EXPECT_EQ(*rto_.rto(), 505);
|
||||||
rto_.ObserveRTT(DurationMs(124));
|
rto_.ObserveRTT(DurationMs(124));
|
||||||
EXPECT_EQ(*rto_.rto(), 453);
|
EXPECT_EQ(*rto_.rto(), 454);
|
||||||
rto_.ObserveRTT(DurationMs(124));
|
rto_.ObserveRTT(DurationMs(124));
|
||||||
EXPECT_EQ(*rto_.rto(), 409);
|
EXPECT_EQ(*rto_.rto(), 410);
|
||||||
rto_.ObserveRTT(DurationMs(124));
|
rto_.ObserveRTT(DurationMs(124));
|
||||||
EXPECT_EQ(*rto_.rto(), 372);
|
EXPECT_EQ(*rto_.rto(), 372);
|
||||||
rto_.ObserveRTT(DurationMs(124));
|
rto_.ObserveRTT(DurationMs(124));
|
||||||
EXPECT_EQ(*rto_.rto(), 339);
|
EXPECT_EQ(*rto_.rto(), 340);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RetransmissionTimeoutTest, WillAlwaysStayAboveRTT) {
|
TEST(RetransmissionTimeoutTest, WillAlwaysStayAboveRTT) {
|
||||||
|
|
Loading…
Reference in a new issue