From 9de411f6236196d5fc4763e3b9c52b122e6f408c Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Tue, 1 Sep 2026 09:31:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../function/frame_policy/Frame_Policy.hpp | 75 ++++++++++++++++ .../function/frame_policy/Frame_Policy.ipp | 88 +++++++++++++++++++ .../include/statistics/Sliding_Statistics.cpp | 66 ++++++++++++++ .../include/statistics/Sliding_Statistics.hpp | 34 +++++++ 4 files changed, 263 insertions(+) create mode 100644 kernel/kernel/include/function/frame_policy/Frame_Policy.hpp create mode 100644 kernel/kernel/include/function/frame_policy/Frame_Policy.ipp create mode 100644 kernel/kernel/include/statistics/Sliding_Statistics.cpp create mode 100644 kernel/kernel/include/statistics/Sliding_Statistics.hpp diff --git a/kernel/kernel/include/function/frame_policy/Frame_Policy.hpp b/kernel/kernel/include/function/frame_policy/Frame_Policy.hpp new file mode 100644 index 0000000..142d69e --- /dev/null +++ b/kernel/kernel/include/function/frame_policy/Frame_Policy.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include "concurrent/base/Concurrent_Struct.hpp" +#include "model/Model.hpp" +#include "statistics/Sliding_Statistics.hpp" + +#include +#include +#include +#include + +namespace aethera { +/* 所有帧策略共享的统计配置、公开状态和统计实现,不拥有具体调度状态。 */ +template +struct Frame_Policy : Model_Layer< + Self, + Root, + Storage_Registration> { + using Layer = Model_Layer< + Self, + Root, + Storage_Registration>; + using Prev_Private = typename Layer::Prev_Private; + using Prev_Builder = typename Layer::Prev_Builder; + + struct Prop : Layer::template Prev { + bool statistics_enabled{}; /* true 时采集并发布滑动统计。 */ + std::size_t statistics_window_size{120}; /* 启用统计时的样本窗口容量。 */ + }; + struct State { + std::uint64_t timer_ticks{}; /* 已处理的周期到期次数。 */ + std::uint64_t dropped_timer_ticks{}; /* 到期时三帧均忙而丢弃的次数。 */ + std::uint64_t completed_frames{}; /* 已完成媒体发送的帧数。 */ + Statistics_Summary render_time_ns; /* 渲染耗时滑动统计,单位纳秒。 */ + Statistics_Summary send_time_ns; /* 发送耗时滑动统计,单位纳秒。 */ + Statistics_Summary end_to_end_time_ns; /* 渲染开始至发送完成耗时统计,单位纳秒。 */ + }; + struct Private : Prev_Private { + struct Active_Statistics { + explicit Active_Statistics(std::size_t window_size); + + Sliding_Statistics render; /* 渲染耗时窗口。 */ + Sliding_Statistics send; /* 发送耗时窗口。 */ + Sliding_Statistics end_to_end; /* 渲染开始至发送结束耗时窗口。 */ + }; + + bool prepare_statistics( + const Prop& properties, + Export_Struct& published_state); + void record_timer_tick( + bool dropped, + Export_Struct& published_state); + void record_render( + std::chrono::nanoseconds duration, + Export_Struct& published_state); + void record_send( + std::chrono::nanoseconds send_duration, + std::chrono::nanoseconds end_to_end_duration, + Export_Struct& published_state); + + private: + void publish(Export_Struct& published_state); + + std::optional statistics; /* 存在即表示本次运行启用了统计。 */ + std::uint64_t timer_ticks{}; /* 本次运行累计的到期次数。 */ + std::uint64_t dropped_timer_ticks{}; /* 本次运行累计的忙碌丢弃次数。 */ + std::uint64_t completed_frames{}; /* 本次运行累计的完成帧数。 */ + }; + struct Builder : Prev_Builder {}; + + Export_Struct state; /* 由策略互斥串行发布的帧策略运行统计。 */ +}; +} + +#include "Frame_Policy.ipp" diff --git a/kernel/kernel/include/function/frame_policy/Frame_Policy.ipp b/kernel/kernel/include/function/frame_policy/Frame_Policy.ipp new file mode 100644 index 0000000..f352ef6 --- /dev/null +++ b/kernel/kernel/include/function/frame_policy/Frame_Policy.ipp @@ -0,0 +1,88 @@ +#pragma once + +namespace aethera { +template +Frame_Policy::Private::Active_Statistics::Active_Statistics( + std::size_t window_size) : + render(window_size), + send(window_size), + end_to_end(window_size) {} + +template +bool Frame_Policy::Private::prepare_statistics( + const Prop& properties, + Export_Struct& published_state) { + if (properties.statistics_enabled && properties.statistics_window_size == 0) { + return false; + } + + statistics.reset(); + timer_ticks = 0; + dropped_timer_ticks = 0; + completed_frames = 0; + if (properties.statistics_enabled) { + statistics.emplace(properties.statistics_window_size); + } + publish(published_state); + return true; +} + +template +void Frame_Policy::Private::record_timer_tick( + bool dropped, + Export_Struct& published_state) { + if (!statistics) { + return; + } + ++timer_ticks; + if (dropped) { + ++dropped_timer_ticks; + } + publish(published_state); +} + +template +void Frame_Policy::Private::record_render( + std::chrono::nanoseconds duration, + Export_Struct& published_state) { + if (!statistics) { + return; + } + statistics->render.add(static_cast(duration.count())); + publish(published_state); +} + +template +void Frame_Policy::Private::record_send( + std::chrono::nanoseconds send_duration, + std::chrono::nanoseconds end_to_end_duration, + Export_Struct& published_state) { + if (!statistics) { + return; + } + statistics->send.add(static_cast(send_duration.count())); + statistics->end_to_end.add( + static_cast(end_to_end_duration.count())); + ++completed_frames; + publish(published_state); +} + +template +void Frame_Policy::Private::publish( + Export_Struct& published_state) { + State& output = *published_state.internal.use(); + output.timer_ticks = timer_ticks; + output.dropped_timer_ticks = dropped_timer_ticks; + output.completed_frames = completed_frames; + output.render_time_ns = statistics + ? statistics->render.summary() + : Statistics_Summary{}; + output.send_time_ns = statistics + ? statistics->send.summary() + : Statistics_Summary{}; + output.end_to_end_time_ns = statistics + ? statistics->end_to_end.summary() + : Statistics_Summary{}; + published_state.internal.advance(); +} +} diff --git a/kernel/kernel/include/statistics/Sliding_Statistics.cpp b/kernel/kernel/include/statistics/Sliding_Statistics.cpp new file mode 100644 index 0000000..04397bc --- /dev/null +++ b/kernel/kernel/include/statistics/Sliding_Statistics.cpp @@ -0,0 +1,66 @@ +#include "Sliding_Statistics.hpp" + +#include +#include +#include +#include + +namespace aethera { +Sliding_Statistics::Sliding_Statistics(std::size_t window_size) : + window_size(window_size) { + if (this->window_size == 0) { + std::terminate(); + } + samples.reserve(this->window_size); +} + +void Sliding_Statistics::add(double sample) { + if (samples.size() < window_size) { + samples.push_back(sample); + } else { + const double retired = samples[next_index]; + const auto retired_position = ordered.find(retired); + if (retired_position == ordered.end()) { + std::terminate(); + } + ordered.erase(retired_position); + sum -= retired; + squared_sum -= retired * retired; + samples[next_index] = sample; + next_index = (next_index + 1) % samples.size(); + } + + ordered.insert(sample); + sum += sample; + squared_sum += sample * sample; +} + +void Sliding_Statistics::clear() noexcept { + samples.clear(); + ordered.clear(); + next_index = 0; + sum = 0.0; + squared_sum = 0.0; +} + +Statistics_Summary Sliding_Statistics::summary() const noexcept { + if (samples.empty()) { + return {}; + } + + const auto count = static_cast(samples.size()); + const double average = sum / static_cast(count); + const double variance = std::max( + 0.0, + squared_sum / static_cast(count) - average * average); + const std::size_t p95_rank = + (static_cast(count) * 95 + 99) / 100; + auto p95_position = ordered.begin(); + std::advance(p95_position, p95_rank - 1); + return { + count, + average, + *p95_position, + std::sqrt(variance)}; +} +} diff --git a/kernel/kernel/include/statistics/Sliding_Statistics.hpp b/kernel/kernel/include/statistics/Sliding_Statistics.hpp new file mode 100644 index 0000000..1d27924 --- /dev/null +++ b/kernel/kernel/include/statistics/Sliding_Statistics.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "global.hpp" + +#include +#include +#include +#include + +namespace aethera { +struct Statistics_Summary { + std::uint64_t sample_count{}; /* 当前滑动窗口内的有效样本数。 */ + double average{}; /* 当前窗口样本的算术平均值。 */ + double p95{}; /* 当前窗口按最近秩定义的第 95 百分位。 */ + double standard_deviation{}; /* 当前窗口总体标准差,用于表达波动。 */ +}; + +/* 固定容量滑动统计;有序样本直接维护 P95,不复制窗口或生成快照。 */ +struct Sliding_Statistics : Immovable { + explicit Sliding_Statistics(std::size_t window_size); + + void add(double sample); + void clear() noexcept; + [[nodiscard]] Statistics_Summary summary() const noexcept; + +private: + std::size_t window_size; /* 构造时确定且非零的滑动窗口容量。 */ + std::vector samples; /* 按写入顺序保存当前窗口,用于淘汰最旧样本。 */ + std::multiset ordered; /* 与 samples 中有效窗口等价的有序样本集合。 */ + std::size_t next_index{}; /* 窗口满后下一次被替换的样本位置。 */ + double sum{}; /* 当前窗口样本和。 */ + double squared_sum{}; /* 当前窗口样本平方和。 */ +}; +}