#1 - quicr module
This commit is contained in:
@@ -1,157 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <print>
|
||||
|
||||
namespace tw::net {
|
||||
|
||||
template<typename T>
|
||||
struct AverageOp {
|
||||
uint32_t count;
|
||||
T sum;
|
||||
|
||||
AverageOp() :
|
||||
count(0),
|
||||
sum{} {
|
||||
}
|
||||
|
||||
void add(const T value) {
|
||||
sum += value;
|
||||
count++;
|
||||
}
|
||||
|
||||
T result() const {
|
||||
return count == 0 ? 0 : sum / count;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct SumOp {
|
||||
T sum;
|
||||
|
||||
void add(const T value) {
|
||||
sum += value;
|
||||
}
|
||||
|
||||
T result() const {
|
||||
return sum;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename Interval,
|
||||
typename Operation = SumOp<T>,
|
||||
typename Clock = std::chrono::steady_clock>
|
||||
class BucketMetric {
|
||||
|
||||
T m_min, m_max;
|
||||
|
||||
std::vector<T> m_metric;
|
||||
std::vector<uint32_t> m_bucket_idx;
|
||||
|
||||
Operation m_op;
|
||||
|
||||
uint32_t m_offset;
|
||||
uint32_t m_right, m_left;
|
||||
|
||||
std::string m_format;
|
||||
|
||||
const uint32_t get_bucket(Clock::time_point time_point) const {
|
||||
return std::chrono::floor<Interval>(time_point).time_since_epoch().count() - m_offset;
|
||||
}
|
||||
|
||||
public:
|
||||
BucketMetric(std::string format, uint32_t size) :
|
||||
m_metric(size),
|
||||
m_bucket_idx(size),
|
||||
m_right(0), m_left(0),
|
||||
m_offset(0),
|
||||
m_format(format)
|
||||
{
|
||||
m_offset = get_bucket(Clock::now());
|
||||
}
|
||||
|
||||
const T max() const {
|
||||
return m_max;
|
||||
}
|
||||
|
||||
const T min() const {
|
||||
return m_min;
|
||||
}
|
||||
|
||||
const std::string& format() const {
|
||||
return m_format;
|
||||
}
|
||||
|
||||
size_t max_size() const {
|
||||
return m_metric.size();
|
||||
}
|
||||
|
||||
void push(T value) {
|
||||
auto time = Clock::now();
|
||||
size_t bucket = get_bucket(time) % m_metric.size();
|
||||
size_t idx = get_bucket(time);
|
||||
|
||||
// set result to correct bucket
|
||||
if(m_right != bucket) {
|
||||
m_metric[m_right] = m_op.result();
|
||||
m_min = std::min(m_min, m_op.result());
|
||||
m_max = std::max(m_max, m_op.result());
|
||||
|
||||
m_bucket_idx[m_right] = idx++;
|
||||
m_right++;
|
||||
m_op = {};
|
||||
}
|
||||
|
||||
// reset all buckets until the required one
|
||||
for(; m_right != bucket; m_right = (m_right + 1) % m_metric.size()) {
|
||||
m_metric[m_right] = {};
|
||||
m_bucket_idx[m_right] = idx++;
|
||||
if(m_right == m_left) {
|
||||
m_left = (m_left + 1) % m_metric.size();
|
||||
}
|
||||
}
|
||||
|
||||
m_op.add(value);
|
||||
}
|
||||
|
||||
const size_t get_size() const {
|
||||
return m_right - m_left + (m_left > m_right ? m_metric.size() : 0);
|
||||
}
|
||||
|
||||
const T get(uint32_t idx) const {
|
||||
if(idx > get_size()) {
|
||||
throw std::invalid_argument("`idx` cannot be higher than buffer size");
|
||||
}
|
||||
|
||||
return m_metric[m_left + idx].result();
|
||||
}
|
||||
|
||||
std::span<T> get_head() {
|
||||
return std::span(m_metric).subspan(m_left, (m_right > m_left ? m_right : m_metric.size()));
|
||||
}
|
||||
|
||||
std::span<uint32_t> get_head_timeline() {
|
||||
return std::span(m_bucket_idx).subspan(m_left, (m_right > m_left ? m_right : m_bucket_idx.size()));
|
||||
}
|
||||
|
||||
std::span<T> get_tail() {
|
||||
if(m_right > m_left) {
|
||||
return std::span<T>();
|
||||
}
|
||||
|
||||
return std::span(m_metric).subspan(0, m_right);
|
||||
}
|
||||
|
||||
std::span<uint32_t> get_tail_timeline() {
|
||||
if(m_right > m_left) {
|
||||
return std::span<T>();
|
||||
}
|
||||
|
||||
return std::span(m_bucket_idx).subspan(0, m_right);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
}
|
||||
|
||||
std::optional<const TValue*> get(TKey key) const {
|
||||
for(size_t i = m_tail; i != m_head; (i++) % max_size()) {
|
||||
for(size_t i = m_tail; i != m_head; i = (i + 1) % max_size()) {
|
||||
if(m_buffer[i].first > key) {
|
||||
return {};
|
||||
}
|
||||
@@ -56,24 +56,25 @@ public:
|
||||
}
|
||||
|
||||
bool set(TKey key, const TValue& value) {
|
||||
if(key < m_buffer.at(m_tail).first) {
|
||||
// If buffer has entries and key is older than the oldest, reject it
|
||||
if(m_tail != m_head && key < m_buffer.at(m_tail).first) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int i = m_tail + 1;
|
||||
size_t i = (m_tail + 1) % max_size();
|
||||
if(m_tail != m_head) {
|
||||
for(i = m_tail + 1; i != m_head; i++) {
|
||||
for(i = (m_tail + 1) % max_size(); i != m_head; i = (i + 1) % max_size()) {
|
||||
if(m_buffer.at(i).first > key) {
|
||||
m_buffer[(i - 1) % max_size()] = std::make_pair(key, value);
|
||||
m_head++;
|
||||
m_buffer[(i - 1 + max_size()) % max_size()] = std::make_pair(key, value);
|
||||
m_head = (m_head + 1) % max_size();
|
||||
return true;
|
||||
} else {
|
||||
m_buffer[(i - 1) % max_size()] = m_buffer[i];
|
||||
m_buffer[(i - 1 + max_size()) % max_size()] = m_buffer[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_buffer[(i - 1) % max_size()] = std::make_pair(key, value);
|
||||
m_buffer[(i - 1 + max_size()) % max_size()] = std::make_pair(key, value);
|
||||
m_head = (m_head + 1) % max_size();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <cassert>
|
||||
|
||||
#include "Address.hpp"
|
||||
#include "BucketMetric.hpp"
|
||||
#include "packets/Packet.hpp"
|
||||
#include "MessageRegistry.hpp"
|
||||
|
||||
namespace tw::net {
|
||||
|
||||
using Clock = std::chrono::steady_clock;
|
||||
using TimePoint = Clock::time_point;
|
||||
|
||||
struct NetworkSendInfo {
|
||||
PacketType message_type;
|
||||
bool is_sent_by_us;
|
||||
Address target;
|
||||
TimePoint timepoint;
|
||||
std::span<uint8_t> buffer;
|
||||
|
||||
NetworkSendInfo(
|
||||
PacketType message_type,
|
||||
bool is_sent_by_us,
|
||||
const Address& target,
|
||||
const std::span<uint8_t> buffer
|
||||
) :
|
||||
message_type(message_type),
|
||||
is_sent_by_us(is_sent_by_us),
|
||||
target(target),
|
||||
timepoint(std::chrono::steady_clock::now()),
|
||||
buffer(buffer)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class NetworkStatsLogger {
|
||||
private:
|
||||
|
||||
|
||||
std::vector<NetworkSendInfo> m_backlog;
|
||||
|
||||
std::vector<uint8_t> m_buffer;
|
||||
size_t m_left, m_right;
|
||||
|
||||
std::optional<std::ostream> m_output;
|
||||
|
||||
using Interval = std::chrono::seconds;
|
||||
|
||||
BucketMetric<uint32_t, Interval, AverageOp<uint32_t>> m_ping_metric;
|
||||
BucketMetric<uint32_t, Interval, SumOp<uint32_t>> m_outgoing;
|
||||
BucketMetric<uint32_t, Interval, SumOp<uint32_t>> m_incoming;
|
||||
|
||||
public:
|
||||
|
||||
NetworkStatsLogger() :
|
||||
m_backlog(10000, {MESSAGE_PACKET, false, Address({}, 0), {}}),
|
||||
m_buffer(1000000),
|
||||
m_left(0), m_right(0),
|
||||
m_ping_metric("ms", 1000),
|
||||
m_outgoing("b/s", 1000),
|
||||
m_incoming("b/s", 1000)
|
||||
{ }
|
||||
|
||||
void set_file_output(std::filesystem::path path);
|
||||
|
||||
size_t get_size() {
|
||||
return m_right - m_left + (m_right < m_left ? m_backlog.size() : 0);
|
||||
}
|
||||
|
||||
NetworkSendInfo& get_item(uint32_t idx) {
|
||||
return m_backlog[(m_left + idx) % m_backlog.size()];
|
||||
}
|
||||
|
||||
std::span<uint8_t> allocate_memory_for_buffer(size_t size) {
|
||||
uint32_t start = m_right;
|
||||
if(m_buffer.size() - m_right < size) {
|
||||
// throw away packets from the start to make space
|
||||
for(; m_backlog[m_left].buffer.data() < m_buffer.data() + start + size &&
|
||||
m_left != m_right; m_left = (m_left + 1) % m_buffer.size()) { }
|
||||
|
||||
start = 0;
|
||||
}
|
||||
|
||||
uint32_t end = start + size;
|
||||
return std::span<uint8_t>(m_buffer.begin() + start, m_buffer.begin() + end);
|
||||
}
|
||||
|
||||
// constexpr void log(PacketType message_type, bool is_sent, const Address& target, const ByteBuffer& content) {
|
||||
// std::span<uint8_t> span = allocate_memory_for_buffer(content.size());
|
||||
|
||||
// memcpy(span.data(), content.data().data(), content.size());
|
||||
|
||||
// m_right = (m_right + 1) % m_backlog.size();
|
||||
// m_backlog[m_right] = NetworkSendInfo(message_type, is_sent, target, span);
|
||||
// }
|
||||
|
||||
// constexpr void log_receive(
|
||||
// PacketType message_type,
|
||||
// const Address& from
|
||||
// ) {
|
||||
// log(message_type, false, from, content);
|
||||
// m_incoming.push(content.size());
|
||||
// }
|
||||
|
||||
// constexpr void log_send(
|
||||
// PacketType message_type,
|
||||
// const Address& to
|
||||
// ) {
|
||||
// log(message_type, true, to, content);
|
||||
// m_outgoing.push(content.size());
|
||||
// }
|
||||
|
||||
void log_ping(uint32_t ping) {
|
||||
m_ping_metric.push(ping);
|
||||
}
|
||||
|
||||
BucketMetric<uint32_t, Interval, AverageOp<uint32_t>>& ping(){
|
||||
return m_ping_metric;
|
||||
}
|
||||
|
||||
BucketMetric<uint32_t, Interval>& outgoing(){
|
||||
return m_outgoing;
|
||||
}
|
||||
|
||||
BucketMetric<uint32_t, Interval>& incoming(){
|
||||
return m_incoming;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user