Files
Alicho/tests/helpers/performance_timer.h

525 lines
13 KiB
C++
Raw Permalink 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.
#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