mirror of
https://github.com/overte-org/overte.git
synced 2025-04-25 09:33:52 +02:00
186 lines
6.9 KiB
C++
186 lines
6.9 KiB
C++
//
|
|
// CongestionControl.cpp
|
|
// libraries/networking/src/udt
|
|
//
|
|
// Created by Clement on 7/23/15.
|
|
// Copyright 2015 High Fidelity, Inc.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
//
|
|
|
|
#include "CongestionControl.h"
|
|
#include "Packet.h"
|
|
|
|
using namespace udt;
|
|
using namespace std::chrono;
|
|
|
|
static const double USECS_PER_SECOND = 1000000.0;
|
|
|
|
void DefaultCC::init() {
|
|
_lastRCTime = high_resolution_clock::now();
|
|
|
|
_mss = udt::MAX_PACKET_SIZE;
|
|
|
|
_slowStartLastAck = _sendCurrSeqNum;
|
|
_lastDecreaseMaxSeq = SequenceNumber { SequenceNumber::MAX };
|
|
|
|
_congestionWindowSize = 16.0;
|
|
_packetSendPeriod = 1.0;
|
|
}
|
|
|
|
void DefaultCC::onACK(SequenceNumber ackNum) {
|
|
double increase = 0;
|
|
|
|
// Note: 1/24/2012
|
|
// The minimum increase parameter is increased from "1.0 / _mss" to 0.01
|
|
// because the original was too small and caused sending rate to stay at low level
|
|
// for long time.
|
|
const double minimumIncrease = 0.01;
|
|
|
|
// we will only adjust once per sync interval so check that it has been at least that long now
|
|
auto now = high_resolution_clock::now();
|
|
if (duration_cast<microseconds>(now - _lastRCTime).count() < synInterval()) {
|
|
return;
|
|
}
|
|
|
|
// our last rate increase time is now
|
|
_lastRCTime = now;
|
|
|
|
if (_slowStart) {
|
|
// we are in slow start phase - increase the congestion window size by the number of packets just ACKed
|
|
_congestionWindowSize += seqlen(_slowStartLastAck, ackNum);
|
|
|
|
// update the last ACK
|
|
_slowStartLastAck = ackNum;
|
|
|
|
// check if we can get out of slow start (is our new congestion window size bigger than the max)
|
|
if (_congestionWindowSize > _maxCongestionWindowSize) {
|
|
_slowStart = false;
|
|
|
|
if (_receiveRate > 0) {
|
|
// if we have a valid receive rate we set the send period to whatever the receive rate dictates
|
|
_packetSendPeriod = USECS_PER_SECOND / _receiveRate;
|
|
} else {
|
|
// no valid receive rate, packet send period is dictated by estimated RTT and current congestion window size
|
|
_packetSendPeriod = (_rtt + synInterval()) / _congestionWindowSize;
|
|
}
|
|
}
|
|
} else {
|
|
// not in slow start - window size should be arrival rate * (RTT + SYN) + 16
|
|
_congestionWindowSize = _receiveRate / USECS_PER_SECOND * (_rtt + synInterval()) + 16;
|
|
}
|
|
|
|
// during slow start we perform no rate increases
|
|
if (_slowStart) {
|
|
return;
|
|
}
|
|
|
|
// if loss has happened since the last rate increase we do not perform another increase
|
|
if (_loss) {
|
|
_loss = false;
|
|
return;
|
|
}
|
|
|
|
int capacitySpeedDelta = (int) (_bandwidth - USECS_PER_SECOND / _packetSendPeriod);
|
|
|
|
// UDT uses what they call DAIMD - additive increase multiplicative decrease with decreasing increases
|
|
// This factor is a protocol parameter that is part of the DAIMD algorithim
|
|
static const int AIMD_DECREASING_INCREASE_FACTOR = 9;
|
|
|
|
if ((_packetSendPeriod > _lastDecreasePeriod) && ((_bandwidth / AIMD_DECREASING_INCREASE_FACTOR) < capacitySpeedDelta)) {
|
|
capacitySpeedDelta = _bandwidth / AIMD_DECREASING_INCREASE_FACTOR;
|
|
}
|
|
|
|
if (capacitySpeedDelta <= 0) {
|
|
increase = minimumIncrease;
|
|
} else {
|
|
// use UDTs DAIMD algorithm to figure out what the send period increase factor should be
|
|
|
|
// inc = max(10 ^ ceil(log10(B * MSS * 8 ) * Beta / MSS, minimumIncrease)
|
|
// B = estimated link capacity
|
|
// Beta = 1.5 * 10^(-6)
|
|
|
|
static const double BETA = 0.0000015;
|
|
static const double BITS_PER_BYTE = 8.0;
|
|
|
|
increase = pow(10.0, ceil(log10(capacitySpeedDelta * _mss * BITS_PER_BYTE))) * BETA / _mss;
|
|
|
|
if (increase < minimumIncrease) {
|
|
increase = minimumIncrease;
|
|
}
|
|
}
|
|
|
|
_packetSendPeriod = (_packetSendPeriod * synInterval()) / (_packetSendPeriod * increase + synInterval());
|
|
}
|
|
|
|
void DefaultCC::onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) {
|
|
// stop the slow start if we haven't yet
|
|
if (_slowStart) {
|
|
stopSlowStart();
|
|
}
|
|
|
|
_loss = true;
|
|
|
|
static const double INTER_PACKET_ARRIVAL_INCREASE = 1.125;
|
|
static const int MAX_DECREASES_PER_CONGESTION_EPOCH = 5;
|
|
|
|
// check if this NAK starts a new congestion period - this will be the case if the
|
|
// NAK received occured for a packet sent after the last decrease
|
|
if (rangeStart > _lastDecreaseMaxSeq) {
|
|
_lastDecreasePeriod = _packetSendPeriod;
|
|
|
|
_packetSendPeriod = ceil(_packetSendPeriod * INTER_PACKET_ARRIVAL_INCREASE);
|
|
|
|
// use EWMA to update the average number of NAKs per congestion
|
|
static const double NAK_EWMA_ALPHA = 0.125;
|
|
_avgNAKNum = (int)ceil(_avgNAKNum * (1 - NAK_EWMA_ALPHA) + _nakCount * NAK_EWMA_ALPHA);
|
|
|
|
// update the count of NAKs and count of decreases in this interval
|
|
_nakCount = 1;
|
|
_decreaseCount = 1;
|
|
|
|
_lastDecreaseMaxSeq = _sendCurrSeqNum;
|
|
|
|
// avoid synchronous rate decrease across connections using randomization
|
|
srand((unsigned) _lastDecreaseMaxSeq);
|
|
_randomDecreaseThreshold = (int) ceil(_avgNAKNum * (double(rand()) / RAND_MAX));
|
|
|
|
if (_randomDecreaseThreshold < 1) {
|
|
_randomDecreaseThreshold = 1;
|
|
}
|
|
|
|
} else if ((_decreaseCount++ < MAX_DECREASES_PER_CONGESTION_EPOCH) && ((++_nakCount % _randomDecreaseThreshold) == 0)) {
|
|
// there have been fewer than MAX_DECREASES_PER_CONGESTION_EPOCH AND this NAK matches the random count at which we
|
|
// decided we would decrease the packet send period
|
|
|
|
_packetSendPeriod = ceil(_packetSendPeriod * INTER_PACKET_ARRIVAL_INCREASE);
|
|
_lastDecreaseMaxSeq = _sendCurrSeqNum;
|
|
}
|
|
}
|
|
|
|
void DefaultCC::onTimeout() {
|
|
if (_slowStart) {
|
|
stopSlowStart();
|
|
} else {
|
|
// UDT used to do the following on timeout if not in slow start - we should check if it could be helpful
|
|
// _lastDecreasePeriod = _packetSendPeriod;
|
|
// _packetSendPeriod = ceil(_packetSendPeriod * 2);
|
|
|
|
// this seems odd - the last ack they were setting _lastDecreaseMaxSeq to only applies to slow start
|
|
// _lastDecreaseMaxSeq = _slowStartLastAck;
|
|
}
|
|
}
|
|
|
|
void DefaultCC::stopSlowStart() {
|
|
_slowStart = false;
|
|
if (_receiveRate > 0) {
|
|
// Set the sending rate to the receiving rate.
|
|
_packetSendPeriod = USECS_PER_SECOND / _receiveRate;
|
|
return;
|
|
}
|
|
// If no receiving rate is observed, we have to compute the sending
|
|
// rate according to the current window size, and decrease it
|
|
// using the method below.
|
|
_packetSendPeriod = _congestionWindowSize / (_rtt + synInterval());
|
|
}
|