Compare commits

...

1 Commits

Author SHA1 Message Date
fe751b77b3
wip change ledbat and start cubic 2023-08-24 18:04:25 +02:00
7 changed files with 157 additions and 6 deletions

View File

@ -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

View File

@ -3,6 +3,16 @@
#include <vector> #include <vector>
#include <cstdint> #include <cstdint>
// 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

View 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) {
}

View 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;
};

View File

@ -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();
}
#endif
updateWindows(); 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,

View File

@ -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;
}; };

View File

@ -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;
} }