blob: da2be78b857b1418ab21eabe8f5e8d2e38cbd4b9 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdlib.h>
#include "net/curvecp/rtt_and_send_rate_calculator.h"
namespace net {
RttAndSendRateCalculator::RttAndSendRateCalculator()
: rtt_latest_(0),
rtt_average_(0),
rtt_deviation_(0),
rtt_lowwater_(0),
rtt_highwater_(0),
rtt_timeout_(base::Time::kMicrosecondsPerSecond),
send_rate_(1000000),
rtt_seenrecenthigh_(0),
rtt_seenrecentlow_(0),
rtt_seenolderhigh_(0),
rtt_seenolderlow_(0),
rtt_phase_(false) {
}
void RttAndSendRateCalculator::OnMessage(int32 rtt_us) {
UpdateRTT(rtt_us);
AdjustSendRate();
}
void RttAndSendRateCalculator::OnTimeout() {
base::TimeDelta time_since_last_loss = last_sample_time_ - last_loss_time_;
if (time_since_last_loss.InMilliseconds() < 4 * rtt_timeout_)
return;
send_rate_ *= 2;
last_loss_time_ = last_sample_time_;
last_edge_time_ = last_sample_time_;
}
// Updates RTT
void RttAndSendRateCalculator::UpdateRTT(int32 rtt_us) {
rtt_latest_ = rtt_us;
last_sample_time_ = base::TimeTicks::Now();
// Initialize for the first sample.
if (!rtt_average_) {
rtt_average_ = rtt_latest_;
rtt_deviation_ = rtt_latest_;
rtt_highwater_ = rtt_latest_;
rtt_lowwater_ = rtt_latest_;
}
// Jacobson's retransmission timeout calculation.
int32 rtt_delta = rtt_latest_ - rtt_average_;
rtt_average_ += rtt_delta / 8;
if (rtt_delta < 0)
rtt_delta = -rtt_delta;
rtt_delta -= rtt_deviation_;
rtt_deviation_ += rtt_delta / 4;
rtt_timeout_ = rtt_average_ + (4 * rtt_deviation_);
// Adjust for delayed acks with anti-spiking.
// TODO(mbelshe): this seems high.
rtt_timeout_ += 8 * send_rate_;
// Recognize the top and bottom of the congestion cycle.
rtt_delta = rtt_latest_ - rtt_highwater_;
rtt_highwater_ += rtt_delta / 1024;
rtt_delta = rtt_latest_ - rtt_lowwater_;
if (rtt_delta > 0)
rtt_lowwater_ += rtt_delta / 8192;
else
rtt_lowwater_ += rtt_delta / 256;
}
void RttAndSendRateCalculator::AdjustSendRate() {
last_sample_time_ = base::TimeTicks::Now();
base::TimeDelta time = last_sample_time_ - last_send_adjust_time_;
// We only adjust the send rate approximately every 16 samples.
if (time.InMicroseconds() < 16 * send_rate_)
return;
if (rtt_average_ >
rtt_highwater_ + (5 * base::Time::kMicrosecondsPerMillisecond))
rtt_seenrecenthigh_ = true;
else if (rtt_average_ < rtt_lowwater_)
rtt_seenrecentlow_ = true;
last_send_adjust_time_ = last_sample_time_;
// If too much time has elapsed, re-initialize the send_rate.
if (time.InMicroseconds() > 10 * base::Time::kMicrosecondsPerSecond) {
send_rate_ = base::Time::kMicrosecondsPerSecond; // restart.
send_rate_ += randommod(send_rate_ / 8);
}
// TODO(mbelshe): Why is 128us a lower bound?
if (send_rate_ >= 128) {
// Additive increase: adjust 1/N by a constant c.
// rtt-fair additive increase: adjust 1/N by a constant c every nanosec.
// approximation: adjust 1/N by cN every N nanoseconds.
// TODO(mbelshe): he used c == 2^-51. for nanosecs.
// I use c == 2^31, for microsecs.
// i.e. N <- 1/(1/N + cN) = N/(1 + cN^2) every N nanosec.
if (false && send_rate_ < 16384) {
// N/(1+cN^2) approx N - cN^3
// TODO(mbelshe): note that he is using (cN)^3 here, not what he wrote.
int32 msec = send_rate_ / 128;
send_rate_ -= msec * msec * msec;
} else {
double d = send_rate_;
send_rate_ = d / (1 + d * d / 2147483648.0); // 2^31
}
}
if (rtt_phase_ == false) {
if (rtt_seenolderhigh_) {
rtt_phase_ = true;
last_edge_time_ = last_sample_time_;
send_rate_ += randommod(send_rate_ / 4);
}
} else {
if (rtt_seenolderlow_) {
rtt_phase_ = false;
}
}
rtt_seenolderhigh_ = rtt_seenrecenthigh_;
rtt_seenolderlow_ = rtt_seenrecentlow_;
rtt_seenrecenthigh_ = false;
rtt_seenrecentlow_ = false;
AttemptToDoubleSendRate();
}
void RttAndSendRateCalculator::AttemptToDoubleSendRate() {
base::TimeDelta time_since_edge = last_sample_time_ - last_edge_time_;
base::TimeDelta time_since_doubling =
last_sample_time_ - last_doubling_time_;
int32 threshold = 0;
if (time_since_edge.InMicroseconds() <
base::Time::kMicrosecondsPerSecond * 60) {
threshold = (4 * send_rate_) +
(64 * rtt_timeout_) +
(5 * base::Time::kMicrosecondsPerSecond);
} else {
threshold = (4 * send_rate_) +
(2 * rtt_timeout_);
}
if (time_since_doubling.InMicroseconds() < threshold)
return;
if (send_rate_ < 64)
return;
send_rate_ /= 2;
last_doubling_time_ = last_sample_time_;
}
// returns a random number from 0 to val-1.
int32 RttAndSendRateCalculator::randommod(int32 val) {
return rand() % val;
}
} // namespace net