更新
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include "concurrent/base/Concurrent_Struct.hpp"
|
||||
#include "model/Model.hpp"
|
||||
#include "statistics/Sliding_Statistics.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace aethera {
|
||||
/* 所有帧策略共享的统计配置、公开状态和统计实现,不拥有具体调度状态。 */
|
||||
template <typename Self>
|
||||
struct Frame_Policy : Model_Layer<
|
||||
Self,
|
||||
Root<Self>,
|
||||
Storage_Registration<Prop_Tag, Import_Struct_With_Dirty>> {
|
||||
using Layer = Model_Layer<
|
||||
Self,
|
||||
Root<Self>,
|
||||
Storage_Registration<Prop_Tag, Import_Struct_With_Dirty>>;
|
||||
using Prev_Private = typename Layer::Prev_Private;
|
||||
using Prev_Builder = typename Layer::Prev_Builder;
|
||||
|
||||
struct Prop : Layer::template Prev<Prop_Tag> {
|
||||
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<State>& published_state);
|
||||
void record_timer_tick(
|
||||
bool dropped,
|
||||
Export_Struct<State>& published_state);
|
||||
void record_render(
|
||||
std::chrono::nanoseconds duration,
|
||||
Export_Struct<State>& published_state);
|
||||
void record_send(
|
||||
std::chrono::nanoseconds send_duration,
|
||||
std::chrono::nanoseconds end_to_end_duration,
|
||||
Export_Struct<State>& published_state);
|
||||
|
||||
private:
|
||||
void publish(Export_Struct<State>& published_state);
|
||||
|
||||
std::optional<Active_Statistics> statistics; /* 存在即表示本次运行启用了统计。 */
|
||||
std::uint64_t timer_ticks{}; /* 本次运行累计的到期次数。 */
|
||||
std::uint64_t dropped_timer_ticks{}; /* 本次运行累计的忙碌丢弃次数。 */
|
||||
std::uint64_t completed_frames{}; /* 本次运行累计的完成帧数。 */
|
||||
};
|
||||
struct Builder : Prev_Builder {};
|
||||
|
||||
Export_Struct<State> state; /* 由策略互斥串行发布的帧策略运行统计。 */
|
||||
};
|
||||
}
|
||||
|
||||
#include "Frame_Policy.ipp"
|
||||
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
namespace aethera {
|
||||
template <typename Self>
|
||||
Frame_Policy<Self>::Private::Active_Statistics::Active_Statistics(
|
||||
std::size_t window_size) :
|
||||
render(window_size),
|
||||
send(window_size),
|
||||
end_to_end(window_size) {}
|
||||
|
||||
template <typename Self>
|
||||
bool Frame_Policy<Self>::Private::prepare_statistics(
|
||||
const Prop& properties,
|
||||
Export_Struct<State>& 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 <typename Self>
|
||||
void Frame_Policy<Self>::Private::record_timer_tick(
|
||||
bool dropped,
|
||||
Export_Struct<State>& published_state) {
|
||||
if (!statistics) {
|
||||
return;
|
||||
}
|
||||
++timer_ticks;
|
||||
if (dropped) {
|
||||
++dropped_timer_ticks;
|
||||
}
|
||||
publish(published_state);
|
||||
}
|
||||
|
||||
template <typename Self>
|
||||
void Frame_Policy<Self>::Private::record_render(
|
||||
std::chrono::nanoseconds duration,
|
||||
Export_Struct<State>& published_state) {
|
||||
if (!statistics) {
|
||||
return;
|
||||
}
|
||||
statistics->render.add(static_cast<double>(duration.count()));
|
||||
publish(published_state);
|
||||
}
|
||||
|
||||
template <typename Self>
|
||||
void Frame_Policy<Self>::Private::record_send(
|
||||
std::chrono::nanoseconds send_duration,
|
||||
std::chrono::nanoseconds end_to_end_duration,
|
||||
Export_Struct<State>& published_state) {
|
||||
if (!statistics) {
|
||||
return;
|
||||
}
|
||||
statistics->send.add(static_cast<double>(send_duration.count()));
|
||||
statistics->end_to_end.add(
|
||||
static_cast<double>(end_to_end_duration.count()));
|
||||
++completed_frames;
|
||||
publish(published_state);
|
||||
}
|
||||
|
||||
template <typename Self>
|
||||
void Frame_Policy<Self>::Private::publish(
|
||||
Export_Struct<State>& 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "Sliding_Statistics.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iterator>
|
||||
#include <exception>
|
||||
|
||||
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<std::uint64_t>(samples.size());
|
||||
const double average = sum / static_cast<double>(count);
|
||||
const double variance = std::max(
|
||||
0.0,
|
||||
squared_sum / static_cast<double>(count) - average * average);
|
||||
const std::size_t p95_rank =
|
||||
(static_cast<std::size_t>(count) * 95 + 99) / 100;
|
||||
auto p95_position = ordered.begin();
|
||||
std::advance(p95_position, p95_rank - 1);
|
||||
return {
|
||||
count,
|
||||
average,
|
||||
*p95_position,
|
||||
std::sqrt(variance)};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "global.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
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<double> samples; /* 按写入顺序保存当前窗口,用于淘汰最旧样本。 */
|
||||
std::multiset<double> ordered; /* 与 samples 中有效窗口等价的有序样本集合。 */
|
||||
std::size_t next_index{}; /* 窗口满后下一次被替换的样本位置。 */
|
||||
double sum{}; /* 当前窗口样本和。 */
|
||||
double squared_sum{}; /* 当前窗口样本平方和。 */
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user