Loading... 最近项目升级到VS2017,无法通过调系统时间测试业务问题 ## 1 问题分析 调试发现工程中很多定时器和时间相关业务都是通过 `clock`函数实现,而 `clock`微软在VS2015及之后版本做了重新实现: - VS2015前,`clock`随系统时间变化 - VS2015及之后,`clock`不随系统时间变化 从Microsoft官方文档:[Microsoft C/C++ change history 2003 - 2015](https://docs.microsoft.com/en-us/cpp/porting/visual-cpp-change-history-2003-2015?view=msvc-170),截取的描述: > #### <time.h> > > - **clock** > > In previous versions, the [clock](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/clock?view=msvc-170) function was implemented using the Windows API [GetSystemTimeAsFileTime](https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime). With this implementation, the clock function was sensitive to the system time, and was thus not necessarily monotonic. The clock function has been reimplemented in terms of [QueryPerformanceCounter](https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter) and is now monotonic. 大致是说 `clock`以前使用 `GetSystemTimeAsFileTime`实现会对系统时间敏感,后续更改为使用 `QueryPerformanceCounter`实现 这时去看了下官方的clock实现(题主只有VS2008和VS2017) **VS2008 clock实现:** 代码在:`代码在:<VS2008安装目录>/VC/crt/src/clock.c`   **VS2017 clock实现:** 代码在:`<sdk目录>/ucrt/time/clock.cpp`  通过对比,可以看出和官方文档描述的一样,`clock`用 `QueryPerformanceCounter`重新实现了 ## 2 解决方案 自定义与 `clock`相同功能函数,并替换代码中的 `clock`调用,有两种实现方案,目前采取方案一 ### 2.1 方案一 使用静态变量特性初始化进程启动时间,每次获取 `clock`时用当前时间减启动时间 ```c++ #include <windows.h> #include <time.h> /* * 获取系统时间 */ static unsigned __int64 GetCurSystemTime() { FILETIME ct; ::GetSystemTimeAsFileTime(&ct); return (unsigned __int64)ct.dwLowDateTime + (((unsigned __int64)ct.dwHighDateTime) << 32); } // 进程启动时间 static unsigned __int64 g_ui64ProcessStartTime = GetCurSystemTime(); /* * clock函数实现 */ clock_t GetClock() { FILETIME ct; ::GetSystemTimeAsFileTime(&ct); unsigned __int64 ui64CurTime = (unsigned __int64)ct.dwLowDateTime + (((unsigned __int64)ct.dwHighDateTime) << 32); ui64CurTime -= g_ui64ProcessStartTime; return (clock_t)(ui64CurTime / 10000); } ``` **效率:** VS2017测试,CPU i7-4790: - `GetClock`:10w次/1ms - 系统提供的`clock`:10w次/2~3ms 可以看出比系统的 `clock`更快(其实也正常,看C++ crt代码就知道哪种更快) ### 2.2 方案二 用VS2008中的源码的实现方法,把初始化进程启动时间放到 `crt`初始化时获取,其它和方案一一样 ``` #include <windows.h> #include <time.h> // 自定义一个段 #pragma section(".CRT$XCTMY",long,read) typedef int(__cdecl *_PIFV)(void); static unsigned __int64 g_ui64ProcessStartTime; // 进程启动时间 /* * 初始化进程启动时间 */ int __cdecl InitProcessStartTime() { FILETIME ct; ::GetSystemTimeAsFileTime(&ct); g_ui64ProcessStartTime = (unsigned __int64)ct.dwLowDateTime + (((unsigned __int64)ct.dwHighDateTime) << 32); return 0; } // 将`__InitProcessStartTime`函数定义在自定义段中 __declspec(allocate(".CRT$XCTMY")) _PIFV __InitProcessStartTime = InitProcessStartTime; /* * clock函数实现 */ clock_t GetClock() { FILETIME ct; ::GetSystemTimeAsFileTime(&ct); unsigned __int64 ui64CurTime = (unsigned __int64)ct.dwLowDateTime + (((unsigned __int64)ct.dwHighDateTime) << 32); ui64CurTime -= g_ui64ProcessStartTime; return (clock_t)(ui64CurTime / 10000); } ``` ## 3 其它的思考 之前在想能否使用 `Windows`的 `timeGetTime`去实现 > timeGetTime:返回Windows启动到当前的时间(毫秒),每49.71天就会再绕回0 > > https://docs.microsoft.com/en-us/previous-versions/ms713418(v=vs.85) 但有个问题,Windows开启超过49.71天后会溢出,限制比较大 最后修改:2021 年 12 月 16 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 0