Files
kcp/tests/netstats.cpp
2026-05-15 08:42:56 +08:00

927 lines
23 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//=====================================================================
//
// AbstractStats.cpp -
//
// Last Modified: 2020/04/03 17:47:58
//
//=====================================================================
#include <assert.h>
#include <limits>
#include "netstats.h"
//---------------------------------------------------------------------
// Namespace begin
//---------------------------------------------------------------------
NAMESPACE_BEGIN(System);
//=====================================================================
// unsined integer unwrapper
//=====================================================================
UnsignedWrap::UnsignedWrap()
{
size = 16;
last_value = -1;
}
UnsignedWrap::UnsignedWrap(int size)
{
this->size = 16;
last_value = -1;
reset(size);
}
UnsignedWrap::UnsignedWrap(const UnsignedWrap& src)
{
size = src.size;
last_value = src.last_value;
}
UnsignedWrap& UnsignedWrap::operator = (const UnsignedWrap& src)
{
size = src.size;
last_value = src.last_value;
return *this;
}
void UnsignedWrap::reset(int size)
{
if (size == 16 || size == 2) {
this->size = 16;
}
else if (size == 32 || size == 4) {
this->size = 32;
}
else {
this->size = size;
}
assert(size == 16 || size == 2 || size == 32 || size == 4);
last_value = -1;
}
void UnsignedWrap::set_last(int64_t last)
{
last_value = last;
}
bool UnsignedWrap::u16_is_newer(uint16_t val, uint16_t prev_val)
{
const uint16_t half = ((uint16_t)0x8000);
if (val - prev_val == half)
return (val > prev_val)? true : false;
if (val != prev_val && ((uint16_t)(val - prev_val)) < half)
return true;
return false;
}
bool UnsignedWrap::u32_is_newer(uint32_t val, uint32_t prev_val)
{
const uint32_t half = ((uint32_t)0x80000000);
if (val - prev_val == half)
return (val > prev_val)? true : false;
if (val != prev_val && ((uint32_t)(val - prev_val)) < half)
return true;
return false;
}
int64_t UnsignedWrap::u16_update(uint16_t val)
{
const int64_t max_plus = ((int64_t)0xffff) + 1;
uint16_t cropped_last = (uint16_t)(last_value & 0xffff);
int64_t delta = ((int64_t)val) - ((int64_t)cropped_last);
if (u16_is_newer(val, cropped_last)) {
if (delta < 0) {
delta += max_plus;
}
}
else if (delta > 0 && last_value + delta - max_plus >= 0) {
delta -= max_plus;
}
return last_value + delta;
}
int64_t UnsignedWrap::u32_update(uint32_t val)
{
const int64_t max_plus = ((int64_t)0xffffffff) + 1;
uint32_t cropped_last = (uint32_t)(last_value & 0xffffffff);
int64_t delta = ((int64_t)val) - ((int64_t)cropped_last);
if (u32_is_newer(val, cropped_last)) {
if (delta < 0) {
delta += max_plus;
}
}
else if (delta > 0 && last_value + delta - max_plus >= 0) {
delta -= max_plus;
}
return last_value + delta;
}
int64_t UnsignedWrap::wrap_uint16(uint16_t val)
{
assert(size == 16);
if (last_value < 0) {
last_value = val;
} else {
last_value = u16_update(val);
}
return last_value;
}
int64_t UnsignedWrap::wrap_uint32(uint32_t val)
{
assert(size == 32);
if (last_value < 0) {
last_value = val;
} else {
last_value = u32_update(val);
}
return last_value;
}
//=====================================================================
// 丢包统计
//=====================================================================
//---------------------------------------------------------------------
// ctor
//---------------------------------------------------------------------
AbstractLossStats::AbstractLossStats()
{
init(4000, 200, 100);
}
//---------------------------------------------------------------------
// dtor
//---------------------------------------------------------------------
AbstractLossStats::~AbstractLossStats()
{
}
//---------------------------------------------------------------------
// copy ctor
//---------------------------------------------------------------------
AbstractLossStats::AbstractLossStats(const AbstractLossStats& src):
_loss_window(src._loss_window),
_wrapper(src._wrapper),
_stat_ts(src._stat_ts),
_max_id(src._max_id),
_k_loss_stats_window_ms(src._k_loss_stats_window_ms),
_k_max_stats_window_num(src._k_max_stats_window_num),
_k_calculation_limit(src._k_calculation_limit)
{
}
//---------------------------------------------------------------------
// move ctor
//---------------------------------------------------------------------
AbstractLossStats::AbstractLossStats(AbstractLossStats&& src):
_loss_window(std::move(src._loss_window)),
_wrapper(src._wrapper),
_stat_ts(src._stat_ts),
_max_id(src._max_id),
_k_loss_stats_window_ms(src._k_loss_stats_window_ms),
_k_max_stats_window_num(src._k_max_stats_window_num),
_k_calculation_limit(src._k_calculation_limit)
{
}
//---------------------------------------------------------------------
// copy assignment
//---------------------------------------------------------------------
AbstractLossStats& AbstractLossStats::operator = (const AbstractLossStats& src)
{
_loss_window.clear();
for (auto it: src._loss_window) {
_loss_window[it.first] = it.second;
}
_wrapper = src._wrapper;
_stat_ts = src._stat_ts;
_max_id = src._max_id;
_k_loss_stats_window_ms = src._k_loss_stats_window_ms;
_k_max_stats_window_num = src._k_max_stats_window_num;
_k_calculation_limit = src._k_calculation_limit;
return *this;
}
//---------------------------------------------------------------------
// 初始化统计
//---------------------------------------------------------------------
void AbstractLossStats::init(int window_ms, int window_num, int limit)
{
if (limit < 10) limit = 10;
if (window_num <= limit) window_num = limit + 1;
_k_loss_stats_window_ms = window_ms;
_k_max_stats_window_num = window_num;
_k_calculation_limit = limit;
_stat_ts = -1;
_max_id = -1;
_loss_window.clear();
_wrapper.reset(16);
}
//---------------------------------------------------------------------
// 复位统计
//---------------------------------------------------------------------
void AbstractLossStats::reset()
{
_stat_ts = -1;
_max_id = -1;
_loss_window.clear();
_wrapper.reset(16);
}
//---------------------------------------------------------------------
// 淘汰超过窗口的太老的数据
//---------------------------------------------------------------------
void AbstractLossStats::evict_oldest(int64_t now_ts)
{
while (_loss_window.size() > 0) {
LossWindow::iterator it = _loss_window.begin();
bool drop = false;
if ((int)_loss_window.size() > _k_max_stats_window_num) {
drop = true;
}
else if (it->second + _k_loss_stats_window_ms < now_ts) {
drop = true;
}
if (drop == false) {
break;
}
else {
_loss_window.erase(it);
}
}
}
//---------------------------------------------------------------------
// 收到一个包时调用
//---------------------------------------------------------------------
void AbstractLossStats::update(uint16_t seq, int64_t now_ts)
{
int64_t id = _wrapper.wrap_uint16(seq);
if (_max_id < id) {
_max_id = id;
}
_loss_window[id] = now_ts;
evict_oldest(now_ts);
}
//---------------------------------------------------------------------
// 计算丢包率fraction_loss 是返回的小数丢包率255 代表 100%
//---------------------------------------------------------------------
int AbstractLossStats::calculate(int64_t now_ts, uint8_t *fraction_loss, int *num)
{
*fraction_loss = 0;
*num = 0;
evict_oldest(now_ts);
if (_max_id < 0) {
return -1;
}
if ((int)_loss_window.size() < _k_calculation_limit) {
// printf("not enough: size=%d limit=%d\n", (int)_loss_window.size(), _k_calculation_limit);
return -2;
}
int count = (int)_loss_window.size();
if (count <= 0) {
return -3;
}
auto it = _loss_window.begin();
int64_t oldest = it->first;
int distance = (int32_t)(_max_id - oldest + 1);
if (distance <= 0)
return -4;
if (distance <= count) {
*fraction_loss = 0;
}
else {
*fraction_loss = (distance - count) * 255 / distance;
}
*num = distance;
_stat_ts = now_ts;
return 0;
}
//=====================================================================
// 速率统计
//=====================================================================
//---------------------------------------------------------------------
// ctor
//---------------------------------------------------------------------
AbstractRateStats::AbstractRateStats()
{
_wnd_size = 2000;
_scale = 8000;
init(2000, 8000);
}
//---------------------------------------------------------------------
// dtor
//---------------------------------------------------------------------
AbstractRateStats::~AbstractRateStats()
{
}
//---------------------------------------------------------------------
// copy ctor
//---------------------------------------------------------------------
AbstractRateStats::AbstractRateStats(const AbstractRateStats& src):
_buckets(src._buckets),
_oldest_ts(src._oldest_ts),
_oldest_index(src._oldest_index),
_wnd_size(src._wnd_size),
_scale(src._scale),
_accumulated_count(src._accumulated_count),
_sample_num(src._sample_num)
{
}
//---------------------------------------------------------------------
// move ctor
//---------------------------------------------------------------------
AbstractRateStats::AbstractRateStats(AbstractRateStats&& src):
_buckets(std::move(src._buckets)),
_oldest_ts(src._oldest_ts),
_oldest_index(src._oldest_index),
_wnd_size(src._wnd_size),
_scale(src._scale),
_accumulated_count(src._accumulated_count),
_sample_num(src._sample_num)
{
}
//---------------------------------------------------------------------
// copy assign
//---------------------------------------------------------------------
AbstractRateStats& AbstractRateStats::operator = (const AbstractRateStats& src)
{
_buckets = src._buckets;
_oldest_ts = src._oldest_ts;
_oldest_index = src._oldest_index;
_wnd_size = src._wnd_size;
_scale = src._scale;
_accumulated_count = src._accumulated_count;
_sample_num = src._sample_num;
return *this;
}
//---------------------------------------------------------------------
// initialize
//---------------------------------------------------------------------
void AbstractRateStats::init(int wnd_size, float scale)
{
if (wnd_size < 0) wnd_size = _wnd_size;
if (scale < 0) scale = _scale;
_wnd_size = wnd_size;
_scale = scale;
_buckets.resize(wnd_size);
reset();
}
//---------------------------------------------------------------------
// reset
//---------------------------------------------------------------------
void AbstractRateStats::reset()
{
_buckets.resize(_wnd_size);
_accumulated_count = 0;
_sample_num = 0;
_oldest_index = 0;
_oldest_ts = -1;
for (int i = 0; i < _wnd_size; i++) {
_buckets[i].sum = 0;
_buckets[i].sample = 0;
}
}
//---------------------------------------------------------------------
// 删除过期数据
//---------------------------------------------------------------------
void AbstractRateStats::evict_oldest(int64_t now_ts)
{
if (_oldest_ts < 0)
return;
int64_t new_oldest_ts = now_ts - _wnd_size + 1;
if (new_oldest_ts < _oldest_ts)
return;
while (_sample_num > 0 && _oldest_ts < new_oldest_ts) {
RateBucket& bucket = _buckets[_oldest_index];
_sample_num -= bucket.sample;
_accumulated_count -= bucket.sum;
bucket.sum = 0;
bucket.sample = 0;
if (++_oldest_index >= _wnd_size) {
_oldest_index = 0;
}
_oldest_ts++;
}
_oldest_ts = new_oldest_ts;
}
//---------------------------------------------------------------------
// 收到一个包时调用
//---------------------------------------------------------------------
void AbstractRateStats::update(size_t count, int64_t now_ts)
{
if (_oldest_ts > now_ts)
return;
evict_oldest(now_ts);
if (_oldest_ts < 0) {
_oldest_ts = now_ts;
}
int offset = (int)(now_ts - _oldest_ts);
int index = (_oldest_index + offset) % _wnd_size;
_sample_num++;
_buckets[index].sum += (int)count;
_buckets[index].sample++;
_accumulated_count += count;
}
//---------------------------------------------------------------------
// 统计码率,数据不够返回 -1成功返回速率
//---------------------------------------------------------------------
int AbstractRateStats::calculate(int64_t now_ts)
{
evict_oldest(now_ts);
int active_wnd_size = (int)((int64_t)(now_ts - _oldest_ts + 1));
if (_sample_num <= 0 || active_wnd_size <= 1 || active_wnd_size < _wnd_size)
return -1;
double rate = ((((double)_accumulated_count) * _scale) / _wnd_size) + 0.5;
return (int)rate;
}
//---------------------------------------------------------------------
// Minimum Sliding Window
//---------------------------------------------------------------------
MinHistory::MinHistory()
{
init(4000, false);
}
//---------------------------------------------------------------------
// dtor
//---------------------------------------------------------------------
MinHistory::~MinHistory()
{
}
//---------------------------------------------------------------------
// copy ctor
//---------------------------------------------------------------------
MinHistory::MinHistory(const MinHistory& src)
{
this->operator=(src);
}
//---------------------------------------------------------------------
// moving ctor
//---------------------------------------------------------------------
MinHistory::MinHistory(MinHistory&& src):
_history(std::move(src._history)),
_wnd_size(src._wnd_size),
_reverse(src._reverse)
{
}
//---------------------------------------------------------------------
// copy assignment
//---------------------------------------------------------------------
MinHistory& MinHistory::operator = (const MinHistory& src)
{
_history.clear();
for (auto &x : src._history) {
_history.push_back(x);
}
_wnd_size = src._wnd_size;
_reverse = src._reverse;
return *this;
}
//---------------------------------------------------------------------
// mode: 0 for minimal, 1 for maximal, win size in millisecs
//---------------------------------------------------------------------
void MinHistory::init(int wnd_size, bool reverse)
{
_wnd_size = wnd_size;
_reverse = reverse;
_history.clear();
}
//---------------------------------------------------------------------
// clear history
//---------------------------------------------------------------------
void MinHistory::clear()
{
_history.clear();
}
//---------------------------------------------------------------------
// update value
//---------------------------------------------------------------------
void MinHistory::update(int value, int64_t now_ts)
{
while (!_history.empty()) {
if (now_ts - _history.front().first + 1 <= _wnd_size) break;
_history.pop_front();
}
while (!_history.empty()) {
if (_reverse == false) {
if (_history.back().second < value) break;
}
else {
if (_history.back().second > value) break;
}
_history.pop_back();
}
_history.push_back(std::make_pair(now_ts, value));
}
//---------------------------------------------------------------------
// ctor
//---------------------------------------------------------------------
MaxHistory::MaxHistory()
{
_min_history.init(4000, true);
}
//---------------------------------------------------------------------
// dtor
//---------------------------------------------------------------------
MaxHistory::~MaxHistory()
{
}
//---------------------------------------------------------------------
// copy ctor
//---------------------------------------------------------------------
MaxHistory::MaxHistory(const MaxHistory& src)
{
_min_history = src._min_history;
}
//---------------------------------------------------------------------
// move ctor
//---------------------------------------------------------------------
MaxHistory::MaxHistory(MaxHistory&& src):
_min_history(std::move(src._min_history))
{
}
//---------------------------------------------------------------------
// copy assignment
//---------------------------------------------------------------------
MaxHistory& MaxHistory::operator=(const MaxHistory &src)
{
_min_history = src._min_history;
return *this;
}
//---------------------------------------------------------------------
// init
//---------------------------------------------------------------------
void MaxHistory::init(int wnd_size)
{
_min_history.init(wnd_size, true);
}
//---------------------------------------------------------------------
// clear
//---------------------------------------------------------------------
void MaxHistory::clear()
{
_min_history.clear();
}
//---------------------------------------------------------------------
// update
//---------------------------------------------------------------------
void MaxHistory::update(int value, int64_t now_ts)
{
_min_history.update(value, now_ts);
}
//---------------------------------------------------------------------
// ctor
//---------------------------------------------------------------------
MovingAverage::MovingAverage()
{
_wnd_size = 5000;
_sum = 0;
_average = 0;
}
//---------------------------------------------------------------------
// dtor
//---------------------------------------------------------------------
MovingAverage::~MovingAverage()
{
}
//---------------------------------------------------------------------
// reset
//---------------------------------------------------------------------
void MovingAverage::init(int wnd_size)
{
_wnd_size = wnd_size;
}
//---------------------------------------------------------------------
// clear
//---------------------------------------------------------------------
void MovingAverage::clear()
{
_history.clear();
_sum = 0;
_average = 0;
}
//---------------------------------------------------------------------
// update value
//---------------------------------------------------------------------
void MovingAverage::update(int value, int64_t now_ts)
{
while (!_history.empty()) {
if (now_ts - _history.front().first + 1 <= _wnd_size) break;
_sum -= _history.front().second;
_history.pop_front();
}
_history.push_back(std::make_pair(now_ts, value));
_sum += value;
_average = (int)(_sum / _history.size());
}
//---------------------------------------------------------------------
// ctor
//---------------------------------------------------------------------
RttHistory::RttHistory()
{
init(200);
}
//---------------------------------------------------------------------
// dtor
//---------------------------------------------------------------------
RttHistory::~RttHistory()
{
}
//---------------------------------------------------------------------
// initialize, wnd_size is the number of samples to keep
//---------------------------------------------------------------------
void RttHistory::init(int wnd_size)
{
_wnd_size = wnd_size;
_sum = 0;
_avg = 0;
_srtt = -1;
_rttval = -1;
_rto = 0;
_deviation = 0;
_min = 0;
_max = 0;
_jitter = 0;
_samples.clear();
}
//---------------------------------------------------------------------
// clear history
//---------------------------------------------------------------------
void RttHistory::clear()
{
_samples.clear();
}
//---------------------------------------------------------------------
// push a new rtt sample
//---------------------------------------------------------------------
void RttHistory::push(double rtt)
{
_samples.push_back(rtt);
_sum += rtt;
if ((int)_samples.size() > _wnd_size) {
_sum -= _samples.front();
_samples.pop_front();
}
if (_samples.size() > 0) {
_avg = _sum / _samples.size();
double sum = 0;
double minimum = 0;
double maximum = 0;
bool first = true;
for (double rtt : _samples) {
double diff = rtt - _avg;
sum += diff * diff;
if (first) {
minimum = rtt;
maximum = rtt;
first = false;
}
if (rtt < minimum) minimum = rtt;
if (rtt > maximum) maximum = rtt;
}
_deviation = sum / _samples.size();
_min = minimum;
_max = maximum;
_jitter = (maximum - minimum) / 2;
}
if (_srtt < 0) {
_srtt = rtt;
_rttval = rtt / 2;
} else {
double delta = rtt - _srtt;
if (delta < 0) delta = -delta;
_rttval = (3 * _rttval + delta) / 4;
_srtt = (7 * _srtt + rtt) / 8;
if (_srtt < 0) _srtt = 0;
}
_rto = _srtt + (_rttval < 0? 0 : (4 * _rttval));
}
//=====================================================================
// 带宽预算Token Bucket
//=====================================================================
//---------------------------------------------------------------------
// ctor
//---------------------------------------------------------------------
PacingBudget::PacingBudget()
{
init(0);
}
//---------------------------------------------------------------------
// 初始化bandwidth 为带宽bytes/secburst 默认等于 bandwidth
//---------------------------------------------------------------------
void PacingBudget::init(int64_t bandwidth_bps)
{
_bandwidth = bandwidth_bps;
_burst = bandwidth_bps;
_budget = 0;
_last_time = -1;
_remainder = 0;
}
//---------------------------------------------------------------------
// 重置状态
//---------------------------------------------------------------------
void PacingBudget::reset()
{
_budget = 0;
_last_time = -1;
_remainder = 0;
}
//---------------------------------------------------------------------
// 查询当前可发送字节数(内部自动更新时间,累加预算)
//---------------------------------------------------------------------
int64_t PacingBudget::available(int64_t now_ms)
{
if (_last_time < 0) {
_last_time = now_ms;
return _budget;
}
int64_t elapsed = now_ms - _last_time;
if (elapsed <= 0) {
return (_budget > 0) ? _budget : 0;
}
_last_time = now_ms;
// 累加预算bandwidth * elapsed / 1000用 remainder 保留余数
int64_t total = _bandwidth * elapsed + _remainder;
int64_t increment = total / 1000;
_remainder = total % 1000;
_budget += increment;
// 限制预算不超过 burst
if (_budget > _burst) {
_budget = _burst;
_remainder = 0;
}
return (_budget > 0) ? _budget : 0;
}
//---------------------------------------------------------------------
// 登记实际发送的字节数,扣减预算
//---------------------------------------------------------------------
void PacingBudget::consume(int64_t bytes_sent)
{
_budget -= bytes_sent;
}
//---------------------------------------------------------------------
// 计算发送 bytes_needed 还需等多少毫秒(已够返回 0
//---------------------------------------------------------------------
int64_t PacingBudget::wait_time(int64_t bytes_needed) const
{
int64_t deficit = bytes_needed - _budget;
if (deficit <= 0) {
return 0;
}
if (_bandwidth <= 0) {
return -1;
}
return (deficit * 1000 + _bandwidth - 1) / _bandwidth;
}
//---------------------------------------------------------------------
// namespace end
//---------------------------------------------------------------------
NAMESPACE_END(System);