VS2015中C++ Chrono在32位/Debug模式下计时异常问题咨询
我之前在Visual Studio 2015里用std::chrono踩过几乎一模一样的坑,结合你的代码和现象,帮你梳理下问题根源和解决办法:
1. 未初始化成员变量引发的垃圾值问题
你的类里_start、_stop、_laptime这三个成员变量在构造函数里完全没初始化。在Debug模式下,VS会给未初始化的栈变量填充0xCC这类垃圾值;32位模式下内存布局的差异,会让这些垃圾值干扰最终的计时计算。虽然你的测试代码是先Start()再Stop(),理论上会覆盖这些变量,但Debug模式的内存检查或者编译器的一些隐性优化,还是可能触发异常结果。
修复方式:把所有成员变量都放到构造函数的初始化列表里初始化:
c_ChronoInfo() : _start(std::chrono::steady_clock::now()), _stop(std::chrono::steady_clock::now()), _totaltime(_zeroDuration), _laptime(_zeroDuration), _isPaused(true) { }
2. 编译器优化导致的指令重排/代码删除问题
在Release模式下,VS的优化器会对无副作用的代码下手:你的测试循环for (long long i = 0; i <= 100; ++i) { x = i; }里,x是局部变量且后续没被使用,编译器会直接把整个循环删掉,导致Start()和Stop()之间的时间几乎为0。更糟的是,优化器可能重排std::chrono::steady_clock::now()的调用顺序,让_stop的时间戳比_start还早,直接算出负数结果。
当你把_laptime改成局部变量时,编译器的优化会更激进——局部变量生命周期短,更容易被重排,这就是为什么64位Release模式也会出问题的原因。
修复方式:
- 给测试代码加副作用,阻止循环被优化:
volatile int x = 0; // 用volatile告诉编译器别优化x的赋值 for (long long i = 0; i <= 100; ++i) { x = i; } - 或者用内存屏障强制保证
now()的调用顺序:
在Start()和Stop()里添加内存屏障,避免指令重排:#include <atomic> // 需要包含这个头文件 void Start() { assert(_isPaused); _isPaused = false; std::atomic_thread_fence(std::memory_order_seq_cst); _start = std::chrono::steady_clock::now(); } void Stop() { assert(!_isPaused); std::atomic_thread_fence(std::memory_order_seq_cst); _stop = std::chrono::steady_clock::now(); _laptime = _stop - _start; _totaltime += _laptime; _isPaused = true; }
3. VS2015对std::chrono的兼容性bug
VS2015对C++11标准的std::chrono支持不算完善,尤其是32位模式下,steady_clock的底层实现可能存在精度或隐性溢出问题——虽然你的测试用例时间极短,但未初始化的变量刚好触发了这些bug。
替代方案:如果上面的修复还没解决问题,可以直接调用Windows原生APIQueryPerformanceCounter和QueryPerformanceFrequency,这其实就是VS里steady_clock的底层实现,反而更可靠:
#include <windows.h> #include <cassert> class c_ChronoInfo { private: LARGE_INTEGER _start; LARGE_INTEGER _stop; LARGE_INTEGER _frequency; double _totaltime; // 总时长(毫秒) bool _isPaused; public: c_ChronoInfo() : _totaltime(0.0), _isPaused(true) { QueryPerformanceFrequency(&_frequency); } ~c_ChronoInfo() {} void Start() { assert(_isPaused); _isPaused = false; QueryPerformanceCounter(&_start); } void Stop() { assert(!_isPaused); QueryPerformanceCounter(&_stop); const double laptime = (static_cast<double>(_stop.QuadPart - _start.QuadPart) / _frequency.QuadPart) * 1000.0; _totaltime += laptime; _isPaused = true; } inline double TotalMilliseconds() const { return _totaltime; } };
验证修复效果
修改后的测试代码,在32/64位、Debug/Release模式下都应该能输出正常的短时长:
c_ChronoInfo test; test.Start(); volatile int x = 0; for (long long i = 0; i <= 100; ++i) { x = i; } test.Stop(); std::cout << "test: " << test.TotalMilliseconds() << std::endl;
内容的提问来源于stack exchange,提问作者user3417339

