#ifndef CTICK2_H #define CTICK2_H #include "GdCPP_Exports.h" #include #include #include /// @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(&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