525 lines
13 KiB
C++
525 lines
13 KiB
C++
#pragma once
|
||
|
||
#include <chrono>
|
||
#include <vector>
|
||
#include <algorithm>
|
||
#include <numeric>
|
||
#include <cmath>
|
||
#include <functional>
|
||
#include <string>
|
||
#include <gtest/gtest.h>
|
||
|
||
#include "logger.h"
|
||
|
||
namespace shm_test {
|
||
/**
|
||
* @brief 时间单位枚举
|
||
*/
|
||
enum class TimeUnit {
|
||
NANOSECONDS,
|
||
MICROSECONDS,
|
||
MILLISECONDS,
|
||
SECONDS
|
||
};
|
||
|
||
/**
|
||
* @brief 性能统计数据
|
||
*
|
||
* 包含延迟分布的各种统计指标
|
||
*/
|
||
class Statistics {
|
||
public:
|
||
Statistics() = default;
|
||
|
||
/**
|
||
* @brief 从延迟样本构造统计数据
|
||
* @param latencies 延迟样本(纳秒)
|
||
*/
|
||
explicit Statistics(std::vector<double> latencies) : latencies_(std::move(latencies)) {
|
||
calculate();
|
||
}
|
||
|
||
/**
|
||
* @brief 添加延迟样本
|
||
* @param latency_ns 延迟(纳秒)
|
||
*/
|
||
void add_sample(double latency_ns) {
|
||
latencies_.push_back(latency_ns);
|
||
}
|
||
|
||
/**
|
||
* @brief 计算所有统计指标
|
||
*/
|
||
void calculate() {
|
||
if (latencies_.empty()) {
|
||
min_ = max_ = avg_ = p50_ = p95_ = p99_ = 0.0;
|
||
stddev_ = 0.0;
|
||
return;
|
||
}
|
||
|
||
// 排序以计算百分位数
|
||
std::sort(latencies_.begin(), latencies_.end());
|
||
|
||
// 最小值和最大值
|
||
min_ = latencies_.front();
|
||
max_ = latencies_.back();
|
||
|
||
// 平均值
|
||
avg_ = std::accumulate(latencies_.begin(), latencies_.end(), 0.0)
|
||
/ latencies_.size();
|
||
|
||
// 标准差
|
||
double sq_sum = std::accumulate(latencies_.begin(), latencies_.end(), 0.0,
|
||
[this](double sum, double val) {
|
||
double diff = val - avg_;
|
||
return sum + diff * diff;
|
||
});
|
||
stddev_ = std::sqrt(sq_sum / latencies_.size());
|
||
|
||
// 百分位数
|
||
p50_ = percentile(50.0);
|
||
p95_ = percentile(95.0);
|
||
p99_ = percentile(99.0);
|
||
}
|
||
|
||
/**
|
||
* @brief 获取指定百分位数的值
|
||
* @param p 百分比 (0-100)
|
||
* @return 百分位数值(纳秒)
|
||
*/
|
||
double percentile(double p) const {
|
||
if (latencies_.empty()) {
|
||
return 0.0;
|
||
}
|
||
|
||
if (p <= 0.0)
|
||
return latencies_.front();
|
||
if (p >= 100.0)
|
||
return latencies_.back();
|
||
|
||
double index = (p / 100.0) * (latencies_.size() - 1);
|
||
size_t lower = static_cast<size_t>(std::floor(index));
|
||
size_t upper = static_cast<size_t>(std::ceil(index));
|
||
|
||
if (lower == upper) {
|
||
return latencies_[lower];
|
||
}
|
||
|
||
double weight = index - lower;
|
||
return latencies_[lower] * (1.0 - weight) + latencies_[upper] * weight;
|
||
}
|
||
|
||
/**
|
||
* @brief 转换时间单位
|
||
* @param value_ns 纳秒值
|
||
* @param unit 目标单位
|
||
* @return 转换后的值
|
||
*/
|
||
static double convert_time(double value_ns, TimeUnit unit) {
|
||
switch (unit) {
|
||
case TimeUnit::NANOSECONDS:
|
||
return value_ns;
|
||
case TimeUnit::MICROSECONDS:
|
||
return value_ns / 1000.0;
|
||
case TimeUnit::MILLISECONDS:
|
||
return value_ns / 1000000.0;
|
||
case TimeUnit::SECONDS:
|
||
return value_ns / 1000000000.0;
|
||
default:
|
||
return value_ns;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 获取时间单位的字符串表示
|
||
*/
|
||
static const char* time_unit_string(TimeUnit unit) {
|
||
switch (unit) {
|
||
case TimeUnit::NANOSECONDS:
|
||
return "ns";
|
||
case TimeUnit::MICROSECONDS:
|
||
return "μs";
|
||
case TimeUnit::MILLISECONDS:
|
||
return "ms";
|
||
case TimeUnit::SECONDS:
|
||
return "s";
|
||
default:
|
||
return "ns";
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 打印统计报告
|
||
* @param unit 时间单位
|
||
* @param label 标签(可选)
|
||
*/
|
||
void print_report(TimeUnit unit = TimeUnit::MICROSECONDS,
|
||
const std::string& label = "") const {
|
||
if (!label.empty()) {
|
||
log_info("=== {} 性能统计 ===", label);
|
||
}
|
||
else {
|
||
log_info("=== 性能统计 ===");
|
||
}
|
||
|
||
const char* unit_str = time_unit_string(unit);
|
||
|
||
log_info("样本数量: {}", latencies_.size());
|
||
log_info("最小值: {:.2f} {}", convert_time(min_, unit), unit_str);
|
||
log_info("最大值: {:.2f} {}", convert_time(max_, unit), unit_str);
|
||
log_info("平均值: {:.2f} {}", convert_time(avg_, unit), unit_str);
|
||
log_info("标准差: {:.2f} {}", convert_time(stddev_, unit), unit_str);
|
||
log_info("P50: {:.2f} {}", convert_time(p50_, unit), unit_str);
|
||
log_info("P95: {:.2f} {}", convert_time(p95_, unit), unit_str);
|
||
log_info("P99: {:.2f} {}", convert_time(p99_, unit), unit_str);
|
||
}
|
||
|
||
// Getters(纳秒)
|
||
double min() const { return min_; }
|
||
double max() const { return max_; }
|
||
double avg() const { return avg_; }
|
||
double stddev() const { return stddev_; }
|
||
double p50() const { return p50_; }
|
||
double p95() const { return p95_; }
|
||
double p99() const { return p99_; }
|
||
size_t sample_count() const { return latencies_.size(); }
|
||
|
||
/**
|
||
* @brief 重置统计数据
|
||
*/
|
||
void reset() {
|
||
latencies_.clear();
|
||
min_ = max_ = avg_ = p50_ = p95_ = p99_ = 0.0;
|
||
stddev_ = 0.0;
|
||
}
|
||
|
||
private:
|
||
std::vector<double> latencies_; ///< 延迟本(纳秒)
|
||
double min_{0.0}; ///< 最小延迟
|
||
double max_{0.0}; ///< 最大延迟
|
||
double avg_{0.0}; ///< 平均延迟
|
||
double stddev_{0.0}; ///< 标准差
|
||
double p50_{0.0}; ///< 50% 百分位数(中位数)
|
||
double p95_{0.0}; ///< 95% 百分位数
|
||
double p99_{0.0}; ///< 99% 百分位数
|
||
};
|
||
|
||
/**
|
||
* @brief RAII 作用域计时器
|
||
*
|
||
* 在构造时开始计时,析构时自动记录耗时
|
||
*
|
||
* @example
|
||
* {
|
||
* ScopedTimer timer("操作名称");
|
||
* // 执行需要计时的操作...
|
||
* } // 析构时自动输出耗时
|
||
*/
|
||
class ScopedTimer {
|
||
public:
|
||
using Clock = std::chrono::high_resolution_clock;
|
||
using TimePoint = Clock::time_point;
|
||
using Duration = std::chrono::nanoseconds;
|
||
|
||
/**
|
||
* @brief 构造函数 - 开始计时
|
||
* @param name 计时器名称
|
||
* @param auto_print 析构时是否自动打印结果
|
||
* @param unit 时间单位
|
||
*/
|
||
explicit ScopedTimer(
|
||
std::string name = "ScopedTimer",
|
||
bool auto_print = true,
|
||
TimeUnit unit = TimeUnit::MILLISECONDS
|
||
) : name_(std::move(name)),
|
||
auto_print_(auto_print),
|
||
unit_(unit),
|
||
start_(Clock::now()) {
|
||
}
|
||
|
||
/**
|
||
* @brief 析构函数 - 停止计时并可选打印结果
|
||
*/
|
||
~ScopedTimer() {
|
||
if (!stopped_) {
|
||
stop();
|
||
if (auto_print_) {
|
||
print();
|
||
}
|
||
}
|
||
}
|
||
|
||
// 禁止拷贝和移动
|
||
ScopedTimer(const ScopedTimer&) = delete;
|
||
ScopedTimer& operator=(const ScopedTimer&) = delete;
|
||
ScopedTimer(ScopedTimer&&) = delete;
|
||
ScopedTimer& operator=(ScopedTimer&&) = delete;
|
||
|
||
/**
|
||
* @brief 手动停止计时
|
||
*/
|
||
void stop() {
|
||
if (!stopped_) {
|
||
end_ = Clock::now();
|
||
stopped_ = true;
|
||
elapsed_ns_ = std::chrono::duration_cast<Duration>(end_ - start_).count();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 获取已经过的时间(纳秒)
|
||
*/
|
||
double elapsed_ns() const {
|
||
if (stopped_) {
|
||
return static_cast<double>(elapsed_ns_);
|
||
}
|
||
else {
|
||
auto now = Clock::now();
|
||
return static_cast<double>(
|
||
std::chrono::duration_cast<Duration>(now - start_).count()
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 获取已经过的时间(指定单位)
|
||
*/
|
||
double elapsed(TimeUnit unit = TimeUnit::MILLISECONDS) const {
|
||
return Statistics::convert_time(elapsed_ns(), unit);
|
||
}
|
||
|
||
/**
|
||
* @brief 打印耗时
|
||
*/
|
||
void print() const {
|
||
const char* unit_str = Statistics::time_unit_string(unit_);
|
||
log_info("{}: {:.3f} {}", name_, elapsed(unit_), unit_str);
|
||
}
|
||
|
||
/**
|
||
* @brief 重置计时器
|
||
*/
|
||
void reset() {
|
||
start_ = Clock::now();
|
||
stopped_ = false;
|
||
elapsed_ns_ = 0;
|
||
}
|
||
|
||
private:
|
||
std::string name_; ///< 计时器名称
|
||
bool auto_print_; ///< 是否自动打印
|
||
TimeUnit unit_; ///< 时间单位
|
||
TimePoint start_; ///< 开始时间
|
||
TimePoint end_; ///< 结束时间
|
||
bool stopped_{false}; ///< 是否已停止
|
||
int64_t elapsed_ns_{0}; ///< 已过时间(纳秒)
|
||
};
|
||
|
||
/**
|
||
* @brief 吞吐量测量器
|
||
*
|
||
* 用于测量操作的吞吐量(ops/sec)
|
||
*
|
||
* @example
|
||
* ThroughputMeter meter("写入操作");
|
||
* meter.start();
|
||
* for (int i = 0; i < 1000; ++i) {
|
||
* // 执行操作...
|
||
* meter.record_operation();
|
||
* }
|
||
* meter.stop();
|
||
* meter.print_report();
|
||
*/
|
||
class ThroughputMeter {
|
||
public:
|
||
using Clock = std::chrono::high_resolution_clock;
|
||
using TimePoint = Clock::time_point;
|
||
|
||
/**
|
||
* @brief 构造函
|
||
* @param name 测量器名
|
||
*/
|
||
explicit ThroughputMeter(std::string name = "ThroughputMeter") : name_(std::move(name)) {
|
||
}
|
||
|
||
/**
|
||
* @brief 开始测量
|
||
*/
|
||
void start() {
|
||
start_time_ = Clock::now();
|
||
operation_count_ = 0;
|
||
running_ = true;
|
||
log_module_debug("ThroughputMeter", "开始测量: {}", name_);
|
||
}
|
||
|
||
/**
|
||
* @brief 停止测量
|
||
*/
|
||
void stop() {
|
||
if (!running_) {
|
||
return;
|
||
}
|
||
|
||
end_time_ = Clock::now();
|
||
running_ = false;
|
||
|
||
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||
end_time_ - start_time_);
|
||
elapsed_seconds_ = duration.count() / 1000000000.0;
|
||
|
||
if (elapsed_seconds_ > 0.0) {
|
||
throughput_ = operation_count_ / elapsed_seconds_;
|
||
}
|
||
|
||
log_module_debug("ThroughputMeter", "停止测量: {}", name_);
|
||
}
|
||
|
||
/**
|
||
* @brief 记录一次操作
|
||
* @param count 操作数量(默认1)
|
||
*/
|
||
void record_operation(size_t count = 1) {
|
||
operation_count_ += count;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取当前吞吐量(ops/sec)
|
||
* @note 如果仍在运行,返回当前时刻的吞吐量
|
||
*/
|
||
double get_throughput() const {
|
||
if (running_) {
|
||
auto now = Clock::now();
|
||
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||
now - start_time_);
|
||
double elapsed = duration.count() / 1000000000.0;
|
||
return (elapsed > 0.0) ? (operation_count_ / elapsed) : 0.0;
|
||
}
|
||
return throughput_;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取操作总数
|
||
*/
|
||
size_t get_operation_count() const {
|
||
return operation_count_;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取耗时(秒)
|
||
*/
|
||
double get_elapsed_seconds() const {
|
||
if (running_) {
|
||
auto now = Clock::now();
|
||
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||
now - start_time_);
|
||
return duration.count() / 1000000000.0;
|
||
}
|
||
return elapsed_seconds_;
|
||
}
|
||
|
||
/**
|
||
* @brief 打印吞吐量报告
|
||
*/
|
||
void print_report() const {
|
||
log_info("=== {} 吞吐量报告 ===", name_);
|
||
log_info("总操作数: {}", operation_count_);
|
||
log_info("耗时: {:.3f} 秒", get_elapsed_seconds());
|
||
log_info("吞吐量: {:.2f} ops/sec", get_throughput());
|
||
|
||
// 计算每个操作的平均延迟
|
||
if (operation_count_ > 0) {
|
||
double avg_latency_ms = (get_elapsed_seconds() * 1000.0) / operation_count_;
|
||
log_info("平均延迟: {:.3f} ms/op", avg_latency_ms);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 重置测量器
|
||
*/
|
||
void reset() {
|
||
operation_count_ = 0;
|
||
throughput_ = 0.0;
|
||
elapsed_seconds_ = 0.0;
|
||
running_ = false;
|
||
}
|
||
|
||
private:
|
||
std::string name_; ///< 测量器名称
|
||
TimePoint start_time_; ///< 开始时间
|
||
TimePoint end_time_; ///< 结束时间
|
||
size_t operation_count_{0}; ///< 操作计数
|
||
double throughput_{0.0}; ///< 吞吐量(ops/sec)
|
||
double elapsed_seconds_{0.0}; ///< 耗时(秒)
|
||
bool running_{false}; ///< 是否正在运行
|
||
};
|
||
|
||
/**
|
||
* @brief 延迟记录器
|
||
*
|
||
* 用于批量记录操作延迟并生成统计报告
|
||
*
|
||
* @example
|
||
* LatencyRecorder recorder("读取操作");
|
||
* for (int i = 0; i < 1000; ++i) {
|
||
* ScopedTimer timer("", false);
|
||
* // 执行操作...
|
||
* timer.stop();
|
||
* recorder.record(timer.elapsed_ns());
|
||
* }
|
||
* auto stats = recorder.get_statistics();
|
||
* stats.print_report();
|
||
*/
|
||
class LatencyRecorder {
|
||
public:
|
||
/**
|
||
* @brief 构造函数
|
||
* @param name 记录器名称
|
||
*/
|
||
explicit LatencyRecorder(std::string name = "LatencyRecorder") : name_(std::move(name)) {
|
||
}
|
||
|
||
/**
|
||
* @brief 记录一次延迟
|
||
* @param latency_ns 延迟(纳秒)
|
||
*/
|
||
void record(double latency_ns) {
|
||
latencies_.push_back(latency_ns);
|
||
}
|
||
|
||
/**
|
||
* @brief 获取统计数据
|
||
*/
|
||
Statistics get_statistics() {
|
||
Statistics stats(latencies_);
|
||
return stats;
|
||
}
|
||
|
||
/**
|
||
* @brief 打印统计报告
|
||
* @param unit 时间单位
|
||
*/
|
||
void print_report(TimeUnit unit = TimeUnit::MICROSECONDS) {
|
||
auto stats = get_statistics();
|
||
stats.print_report(unit, name_);
|
||
}
|
||
|
||
/**
|
||
* @brief 重置记录器
|
||
*/
|
||
void reset() {
|
||
latencies_.clear();
|
||
}
|
||
|
||
/**
|
||
* @brief 获取记录的延迟样本数量
|
||
*/
|
||
size_t sample_count() const {
|
||
return latencies_.size();
|
||
}
|
||
|
||
private:
|
||
std::string name_; ///< 记录器名称
|
||
std::vector<double> latencies_; ///< 延迟样本(纳秒)
|
||
};
|
||
} // namespace shm_test
|