mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 13:50:40 +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 <cmath>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
#include "net/dcsctp/public/dcsctp_options.h"
|
||||
|
||||
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)
|
||||
: min_rto_(*options.rto_min),
|
||||
|
@ -28,42 +23,39 @@ RetransmissionTimeout::RetransmissionTimeout(const DcSctpOptions& options)
|
|||
rto_(*options.rto_initial) {}
|
||||
|
||||
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
|
||||
// corrupt) value was processed, it could change the state in a way that would
|
||||
// take a very long time to recover.
|
||||
if (rtt < 0.0 || rtt > max_rtt_) {
|
||||
if (rtt < 0 || rtt > max_rtt_) {
|
||||
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_) {
|
||||
// https://tools.ietf.org/html/rfc4960#section-6.3.1
|
||||
// "When the first RTT measurement R is made, set
|
||||
// SRTT <- R,
|
||||
// RTTVAR <- R/2, and
|
||||
// RTO <- SRTT + 4 * RTTVAR."
|
||||
srtt_ = rtt;
|
||||
rttvar_ = rtt * 0.5;
|
||||
rto_ = srtt_ + 4 * rttvar_;
|
||||
scaled_srtt_ = rtt << kRttShift;
|
||||
scaled_rtt_var_ = (rtt / 2) << kRttVarShift;
|
||||
first_measurement_ = false;
|
||||
} else {
|
||||
// https://tools.ietf.org/html/rfc4960#section-6.3.1
|
||||
// "When a new RTT measurement R' is made, set
|
||||
// RTTVAR <- (1 - RTO.Beta) * RTTVAR + RTO.Beta * |SRTT - R'|
|
||||
// SRTT <- (1 - RTO.Alpha) * SRTT + RTO.Alpha * R'
|
||||
// RTO <- SRTT + 4 * RTTVAR."
|
||||
rttvar_ = (1 - kRtoBeta) * rttvar_ + kRtoBeta * std::abs(srtt_ - rtt);
|
||||
srtt_ = (1 - kRtoAlpha) * srtt_ + kRtoAlpha * rtt;
|
||||
rto_ = srtt_ + 4 * rttvar_;
|
||||
rtt -= (scaled_srtt_ >> kRttShift);
|
||||
scaled_srtt_ += rtt;
|
||||
if (rtt < 0) {
|
||||
rtt = -rtt;
|
||||
}
|
||||
rtt -= (scaled_rtt_var_ >> kRttVarShift);
|
||||
scaled_rtt_var_ += rtt;
|
||||
}
|
||||
rto_ = (scaled_srtt_ >> kRttShift) + scaled_rtt_var_;
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
rto_ = std::fmin(std::fmax(rto_, min_rto_), max_rto_);
|
||||
rto_ = std::min(std::max(rto_, min_rto_), max_rto_);
|
||||
}
|
||||
} // namespace dcsctp
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace dcsctp {
|
|||
// a lot, which is an indicator of a bad connection.
|
||||
class RetransmissionTimeout {
|
||||
public:
|
||||
static constexpr int kRttShift = 3;
|
||||
static constexpr int kRttVarShift = 2;
|
||||
explicit RetransmissionTimeout(const DcSctpOptions& options);
|
||||
|
||||
// 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_); }
|
||||
|
||||
// Returns the smoothed RTT value, in milliseconds.
|
||||
DurationMs srtt() const { return DurationMs(srtt_); }
|
||||
DurationMs srtt() const { return DurationMs(scaled_srtt_ >> kRttShift); }
|
||||
|
||||
private:
|
||||
// Note that all intermediate state calculation is done in the floating point
|
||||
// domain, to maintain precision.
|
||||
const double min_rto_;
|
||||
const double max_rto_;
|
||||
const double max_rtt_;
|
||||
const int32_t min_rto_;
|
||||
const int32_t max_rto_;
|
||||
const int32_t max_rtt_;
|
||||
// If this is the first measurement
|
||||
bool first_measurement_ = true;
|
||||
// Smoothed Round-Trip Time
|
||||
double srtt_ = 0.0;
|
||||
// Round-Trip Time Variation
|
||||
double rttvar_ = 0.0;
|
||||
// Smoothed Round-Trip Time, shifted by kRttShift
|
||||
int32_t scaled_srtt_ = 0;
|
||||
// Round-Trip Time Variation, shifted by kRttVarShift
|
||||
int32_t scaled_rtt_var_ = 0;
|
||||
// Retransmission Timeout
|
||||
double rto_;
|
||||
int32_t rto_;
|
||||
};
|
||||
} // namespace dcsctp
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ TEST(RetransmissionTimeoutTest, CalculatesRtoForStableRtt) {
|
|||
rto_.ObserveRTT(DurationMs(125));
|
||||
EXPECT_EQ(*rto_.rto(), 233);
|
||||
rto_.ObserveRTT(DurationMs(127));
|
||||
EXPECT_EQ(*rto_.rto(), 208);
|
||||
EXPECT_EQ(*rto_.rto(), 209);
|
||||
}
|
||||
|
||||
TEST(RetransmissionTimeoutTest, CalculatesRtoForUnstableRtt) {
|
||||
|
@ -116,21 +116,21 @@ TEST(RetransmissionTimeoutTest, WillStabilizeAfterAWhile) {
|
|||
rto_.ObserveRTT(DurationMs(124));
|
||||
EXPECT_EQ(*rto_.rto(), 800);
|
||||
rto_.ObserveRTT(DurationMs(122));
|
||||
EXPECT_EQ(*rto_.rto(), 709);
|
||||
EXPECT_EQ(*rto_.rto(), 710);
|
||||
rto_.ObserveRTT(DurationMs(123));
|
||||
EXPECT_EQ(*rto_.rto(), 630);
|
||||
EXPECT_EQ(*rto_.rto(), 631);
|
||||
rto_.ObserveRTT(DurationMs(124));
|
||||
EXPECT_EQ(*rto_.rto(), 561);
|
||||
EXPECT_EQ(*rto_.rto(), 562);
|
||||
rto_.ObserveRTT(DurationMs(122));
|
||||
EXPECT_EQ(*rto_.rto(), 504);
|
||||
EXPECT_EQ(*rto_.rto(), 505);
|
||||
rto_.ObserveRTT(DurationMs(124));
|
||||
EXPECT_EQ(*rto_.rto(), 453);
|
||||
EXPECT_EQ(*rto_.rto(), 454);
|
||||
rto_.ObserveRTT(DurationMs(124));
|
||||
EXPECT_EQ(*rto_.rto(), 409);
|
||||
EXPECT_EQ(*rto_.rto(), 410);
|
||||
rto_.ObserveRTT(DurationMs(124));
|
||||
EXPECT_EQ(*rto_.rto(), 372);
|
||||
rto_.ObserveRTT(DurationMs(124));
|
||||
EXPECT_EQ(*rto_.rto(), 339);
|
||||
EXPECT_EQ(*rto_.rto(), 340);
|
||||
}
|
||||
|
||||
TEST(RetransmissionTimeoutTest, WillAlwaysStayAboveRTT) {
|
||||
|
|
Loading…
Reference in a new issue