wip change ledbat and start cubic
This commit is contained in:
parent
475a99054f
commit
f460d7b4a5
@ -25,6 +25,8 @@ add_library(solanaceae_ngcft1
|
|||||||
./solanaceae/ngc_ft1/cca.hpp
|
./solanaceae/ngc_ft1/cca.hpp
|
||||||
./solanaceae/ngc_ft1/ledbat.hpp
|
./solanaceae/ngc_ft1/ledbat.hpp
|
||||||
./solanaceae/ngc_ft1/ledbat.cpp
|
./solanaceae/ngc_ft1/ledbat.cpp
|
||||||
|
./solanaceae/ngc_ft1/cubic.hpp
|
||||||
|
./solanaceae/ngc_ft1/cubic.cpp
|
||||||
|
|
||||||
./solanaceae/ngc_ft1/rcv_buf.hpp
|
./solanaceae/ngc_ft1/rcv_buf.hpp
|
||||||
./solanaceae/ngc_ft1/rcv_buf.cpp
|
./solanaceae/ngc_ft1/rcv_buf.cpp
|
||||||
|
@ -4,6 +4,16 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
|
// TODO: refactor, more state tracking in ccai and seperate into flow and congestion algos
|
||||||
|
inline bool isSkipSeqID(const std::pair<uint8_t, uint16_t>& a, const std::pair<uint8_t, uint16_t>& b) {
|
||||||
|
// this is not perfect, would need more ft id based history
|
||||||
|
if (a.first != b.first) {
|
||||||
|
return false; // we dont know
|
||||||
|
} else {
|
||||||
|
return a.second+1 != b.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct CCAI {
|
struct CCAI {
|
||||||
public: // config
|
public: // config
|
||||||
using SeqIDType = std::pair<uint8_t, uint16_t>; // tf_id, seq_id
|
using SeqIDType = std::pair<uint8_t, uint16_t>; // tf_id, seq_id
|
||||||
|
49
solanaceae/ngc_ft1/cubic.cpp
Normal file
49
solanaceae/ngc_ft1/cubic.cpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#include "./cubic.hpp"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
float CUBIC::getCWnD(void) const {
|
||||||
|
const double K = cbrt(
|
||||||
|
(_window_max * (1. - BETA)) / SCALING_CONSTANT
|
||||||
|
);
|
||||||
|
|
||||||
|
const double TK = _time_since_reduction - K;
|
||||||
|
|
||||||
|
const double cwnd =
|
||||||
|
SCALING_CONSTANT
|
||||||
|
* TK * TK * TK // TK^3
|
||||||
|
+ _window_max
|
||||||
|
;
|
||||||
|
|
||||||
|
std::cout << "K:" << K << " TK:" << TK << " cwnd:" << cwnd << " rtt:" << getCurrentDelay() << "\n";
|
||||||
|
|
||||||
|
return cwnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
float CUBIC::getCurrentDelay(void) const {
|
||||||
|
return _rtt_ema;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CUBIC::addRTT(float new_delay) {
|
||||||
|
// lerp(new_delay, rtt_ema, 0.1)
|
||||||
|
_rtt_ema = RTT_EMA_ALPHA * new_delay + (1.f - RTT_EMA_ALPHA) * _rtt_ema;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t CUBIC::canSend(void) const {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<CUBIC::SeqIDType> CUBIC::getTimeouts(void) const {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void CUBIC::onSent(SeqIDType seq, size_t data_size) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void CUBIC::onAck(std::vector<SeqIDType> seqs) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void CUBIC::onLoss(SeqIDType seq, bool discard) {
|
||||||
|
}
|
||||||
|
|
64
solanaceae/ngc_ft1/cubic.hpp
Normal file
64
solanaceae/ngc_ft1/cubic.hpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "./cca.hpp"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
struct CUBIC : public CCAI {
|
||||||
|
using clock = std::chrono::steady_clock;
|
||||||
|
|
||||||
|
public: // config
|
||||||
|
static constexpr float BETA {0.7f};
|
||||||
|
static constexpr float SCALING_CONSTANT {0.4f};
|
||||||
|
static constexpr float RTT_EMA_ALPHA = 0.1f; // 0.1 is very smooth, might need more
|
||||||
|
|
||||||
|
private:
|
||||||
|
// window size before last reduciton
|
||||||
|
double _window_max {2.f * MAXIMUM_SEGMENT_SIZE}; // start with mss*2
|
||||||
|
double _window_last_max {2.f * MAXIMUM_SEGMENT_SIZE};
|
||||||
|
double _time_since_reduction {0.};
|
||||||
|
|
||||||
|
// initialize to low value, will get corrected very fast
|
||||||
|
float _fwnd {0.01f * max_byterate_allowed}; // in bytes
|
||||||
|
|
||||||
|
// rtt exponental moving average
|
||||||
|
float _rtt_ema {0.5f};
|
||||||
|
|
||||||
|
clock::time_point _time_start_offset;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float getCWnD(void) const;
|
||||||
|
|
||||||
|
// make values relative to algo start for readability (and precision)
|
||||||
|
// get timestamp in seconds
|
||||||
|
float getTimeNow(void) const {
|
||||||
|
return std::chrono::duration<float>{clock::now() - _time_start_offset}.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
// moving avg over the last few delay samples
|
||||||
|
// VERY sensitive to bundling acks
|
||||||
|
float getCurrentDelay(void) const;
|
||||||
|
|
||||||
|
void addRTT(float new_delay);
|
||||||
|
|
||||||
|
public: // api
|
||||||
|
CUBIC(size_t maximum_segment_data_size) : CCAI(maximum_segment_data_size) {}
|
||||||
|
|
||||||
|
// TODO: api for how much data we should send
|
||||||
|
// take time since last sent into account
|
||||||
|
// respect max_byterate_allowed
|
||||||
|
size_t canSend(void) const override;
|
||||||
|
|
||||||
|
// get the list of timed out seq_ids
|
||||||
|
std::vector<SeqIDType> getTimeouts(void) const override;
|
||||||
|
|
||||||
|
public: // callbacks
|
||||||
|
// data size is without overhead
|
||||||
|
void onSent(SeqIDType seq, size_t data_size) override;
|
||||||
|
|
||||||
|
void onAck(std::vector<SeqIDType> seqs) override;
|
||||||
|
|
||||||
|
// if discard, not resent, not inflight
|
||||||
|
void onLoss(SeqIDType seq, bool discard) override;
|
||||||
|
};
|
||||||
|
|
@ -1,4 +1,5 @@
|
|||||||
#include "./ledbat.hpp"
|
#include "./ledbat.hpp"
|
||||||
|
#include "solanaceae/ngc_ft1/cca.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@ -68,6 +69,11 @@ void LEDBAT::onSent(SeqIDType seq, size_t data_size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LEDBAT::onAck(std::vector<SeqIDType> seqs) {
|
void LEDBAT::onAck(std::vector<SeqIDType> seqs) {
|
||||||
|
if (seqs.empty()) {
|
||||||
|
assert(false && "got empty list of acks???");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// only take the smallest value
|
// only take the smallest value
|
||||||
float most_recent {-std::numeric_limits<float>::infinity()};
|
float most_recent {-std::numeric_limits<float>::infinity()};
|
||||||
|
|
||||||
@ -75,6 +81,23 @@ void LEDBAT::onAck(std::vector<SeqIDType> seqs) {
|
|||||||
|
|
||||||
const auto now {getTimeNow()};
|
const auto now {getTimeNow()};
|
||||||
|
|
||||||
|
{ // skip in ack is congestion event
|
||||||
|
// 1. look at primary ack of packet
|
||||||
|
auto it = std::find_if(_in_flight.begin(), _in_flight.end(), [seq = seqs.front()](const auto& v) -> bool {
|
||||||
|
return std::get<0>(v) == seq;
|
||||||
|
});
|
||||||
|
if (it != _in_flight.end()) {
|
||||||
|
if (isSkipSeqID(_last_ack_got, std::get<0>(*it))) {
|
||||||
|
if (getTimeNow() >= _last_congestion_event + _last_congestion_rtt) {
|
||||||
|
_recently_lost_data = true;
|
||||||
|
_last_congestion_event = getTimeNow();
|
||||||
|
_last_congestion_rtt = getCurrentDelay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: only if newer, triggers double event otherwise (without a timer)
|
||||||
|
_last_ack_got = std::get<0>(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
for (const auto& seq : seqs) {
|
for (const auto& seq : seqs) {
|
||||||
auto it = std::find_if(_in_flight.begin(), _in_flight.end(), [seq](const auto& v) -> bool {
|
auto it = std::find_if(_in_flight.begin(), _in_flight.end(), [seq](const auto& v) -> bool {
|
||||||
return std::get<0>(v) == seq;
|
return std::get<0>(v) == seq;
|
||||||
@ -124,15 +147,17 @@ void LEDBAT::onLoss(SeqIDType seq, bool discard) {
|
|||||||
}
|
}
|
||||||
// TODO: reset timestamp?
|
// TODO: reset timestamp?
|
||||||
|
|
||||||
|
#if 0 // temporarily disable ce for timeout
|
||||||
// at most once per rtt?
|
// at most once per rtt?
|
||||||
// TODO: use delay at event instead
|
// TODO: use delay at event instead
|
||||||
if (getTimeNow() >= _last_congestion_event + _last_congestion_rtt) {
|
if (getTimeNow() >= _last_congestion_event + _last_congestion_rtt) {
|
||||||
_recently_lost_data = true;
|
_recently_lost_data = true;
|
||||||
_last_congestion_event = getTimeNow();
|
_last_congestion_event = getTimeNow();
|
||||||
_last_congestion_rtt = getCurrentDelay();
|
_last_congestion_rtt = getCurrentDelay();
|
||||||
|
|
||||||
updateWindows();
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
updateWindows();
|
||||||
}
|
}
|
||||||
|
|
||||||
float LEDBAT::getCurrentDelay(void) const {
|
float LEDBAT::getCurrentDelay(void) const {
|
||||||
@ -205,7 +230,7 @@ void LEDBAT::updateWindows(void) {
|
|||||||
|
|
||||||
if (_recently_lost_data) {
|
if (_recently_lost_data) {
|
||||||
_cwnd = std::clamp(
|
_cwnd = std::clamp(
|
||||||
_cwnd / 2.f,
|
_cwnd * 0.7f,
|
||||||
//_cwnd / 1.6f,
|
//_cwnd / 1.6f,
|
||||||
2.f * MAXIMUM_SEGMENT_SIZE,
|
2.f * MAXIMUM_SEGMENT_SIZE,
|
||||||
_cwnd
|
_cwnd
|
||||||
@ -213,7 +238,7 @@ void LEDBAT::updateWindows(void) {
|
|||||||
} else {
|
} else {
|
||||||
// LEDBAT++ (the Rethinking the LEDBAT Protocol paper)
|
// LEDBAT++ (the Rethinking the LEDBAT Protocol paper)
|
||||||
// "Multiplicative decrease"
|
// "Multiplicative decrease"
|
||||||
const float constant {2.f}; // spec recs 1
|
const float constant {1.f}; // spec recs 1
|
||||||
if (queuing_delay < target_delay) {
|
if (queuing_delay < target_delay) {
|
||||||
_cwnd = std::min(
|
_cwnd = std::min(
|
||||||
_cwnd + gain,
|
_cwnd + gain,
|
||||||
|
@ -123,6 +123,8 @@ struct LEDBAT : public CCAI{
|
|||||||
|
|
||||||
int64_t _in_flight_bytes {0};
|
int64_t _in_flight_bytes {0};
|
||||||
|
|
||||||
|
SeqIDType _last_ack_got {0xff, 0xffff}; // some default
|
||||||
|
|
||||||
private: // helper
|
private: // helper
|
||||||
clock::time_point _time_start_offset;
|
clock::time_point _time_start_offset;
|
||||||
};
|
};
|
||||||
|
@ -826,6 +826,7 @@ bool SHA1_NGCFT1::onEvent(const Events::NGCFT1_send_data& e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto& transfer = peer.at(e.transfer_id);
|
auto& transfer = peer.at(e.transfer_id);
|
||||||
|
transfer.time_since_activity = 0.f;
|
||||||
if (std::holds_alternative<SendingTransfer::Info>(transfer.v)) {
|
if (std::holds_alternative<SendingTransfer::Info>(transfer.v)) {
|
||||||
auto& info_transfer = std::get<SendingTransfer::Info>(transfer.v);
|
auto& info_transfer = std::get<SendingTransfer::Info>(transfer.v);
|
||||||
for (size_t i = 0; i < e.data_size && (i + e.data_offset) < info_transfer.info_data.size(); i++) {
|
for (size_t i = 0; i < e.data_size && (i + e.data_offset) < info_transfer.info_data.size(); i++) {
|
||||||
@ -859,8 +860,6 @@ bool SHA1_NGCFT1::onEvent(const Events::NGCFT1_send_data& e) {
|
|||||||
assert(false && "not implemented?");
|
assert(false && "not implemented?");
|
||||||
}
|
}
|
||||||
|
|
||||||
transfer.time_since_activity = 0.f;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user