util: use time::nanoseconds in RttEstimator
Refs: #4887
Change-Id: Ie6469732fec53cca4b274b36d7ec58eff7addc1b
diff --git a/ndn-cxx/util/rtt-estimator.cpp b/ndn-cxx/util/rtt-estimator.cpp
index c0d62d6..e214ebb 100644
--- a/ndn-cxx/util/rtt-estimator.cpp
+++ b/ndn-cxx/util/rtt-estimator.cpp
@@ -30,18 +30,25 @@
RttEstimator::RttEstimator(const Options& options)
: m_options(options)
- , m_sRtt(std::numeric_limits<double>::quiet_NaN())
- , m_rttVar(std::numeric_limits<double>::quiet_NaN())
+ , m_sRtt(0)
+ , m_rttVar(0)
, m_rto(m_options.initialRto)
- , m_rttMin(std::numeric_limits<double>::max())
- , m_rttMax(std::numeric_limits<double>::min())
- , m_rttAvg(0.0)
+ , m_rttMin(time::nanoseconds::max())
+ , m_rttMax(time::nanoseconds::min())
+ , m_rttAvg(0)
, m_nRttSamples(0)
{
+ BOOST_ASSERT(m_options.alpha >= 0 && m_options.alpha <= 1);
+ BOOST_ASSERT(m_options.beta >= 0 && m_options.beta <= 1);
+ BOOST_ASSERT(m_options.initialRto >= 0_ns);
+ BOOST_ASSERT(m_options.minRto >= 0_ns);
+ BOOST_ASSERT(m_options.maxRto >= m_options.minRto);
+ BOOST_ASSERT(m_options.k >= 0);
+ BOOST_ASSERT(m_options.rtoBackoffMultiplier >= 1);
}
void
-RttEstimator::addMeasurement(MillisecondsDouble rtt, size_t nExpectedSamples,
+RttEstimator::addMeasurement(time::nanoseconds rtt, size_t nExpectedSamples,
optional<uint64_t> segNum)
{
BOOST_ASSERT(nExpectedSamples > 0);
@@ -49,17 +56,17 @@
if (m_nRttSamples == 0) { // first measurement
m_sRtt = rtt;
m_rttVar = m_sRtt / 2;
- m_rto = m_sRtt + m_options.k * m_rttVar;
}
else {
double alpha = m_options.alpha / nExpectedSamples;
double beta = m_options.beta / nExpectedSamples;
- m_rttVar = (1 - beta) * m_rttVar + beta * time::abs(m_sRtt - rtt);
- m_sRtt = (1 - alpha) * m_sRtt + alpha * rtt;
- m_rto = m_sRtt + m_options.k * m_rttVar;
+ m_rttVar = time::duration_cast<time::nanoseconds>((1 - beta) * m_rttVar +
+ beta * time::abs(m_sRtt - rtt));
+ m_sRtt = time::duration_cast<time::nanoseconds>((1 - alpha) * m_sRtt + alpha * rtt);
}
+ m_rto = clamp(m_sRtt + m_options.k * m_rttVar,
+ m_options.minRto, m_options.maxRto);
- m_rto = clamp(m_rto, m_options.minRto, m_options.maxRto);
afterMeasurement({rtt, m_sRtt, m_rttVar, m_rto, segNum});
m_rttAvg = (m_nRttSamples * m_rttAvg + rtt) / (m_nRttSamples + 1);
diff --git a/ndn-cxx/util/rtt-estimator.hpp b/ndn-cxx/util/rtt-estimator.hpp
index bcd2c12..da43ea4 100644
--- a/ndn-cxx/util/rtt-estimator.hpp
+++ b/ndn-cxx/util/rtt-estimator.hpp
@@ -41,8 +41,6 @@
class RttEstimator
{
public:
- using MillisecondsDouble = time::duration<double, time::milliseconds::period>;
-
class Options
{
public:
@@ -54,9 +52,9 @@
public:
double alpha = 0.125; ///< weight of exponential moving average for smoothed RTT
double beta = 0.25; ///< weight of exponential moving average for RTT variation
- MillisecondsDouble initialRto{1000.0}; ///< initial RTO value
- MillisecondsDouble minRto{200.0}; ///< lower bound of RTO
- MillisecondsDouble maxRto{60000.0}; ///< upper bound of RTO
+ time::nanoseconds initialRto = 1_s; ///< initial RTO value
+ time::nanoseconds minRto = 200_ms; ///< lower bound of RTO
+ time::nanoseconds maxRto = 1_min; ///< upper bound of RTO
int k = 4; ///< RTT variation multiplier used when calculating RTO
int rtoBackoffMultiplier = 2; ///< RTO multiplier used in backoff operation
};
@@ -83,13 +81,13 @@
* @note Do not call this function with RTT samples from retransmitted Interests (per Karn's algorithm).
*/
void
- addMeasurement(MillisecondsDouble rtt, size_t nExpectedSamples,
+ addMeasurement(time::nanoseconds rtt, size_t nExpectedSamples,
optional<uint64_t> segNum = nullopt);
/**
* @brief Returns the estimated RTO value.
*/
- MillisecondsDouble
+ time::nanoseconds
getEstimatedRto() const
{
return m_rto;
@@ -98,7 +96,7 @@
/**
* @brief Returns the minimum RTT observed.
*/
- MillisecondsDouble
+ time::nanoseconds
getMinRtt() const
{
return m_rttMin;
@@ -107,7 +105,7 @@
/**
* @brief Returns the maximum RTT observed.
*/
- MillisecondsDouble
+ time::nanoseconds
getMaxRtt() const
{
return m_rttMax;
@@ -116,22 +114,31 @@
/**
* @brief Returns the average RTT.
*/
- MillisecondsDouble
+ time::nanoseconds
getAvgRtt() const
{
return m_rttAvg;
}
/**
- * @brief Returns the smoothed RTT.
+ * @brief Returns the smoothed RTT value (SRTT).
*/
- MillisecondsDouble
+ time::nanoseconds
getSmoothedRtt() const
{
return m_sRtt;
}
/**
+ * @brief Returns the RTT variation (RTTVAR).
+ */
+ time::nanoseconds
+ getRttVariation() const
+ {
+ return m_rttVar;
+ }
+
+ /**
* @brief Backoff RTO by a factor of Options::rtoBackoffMultiplier.
*/
void
@@ -140,10 +147,10 @@
public:
struct Sample
{
- MillisecondsDouble rtt; ///< measured RTT
- MillisecondsDouble sRtt; ///< smoothed RTT
- MillisecondsDouble rttVar; ///< RTT variation
- MillisecondsDouble rto; ///< retransmission timeout
+ time::nanoseconds rtt; ///< measured RTT
+ time::nanoseconds sRtt; ///< smoothed RTT
+ time::nanoseconds rttVar; ///< RTT variation
+ time::nanoseconds rto; ///< retransmission timeout
optional<uint64_t> segNum; ///< segment number, see description in addMeasurement()
};
@@ -151,16 +158,12 @@
private:
const Options m_options;
-
-NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
- MillisecondsDouble m_sRtt; ///< smoothed round-trip time
- MillisecondsDouble m_rttVar; ///< round-trip time variation
- MillisecondsDouble m_rto; ///< retransmission timeout
-
-private:
- MillisecondsDouble m_rttMin;
- MillisecondsDouble m_rttMax;
- MillisecondsDouble m_rttAvg;
+ time::nanoseconds m_sRtt; ///< smoothed round-trip time
+ time::nanoseconds m_rttVar; ///< round-trip time variation
+ time::nanoseconds m_rto; ///< retransmission timeout
+ time::nanoseconds m_rttMin;
+ time::nanoseconds m_rttMax;
+ time::nanoseconds m_rttAvg;
int64_t m_nRttSamples;
};
diff --git a/tests/unit/util/rtt-estimator.t.cpp b/tests/unit/util/rtt-estimator.t.cpp
index 59041e4..10c2c74 100644
--- a/tests/unit/util/rtt-estimator.t.cpp
+++ b/tests/unit/util/rtt-estimator.t.cpp
@@ -34,117 +34,110 @@
BOOST_AUTO_TEST_SUITE(Util)
BOOST_AUTO_TEST_SUITE(TestRttEstimator)
-using Millis = RttEstimator::MillisecondsDouble;
-
BOOST_AUTO_TEST_CASE(MinAvgMaxRtt)
{
RttEstimator rttEstimator;
// check initial values
- BOOST_CHECK_CLOSE(rttEstimator.getMinRtt().count(), std::numeric_limits<double>::max(), 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.getAvgRtt().count(), 0.0, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.getMaxRtt().count(), std::numeric_limits<double>::min(), 0.001);
+ BOOST_CHECK_EQUAL(rttEstimator.getMinRtt().count(), std::numeric_limits<time::nanoseconds::rep>::max());
+ BOOST_CHECK_EQUAL(rttEstimator.getAvgRtt().count(), 0);
+ BOOST_CHECK_EQUAL(rttEstimator.getMaxRtt().count(), std::numeric_limits<time::nanoseconds::rep>::min());
// start with three samples
- rttEstimator.addMeasurement(Millis(100), 1);
- rttEstimator.addMeasurement(Millis(400), 1);
- rttEstimator.addMeasurement(Millis(250), 1);
+ rttEstimator.addMeasurement(100_ms, 1);
+ rttEstimator.addMeasurement(400_ms, 1);
+ rttEstimator.addMeasurement(250_ms, 1);
- BOOST_CHECK_CLOSE(rttEstimator.getMinRtt().count(), 100.0, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.getAvgRtt().count(), 250.0, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.getMaxRtt().count(), 400.0, 0.001);
+ BOOST_CHECK_EQUAL(rttEstimator.getMinRtt(), 100_ms);
+ BOOST_CHECK_EQUAL(rttEstimator.getAvgRtt(), 250_ms);
+ BOOST_CHECK_EQUAL(rttEstimator.getMaxRtt(), 400_ms);
// add another sample (new minimum)
- rttEstimator.addMeasurement(Millis(50), 2);
- BOOST_CHECK_CLOSE(rttEstimator.getMinRtt().count(), 50.0, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.getAvgRtt().count(), 200.0, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.getMaxRtt().count(), 400.0, 0.001);
+ rttEstimator.addMeasurement(50_ms, 2);
+ BOOST_CHECK_EQUAL(rttEstimator.getMinRtt(), 50_ms);
+ BOOST_CHECK_EQUAL(rttEstimator.getAvgRtt(), 200_ms);
+ BOOST_CHECK_EQUAL(rttEstimator.getMaxRtt(), 400_ms);
// add another sample (new maximum)
- rttEstimator.addMeasurement(Millis(700), 1);
- BOOST_CHECK_CLOSE(rttEstimator.getMinRtt().count(), 50.0, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.getAvgRtt().count(), 300.0, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.getMaxRtt().count(), 700.0, 0.001);
+ rttEstimator.addMeasurement(700_ms, 1);
+ BOOST_CHECK_EQUAL(rttEstimator.getMinRtt(), 50_ms);
+ BOOST_CHECK_EQUAL(rttEstimator.getAvgRtt(), 300_ms);
+ BOOST_CHECK_EQUAL(rttEstimator.getMaxRtt(), 700_ms);
}
BOOST_AUTO_TEST_CASE(EstimatedRto)
{
RttEstimator::Options opts;
- opts.initialRto = Millis(1000);
- opts.maxRto = Millis(4000);
+ opts.initialRto = 400_ms;
+ opts.maxRto = 2_s;
RttEstimator rttEstimator(opts);
// check initial values
- BOOST_CHECK(std::isnan(rttEstimator.m_sRtt.count()));
- BOOST_CHECK(std::isnan(rttEstimator.m_rttVar.count()));
- BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 1000.0, 0.001);
+ BOOST_CHECK_EQUAL(rttEstimator.getSmoothedRtt(), 0_ns);
+ BOOST_CHECK_EQUAL(rttEstimator.getRttVariation(), 0_ns);
+ BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), opts.initialRto);
// first measurement
- rttEstimator.addMeasurement(Millis(100), 1);
+ rttEstimator.addMeasurement(200_ms, 1);
- BOOST_CHECK_CLOSE(rttEstimator.m_sRtt.count(), 100.0, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.m_rttVar.count(), 50.0, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 300.0, 0.001);
+ BOOST_CHECK_EQUAL(rttEstimator.getSmoothedRtt(), 200_ms);
+ BOOST_CHECK_EQUAL(rttEstimator.getRttVariation(), 100_ms);
+ BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), 600_ms);
- rttEstimator.m_sRtt = Millis(500);
- rttEstimator.m_rttVar = Millis(100);
- rttEstimator.m_rto = Millis(900);
+ rttEstimator.addMeasurement(100_ms, 1);
- rttEstimator.addMeasurement(Millis(100), 1);
-
- BOOST_CHECK_CLOSE(rttEstimator.getSmoothedRtt().count(), 450.0, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.m_rttVar.count(), 175.0, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 1150.0, 0.001);
+ BOOST_CHECK_EQUAL(rttEstimator.getSmoothedRtt(), 187500_us);
+ BOOST_CHECK_EQUAL(rttEstimator.getRttVariation(), 100000_us);
+ BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), 587500_us);
// expected samples larger than 1
- rttEstimator.addMeasurement(Millis(100), 5);
+ rttEstimator.addMeasurement(50_ms, 5);
- BOOST_CHECK_CLOSE(rttEstimator.getSmoothedRtt().count(), 441.25, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.m_rttVar.count(), 183.75, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 1176.25, 0.001);
-
- rttEstimator.m_sRtt = Millis(100.0);
- rttEstimator.m_rttVar = Millis(30.0);
- rttEstimator.m_rto = Millis(220.0);
+ BOOST_CHECK_EQUAL(rttEstimator.getSmoothedRtt(), 184062500_ns);
+ BOOST_CHECK_EQUAL(rttEstimator.getRttVariation(), 101875000_ns);
+ BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), 591562500_ns);
// check if minRto works
- rttEstimator.addMeasurement(Millis(100), 1);
+ for (int i = 0; i < 20; i++) {
+ rttEstimator.addMeasurement(10_ms, 1);
+ }
- BOOST_CHECK_CLOSE(rttEstimator.getSmoothedRtt().count(), 100.0, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.m_rttVar.count(), 22.5, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 200.0, 0.001);
-
- rttEstimator.m_sRtt = Millis(2000);
- rttEstimator.m_rttVar = Millis(400);
- rttEstimator.m_rto = Millis(3600);
+ BOOST_CHECK_EQUAL(rttEstimator.getSmoothedRtt(), 22046646_ns);
+ BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), opts.minRto);
// check if maxRto works
- rttEstimator.addMeasurement(Millis(100), 1);
+ for (int i = 0; i < 10; i++) {
+ rttEstimator.addMeasurement(1_s, 1);
+ rttEstimator.addMeasurement(10_ms, 1);
+ }
- BOOST_CHECK_CLOSE(rttEstimator.getSmoothedRtt().count(), 1762.5, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.m_rttVar.count(), 775.0, 0.001);
- BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 4000.0, 0.001);
+ BOOST_CHECK_EQUAL(rttEstimator.getSmoothedRtt(), 440859284_ns);
+ BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), opts.maxRto);
}
BOOST_AUTO_TEST_CASE(BackoffRto)
{
RttEstimator::Options opts;
- opts.initialRto = Millis(500);
- opts.maxRto = Millis(4000);
+ opts.initialRto = 500_ms;
+ opts.maxRto = 4_s;
RttEstimator rttEstimator(opts);
rttEstimator.backoffRto();
- BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 1000.0, 0.001);
+ BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), 1_s);
// check if minRto works
- rttEstimator.m_rto = Millis(10);
+ for (int i = 0; i < 10; i++) {
+ rttEstimator.addMeasurement(5_ms, 1);
+ }
rttEstimator.backoffRto();
- BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 200.0, 0.001);
+ BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), 400_ms);
// check if maxRto works
- rttEstimator.m_rto = Millis(3000);
+ for (int i = 0; i < 10; i++) {
+ rttEstimator.addMeasurement(5_s, 1);
+ }
rttEstimator.backoffRto();
- BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 4000.0, 0.001);
+ BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), 4_s);
}
BOOST_AUTO_TEST_CASE(AfterMeasurement)
@@ -154,24 +147,24 @@
int nHandlerInvocations = 0;
rttEstimator.afterMeasurement.connectSingleShot([&nHandlerInvocations] (const auto& sample) {
++nHandlerInvocations;
- BOOST_CHECK_CLOSE(sample.rtt.count(), 80.0, 0.001);
- BOOST_CHECK_CLOSE(sample.sRtt.count(), 80.0, 0.001);
- BOOST_CHECK_CLOSE(sample.rttVar.count(), 40.0, 0.001);
- BOOST_CHECK_CLOSE(sample.rto.count(), 240.0, 0.001);
+ BOOST_CHECK_EQUAL(sample.rtt, 80_ms);
+ BOOST_CHECK_EQUAL(sample.sRtt, 80_ms);
+ BOOST_CHECK_EQUAL(sample.rttVar, 40_ms);
+ BOOST_CHECK_EQUAL(sample.rto, 240_ms);
BOOST_CHECK(!sample.segNum.has_value());
});
- rttEstimator.addMeasurement(Millis(80), 1);
+ rttEstimator.addMeasurement(80_ms, 1);
BOOST_CHECK_EQUAL(nHandlerInvocations, 1);
rttEstimator.afterMeasurement.connectSingleShot([&nHandlerInvocations] (const auto& sample) {
++nHandlerInvocations;
- BOOST_CHECK_CLOSE(sample.rtt.count(), 40.0, 0.001);
- BOOST_CHECK_CLOSE(sample.sRtt.count(), 75.0, 0.001);
- BOOST_CHECK_CLOSE(sample.rttVar.count(), 40.0, 0.001);
- BOOST_CHECK_CLOSE(sample.rto.count(), 235.0, 0.001);
+ BOOST_CHECK_EQUAL(sample.rtt, 40_ms);
+ BOOST_CHECK_EQUAL(sample.sRtt, 75_ms);
+ BOOST_CHECK_EQUAL(sample.rttVar, 40_ms);
+ BOOST_CHECK_EQUAL(sample.rto, 235_ms);
BOOST_CHECK(sample.segNum == 42U);
});
- rttEstimator.addMeasurement(Millis(40), 1, 42);
+ rttEstimator.addMeasurement(40_ms, 1, 42);
BOOST_CHECK_EQUAL(nHandlerInvocations, 2);
}