QueryPerformanceFrequency在32/64位GCC下异常的原因及优化方案
QueryPerformanceCounter计时器在64位GCC下计时为0的问题分析与解决
问题根源:构造函数命名错误
你的代码存在致命C++语法错误:类定义为class qlTime,但编写的构造函数却是qTimeClass()——这不是构造函数,只是普通成员函数。编译器会自动为qlTime生成默认构造函数,导致:
- 全局对象
qTime初始化时,qFreq作为全局变量被默认初始化为0.0 - 后续计算
qMSec和qSec时,均是除以0,得到的NaN/无穷大强制转换为unsigned long int后变为0,这就是计时全为0的直接原因
32位GCC可能因宽松编译检查或测试版本中构造函数命名正确,才未暴露问题;而64位GCC严格遵循标准,直接触发了错误。你遇到的情况并非QueryPerformanceFrequency调用失败,而是构造逻辑根本没执行,频率值从未被正确设置。
临时修复的逻辑隐患
你在Update中通过qMSec==0触发重新获取频率,本质是利用初始值特性补做了一次初始化,但存在问题:
- 若程序运行极长时间(超出
unsigned long范围),qMSec可能溢出回到0,导致不必要的重复获取频率 - 未检查
QueryPerformanceFrequency的返回值,无法处理极端异常场景
更优解决方案
1. 修正构造函数命名,添加返回值检查
先修复构造函数命名错误,同时添加返回值检查确保频率获取成功:
class qlTime{ public: LARGE_INTEGER qTime_Start, qTime_End; unsigned long int qSec, qMSec; double qFreq; // 构造函数名与类名保持一致 qlTime(){ qSec = 0; qMSec = 0; qFreq = 0.0; LARGE_INTEGER freq; if (QueryPerformanceFrequency(&freq)) { qFreq = static_cast<double>(freq.QuadPart); } else { // 极端场景降级处理,使用默认1MHz频率 qFreq = 1000000.0; } QueryPerformanceCounter(&qTime_Start); } void qReset(){ QueryPerformanceCounter(&qTime_Start); } void Update(){ QueryPerformanceCounter(&qTime_End); const double elapsedTicks = qTime_End.QuadPart - qTime_Start.QuadPart; qMSec = static_cast<unsigned long>(elapsedTicks * 1000.0 / qFreq); qSec = static_cast<unsigned long>(elapsedTicks / qFreq); } } qTime;
2. 延迟初始化(懒加载)
若担心全局对象初始化时机问题(如程序启动时QueryPerformanceFrequency无法正常调用),可将频率获取延迟到第一次调用Update时:
class qlTime{ public: LARGE_INTEGER qTime_Start, qTime_End; unsigned long int qSec, qMSec; double qFreq; bool isFreqInitialized; qlTime() : qSec(0), qMSec(0), qFreq(0.0), isFreqInitialized(false) { // 先记录起始时间,频率等第一次Update再初始化 QueryPerformanceCounter(&qTime_Start); } void qReset(){ QueryPerformanceCounter(&qTime_Start); } void Update(){ if (!isFreqInitialized) { LARGE_INTEGER freq; if (QueryPerformanceFrequency(&freq)) { qFreq = static_cast<double>(freq.QuadPart); } else { qFreq = 1000000.0; } isFreqInitialized = true; } QueryPerformanceCounter(&qTime_End); const double elapsedTicks = qTime_End.QuadPart - qTime_Start.QuadPart; qMSec = static_cast<unsigned long>(elapsedTicks * 1000.0 / qFreq); qSec = static_cast<unsigned long>(elapsedTicks / qFreq); } } qTime;
3. 局部静态变量封装(线程安全,推荐)
若无需全局对象,可将计时器封装为函数内的局部静态变量,确保初始化时机正确(C++11及以上线程安全):
qlTime& GetGlobalTimer() { static qlTime timer; // 第一次调用时初始化 return timer; }
额外注意事项
- 编译时启用C++11及以上标准,避免线程安全问题
unsigned long int通常为32位,最大仅支持约71分钟计时,若需更长时间建议使用uint64_t类型- 计算时优先做乘法再除法,减少精度损失(如
(ticks * 1000) / freq比ticks / freq * 1000精度更高)
内容的提问来源于stack exchange,提问作者Tobi PL
相关产品推荐
相关产品推荐

