blob: 2438493cc0bfd05b65f0de0a6dcf59b05ef49c59 [file] [log] [blame]
// Copyright (c) 2013 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 "net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h"
#include <algorithm>
#include "base/basictypes.h"
#include "base/logging.h"
#include "net/quic/congestion_control/cube_root.h"
#include "net/quic/quic_protocol.h"
namespace {
// The following constants are in 2^10 fractions of a second instead of ms to
// allow a 10 shift right to divide.
const int kCubeScale = 40; // 1024*1024^3 (first 1024 is from 0.100^3)
// where 0.100 is 100 ms which is the scaling
// round trip time.
// TODO(pwestin): Tuning parameter, currently close to TCP cubic at 100ms RTT.
const int kPacedCubeScale = 6000;
const uint64 kCubeFactor = (GG_UINT64_C(1) << kCubeScale) / kPacedCubeScale;
} // namespace
namespace net {
InterArrivalBitrateRampUp::InterArrivalBitrateRampUp(const QuicClock* clock)
: clock_(clock),
current_rate_(QuicBandwidth::Zero()),
channel_estimate_(QuicBandwidth::Zero()),
available_channel_estimate_(QuicBandwidth::Zero()),
halfway_point_(QuicBandwidth::Zero()),
epoch_(QuicTime::Zero()),
last_update_time_(QuicTime::Zero()) {
}
void InterArrivalBitrateRampUp::Reset(QuicBandwidth new_rate,
QuicBandwidth available_channel_estimate,
QuicBandwidth channel_estimate) {
epoch_ = clock_->ApproximateNow();
last_update_time_ = epoch_;
available_channel_estimate_ = std::max(new_rate, available_channel_estimate);
channel_estimate_ = std::max(channel_estimate, available_channel_estimate_);
halfway_point_ = available_channel_estimate_.Add(
(channel_estimate_.Subtract(available_channel_estimate_)).Scale(0.5f));
if (new_rate < available_channel_estimate_) {
time_to_origin_point_ = CalcuateTimeToOriginPoint(
available_channel_estimate_.Subtract(new_rate));
} else if (new_rate >= channel_estimate_) {
time_to_origin_point_ = 0;
} else if (new_rate >= halfway_point_) {
time_to_origin_point_ =
CalcuateTimeToOriginPoint(channel_estimate_.Subtract(new_rate));
} else {
time_to_origin_point_ = CalcuateTimeToOriginPoint(
new_rate.Subtract(available_channel_estimate_));
}
current_rate_ = new_rate;
DVLOG(1) << "Reset; time to origin point:" << time_to_origin_point_;
}
void InterArrivalBitrateRampUp::UpdateChannelEstimate(
QuicBandwidth channel_estimate) {
if (available_channel_estimate_ > channel_estimate ||
current_rate_ > channel_estimate ||
channel_estimate_ == channel_estimate) {
// Ignore, because one of the following reasons:
// 1) channel estimate is bellow our current available estimate which we
// value higher that this estimate.
// 2) channel estimate is bellow our current send rate.
// 3) channel estimate has not changed.
return;
}
if (available_channel_estimate_ == halfway_point_ &&
channel_estimate_ == halfway_point_) {
// First time we get a usable channel estimate.
channel_estimate_ = channel_estimate;
halfway_point_ = available_channel_estimate_.Add(
(channel_estimate_.Subtract(available_channel_estimate_).Scale(0.5f)));
DVLOG(1) << "UpdateChannelEstimate; first usable value:"
<< channel_estimate.ToKBitsPerSecond() << " Kbits/s";
return;
}
if (current_rate_ < halfway_point_) {
// Update channel estimate without recalculating if we are bellow the
// halfway point.
channel_estimate_ = channel_estimate;
return;
}
// We are between halfway point and our channel_estimate.
epoch_ = clock_->ApproximateNow();
last_update_time_ = epoch_;
channel_estimate_ = channel_estimate;
time_to_origin_point_ =
CalcuateTimeToOriginPoint(channel_estimate_.Subtract(current_rate_));
DVLOG(1) << "UpdateChannelEstimate; time to origin point:"
<< time_to_origin_point_;
}
QuicBandwidth InterArrivalBitrateRampUp::GetNewBitrate(
QuicBandwidth sent_bitrate) {
DCHECK(epoch_.IsInitialized());
QuicTime current_time = clock_->ApproximateNow();
// Cubic is "independent" of RTT, the update is limited by the time elapsed.
if (current_time.Subtract(last_update_time_) <= MaxCubicTimeInterval()) {
return current_rate_;
}
QuicTime::Delta time_from_last_update =
current_time.Subtract(last_update_time_);
last_update_time_ = current_time;
if (!sent_bitrate.IsZero() &&
sent_bitrate.Add(sent_bitrate) < current_rate_) {
// Don't go up in bitrate when we are not sending.
// We need to update the epoch to reflect this state.
epoch_ = epoch_.Add(time_from_last_update);
DVLOG(1) << "Don't increase; our sent bitrate is:"
<< sent_bitrate.ToKBitsPerSecond() << " Kbits/s"
<< " current target rate is:"
<< current_rate_.ToKBitsPerSecond() << " Kbits/s";
return current_rate_;
}
QuicTime::Delta time_from_epoch = current_time.Subtract(epoch_);
// Change the time unit from microseconds to 2^10 fractions per second. This
// is done to allow us to use shift as a divide operator.
int64 elapsed_time = (time_from_epoch.ToMicroseconds() << 10) /
kNumMicrosPerSecond;
int64 offset = time_to_origin_point_ - elapsed_time;
// Note: using int64 since QuicBandwidth can't be negative
int64 delta_pace_kbps = (kPacedCubeScale * offset * offset * offset) >>
kCubeScale;
bool start_bellow_halfway_point = false;
if (current_rate_ < halfway_point_) {
start_bellow_halfway_point = true;
// available_channel_estimate_ is the orgin of the cubic function.
QuicBandwidth current_rate = QuicBandwidth::FromBytesPerSecond(
available_channel_estimate_.ToBytesPerSecond() -
(delta_pace_kbps << 10));
if (start_bellow_halfway_point && current_rate >= halfway_point_) {
// We passed the halfway point, recalculate with new orgin.
epoch_ = clock_->ApproximateNow();
// channel_estimate_ is the new orgin of the cubic function.
if (current_rate >= channel_estimate_) {
time_to_origin_point_ = 0;
} else {
time_to_origin_point_ =
CalcuateTimeToOriginPoint(channel_estimate_.Subtract(current_rate));
}
DVLOG(1) << "Passed the halfway point; time to origin point:"
<< time_to_origin_point_;
}
current_rate_ = current_rate;
} else {
// channel_estimate_ is the orgin of the cubic function.
current_rate_ = QuicBandwidth::FromBytesPerSecond(
channel_estimate_.ToBytesPerSecond() - (delta_pace_kbps << 10));
}
return current_rate_;
}
uint32 InterArrivalBitrateRampUp::CalcuateTimeToOriginPoint(
QuicBandwidth rate_difference) const {
return CubeRoot::Root(kCubeFactor * rate_difference.ToKBytesPerSecond());
}
} // namespace net