Files
GdCpp12/include/CTic.h

230 lines
10 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.
#ifndef CTICK2_H
#define CTICK2_H
#include "GdCPP_Exports.h"
#include <stdint.h>
#include <Windows.h>
#include <iostream>
/// @brief 在不同的时间点(电脑开机,程序启动,实例启动等)、精度单位(s, ms, us等下计算时间差。
class GDCPP_API CTic
{
// ------ 静态变量和静态成员函数 ------
public:
/// 初始化获取时钟频率和起始值只需要在App或Dll初始化时调用一次。
static bool Init();
// ------ 静态变量必须调用一次Init()确定 -----
/// init()初始化时检测到的系统高精度定时器的频率
static int64_t Freq; //1Hz
// 以下是由Freq转换出来的不同单位的频率供计算不同单位的延迟时选用。
// 减少一个乘法,目的是减少运算量并防止乘法溢出。
static int64_t Freq_1k; //1kHz用于计算1ms的时间差
static int64_t Freq_10k; //10kHz, 用于计算100us的时间差
static int64_t Freq_100k; //100kHz, 用于计算10us以下的时间差。目前假设时钟是100k的整数倍
// 以下用于将时钟数转换成频率。
static int64_t Freq_M10; //0.1Hz
static int64_t Freq_M100; //0.2Hz
/// 记录Init()时的时间戳,当做启动软件时的时间
static int64_t AppStartTick;
/// 获取高精度时钟原始值,保存到指定变量
static inline void QueryCnt(int64_t& t) { QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&t)); }
/// 返回高精度时钟原始值
static inline int64_t QueryCnt() { int64_t t; QueryCnt(t); return t; }
// 四舍五入除法,适合要求更高精度的场合
//static inline int64_t _div(int64_t t, int64_t f) { return (t + f / 2) / f; };
// 截尾除法,运算量最小
static inline int64_t _div(int64_t t, int64_t f) { return t / f; }
/// 延迟一小段时间。延迟过程中不挂起线程长延迟请用Sleep()挂起线程
static void Delay (uint32_t ms) { int64_t start = QueryCnt(); while (((QueryCnt() - start) / Freq_1k) < int(ms)); }
static void Delay_us(uint32_t us) { int64_t start = QueryCnt(); while (((QueryCnt() - start) * 1000 / Freq_1k) < int(us)); }
/// @brief 由时间差计算频率单位分别为1Hz, 0.1Hz, 0.01Hz。
static inline int64_t cnt2freq(int64_t t) { return _div(Freq, t); }
static inline int64_t cnt2freq_p10(int64_t t) { return _div(Freq_M10, t); }
static inline int64_t cnt2freq_p100(int64_t t) { return _div(Freq_M100, t); }
/// 当前计数器原始值
int64_t Cnt;
/// 默认构造函数,获取当前高精度时钟值。
inline CTic() { QueryCnt(Cnt);}
/// @brief 带参数的构造函数,通常复制另一个时钟的值。
/// @param t 不能是负数
inline CTic(int64_t t) : Cnt(t) { _ASSERT(t >= 0); }
inline CTic(const CTic &s) : Cnt(s.Cnt){ }
/// 更新计数器值并返回原始值
inline int64_t Update() { QueryCnt(Cnt); return Cnt; }
// 时钟数转不同单位的时间
static inline int64_t to_s (int64_t t) { return _div(t, Freq); } // 时钟计数转时间单位为s
static inline int64_t to_ms (int64_t t) { return _div(t, Freq_1k); } // 时钟计数转时间单位为ms
static inline int64_t to_100us(int64_t t) { return _div(t, Freq_10k); } // 时钟计数转时间单位为100us
static inline int64_t to_10us (int64_t t) { return _div(t, Freq_100k);} // 时钟计数转时间单位为10us。测试算法到10us精度足够了
static inline int64_t to_us (int64_t t) { return _div(t*10, Freq_100k);} // 时钟计数转时间单位为us。 这一档开始比前面的运算量多一个乘法
static inline int64_t to_ns (int64_t t) { return _div(t*10000,Freq_100k);} // 时钟计数转时间单位为ns。 注意仅用于短时间的计算t太大乘法会溢出。
static inline int64_t ms2cnt(int64_t t) { return t * Freq_1k; } // 时间转时钟计数单位为ms
static inline int64_t us2cnt(int64_t t) { return _div(t * Freq_100k, 10); } // 时间转时钟计数单位为us
static inline int64_t ns2cnt(int64_t t) { return _div(t * Freq_100k, 10000);; } // 时间转时钟计数单位为ns
inline int64_t to_s() const { return to_s (Cnt); }
inline int64_t to_ms() const { return to_ms (Cnt); } // 普通计算用这一档
inline int64_t to_100us()const { return to_100us (Cnt); }
inline int64_t to_10us() const { return to_10us (Cnt); } // 测试算法到10us精度足够了
inline int64_t to_us() const { return to_us (Cnt); } // 这一档开始会多算一个乘法
inline int64_t to_ns() const { return to_ns (Cnt); } // 注意t不能太大乘法会溢出
// 这些是最常用的函数,获取两次访问之间的时间差,同时更新时间计数。
// 两次访问包括构造函数、Update()、elapse()、sincePowerOn()、sinceAppRun(),以及子类Tick2::sinceStart()
inline int64_t elapse_s() { auto bak = Cnt; QueryCnt(Cnt); return to_s (Cnt - bak); }
inline int64_t elapse() { auto bak = Cnt; QueryCnt(Cnt); return to_ms (Cnt - bak); }
inline int64_t elapse_100us(){ auto bak = Cnt; QueryCnt(Cnt); return to_100us(Cnt - bak); }
inline int64_t elapse_10us() { auto bak = Cnt; QueryCnt(Cnt); return to_10us (Cnt - bak); }
inline int64_t elapse_us() { auto bak = Cnt; QueryCnt(Cnt); return to_us (Cnt - bak); }
inline int64_t elapse_ns() { auto bak = Cnt; QueryCnt(Cnt); return to_ns (Cnt - bak); }
inline int64_t elapse_raw() { auto bak = Cnt; QueryCnt(Cnt); return Cnt - bak; }
/// 获取从电脑开机开始记的计时器值。
static inline int64_t PowerOn_s() { return to_s (QueryCnt()); }
static inline int64_t PowerOn() { return to_ms (QueryCnt()); }
static inline int64_t PowerOn_100us() { return to_100us(QueryCnt()); }
inline int64_t powerOn_s() { QueryCnt(Cnt); return to_s (); }
inline int64_t powerOn() { QueryCnt(Cnt); return to_ms (); }
inline int64_t powerOn_100us() { QueryCnt(Cnt); return to_100us(); }
/// 获取程序启动后执行Init()开始算起的时间。通常在App启动时调用Init(),因此可以等于程序启动后的时间
static inline int64_t AppRun_s() { return to_s (QueryCnt() - AppStartTick); }
static inline int64_t AppRun() { return to_ms (QueryCnt() - AppStartTick); }
static inline int64_t AppRun_100us() { return to_100us(QueryCnt() - AppStartTick); }
inline int64_t appRun_s() { QueryCnt(Cnt); return to_s (Cnt - AppStartTick); }
inline int64_t appRun() { QueryCnt(Cnt); return to_ms (Cnt - AppStartTick); }
inline int64_t appRun_100us() { QueryCnt(Cnt); return to_100us(Cnt - AppStartTick); }
inline int64_t since_s (const CTic& start) { QueryCnt(Cnt); return to_s (Cnt - start.Cnt); }
inline int64_t since (const CTic& start) { QueryCnt(Cnt); return to_ms (Cnt - start.Cnt); }
inline int64_t since_100us (const CTic& start) { QueryCnt(Cnt); return to_100us (Cnt - start.Cnt); }
// 两个实例相减,得到计数值的差值
inline int64_t operator -(const CTic& t) {return Cnt - t.Cnt;}
};
// 计数两个实例的时间差
static inline int64_t DiffTime_s (const CTic& start, const CTic& end) { return CTic::to_s (end.Cnt - start.Cnt); }
static inline int64_t DiffTime (const CTic& start, const CTic& end) { return CTic::to_ms (end.Cnt - start.Cnt); }
static inline int64_t DiffTime_100us(const CTic& start, const CTic& end) { return CTic::to_100us(end.Cnt - start.Cnt); }
static inline int64_t DiffTime_10us (const CTic& start, const CTic& end) { return CTic::to_10us (end.Cnt - start.Cnt); }
static inline int64_t DiffTime_us (const CTic& start, const CTic& end) { return CTic::to_us (end.Cnt - start.Cnt); }
static inline int64_t DiffTime_ns (const CTic& start, const CTic& end) { return CTic::to_ns (end.Cnt - start.Cnt); }
/// @brief 双计时器。应用场景:某个算法包含多个耗时的步骤,需要统计每一步的时间和总时间。
/// @detail 示例:
/// 1. 算法开头构造Tick2实例或Restart()现有实例
/// Tick2 t; //或t.Restart();
///
/// 2. 分步统计耗时
/// Step1(); // 步骤1
/// log(t.elapse()); // 记录步骤1耗时
/// Step2(); // 步骤2
/// log(t.elapse()); // 记录步骤2耗时
/// Step3(); // 步骤3
/// log(t.elapse()); // 记录步骤3耗时
///
/// 2. 统计总耗时
/// log(t.sinceStart());// 记录总耗时
class GDCPP_API CTic2 : public CTic
{
public:
inline CTic2()
: Start(Cnt) // 基类构造时用默认构造函数本类构造Start时用直接复制Cnt值避免再获取一次
{
}
inline CTic2(const CTic2 &s)
: Start(s.Start)
{
Cnt = s.Cnt;
}
/// @brief 保存起始时间
CTic Start;
/// @brief 重置两个定时器的值。
inline void Restart() {QueryCnt(Cnt); Start.Cnt = Cnt;}
/// 用于最近获取过时间,快速重置起始时间,
inline void fastRestart(){Start.Cnt = Cnt; }
/// 从构造实例或重置起经过了多少时间
inline int64_t sinceStart_s() { QueryCnt(Cnt); return to_s (Cnt - Start.Cnt); }
inline int64_t sinceStart() { QueryCnt(Cnt); return to_ms (Cnt - Start.Cnt); }
inline int64_t sinceStart_100us(){ QueryCnt(Cnt); return to_100us(Cnt - Start.Cnt); }
inline int64_t sinceStart_10us() { QueryCnt(Cnt); return to_10us (Cnt - Start.Cnt); }
inline int64_t sinceStart_us() { QueryCnt(Cnt); return to_us (Cnt - Start.Cnt); }
inline int64_t sinceStart_ns() { QueryCnt(Cnt); return to_ns (Cnt - Start.Cnt); }
};
/// @brief 在CTic基础上增加计算N个时间戳的差值的平均
class GDCPP_API CAvgDiffTick
: public CTic
{
public:
CAvgDiffTick(int n = 0)
: N(n)
{
if (n > 0) Buf = new int64_t[n];
}
~CAvgDiffTick()
{
delete[] Buf;
}
void setSize(int n) {
_ASSERT(n != 0);
if (n != N) {
if (Buf) delete[] Buf;
Buf = new int64_t[n];
N = n;
}
clear();
}
/// @brief 添加一个新值,返回差值的平均
int64_t add(int64_t newvalue);
inline int64_t add()
{
return add(Update());
}
void clear()
{
Cnt = 0;
index = 0;
}
int Avg() { return _Avg; }
// 测试代码
static void Test1(int avgnum = 4);
static void Test2(int avgnum = 4);
protected:
int64_t* Buf = nullptr; // 最近N个CTic的缓冲区
int64_t Cnt = 0; // 已经缓冲的计数
int index = 0; // 新增数据的下标
int N; // 平均次数 = 缓冲区长度
int _Avg = 0; // 最近一次的平均值
};
#endif // CTICK_H