fdl*_*dlm 10 c++ windows timing windows-7-x64
我GetSystemTimeAdjustment在Windows 7上使用该功能运行了一些测试,得到了一些我无法解释的有趣结果.正如我所理解的那样,如果系统时间定期同步,则该方法应该返回,如果是,则在哪个时间间隔以及更新它的增量(请参阅MSDN上的GetSystemTimeAdjustment函数).
从此我可以看出,如果我查询系统时间,例如GetSystemTimeAsFileTime重复使用,我应该不做任何更改(系统时钟尚未更新),或者是检索到的增量的倍数的更改GetSystemTimeAdjustment.问题一:这个假设是否正确?
现在考虑以下测试代码:
#include <windows.h>
#include <iostream>
#include <iomanip>
int main()
{
FILETIME fileStart;
GetSystemTimeAsFileTime(&fileStart);
ULARGE_INTEGER start;
start.HighPart = fileStart.dwHighDateTime;
start.LowPart = fileStart.dwLowDateTime;
for (int i=20; i>0; --i)
{
FILETIME timeStamp1;
ULARGE_INTEGER ts1;
GetSystemTimeAsFileTime(&timeStamp1);
ts1.HighPart = timeStamp1.dwHighDateTime;
ts1.LowPart = timeStamp1.dwLowDateTime;
std::cout << "Timestamp: " << std::setprecision(20) << (double)(ts1.QuadPart - start.QuadPart) / 10000000 << std::endl;
}
DWORD dwTimeAdjustment = 0, dwTimeIncrement = 0, dwClockTick;
BOOL fAdjustmentDisabled = TRUE;
GetSystemTimeAdjustment(&dwTimeAdjustment, &dwTimeIncrement, &fAdjustmentDisabled);
std::cout << "\nTime Adjustment disabled: " << fAdjustmentDisabled
<< "\nTime Adjustment: " << (double)dwTimeAdjustment/10000000
<< "\nTime Increment: " << (double)dwTimeIncrement/10000000 << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
它在循环中需要20个时间戳并将它们打印到控制台.最后,它打印系统时钟更新的增量.我希望循环中打印的时间戳之间的差异可以是0或此增量的倍数.但是,我得到这样的结果:
Timestamp: 0
Timestamp: 0.0025000000000000001
Timestamp: 0.0074999999999999997
Timestamp: 0.01
Timestamp: 0.012500000000000001
Timestamp: 0.014999999999999999
Timestamp: 0.017500000000000002
Timestamp: 0.022499999999999999
Timestamp: 0.025000000000000001
Timestamp: 0.0275
Timestamp: 0.029999999999999999
Timestamp: 0.032500000000000001
Timestamp: 0.035000000000000003
Timestamp: 0.040000000000000001
Timestamp: 0.042500000000000003
Timestamp: 0.044999999999999998
Timestamp: 0.050000000000000003
Timestamp: 0.052499999999999998
Timestamp: 0.055
Timestamp: 0.057500000000000002
Time Adjustment disabled: 0
Time Adjustment: 0.0156001
Time Increment: 0.0156001
Run Code Online (Sandbox Code Playgroud)
因此,似乎系统时间使用约0.0025秒的间隔更新,而不是0.0156秒作为返回GetSystemTimeAdjustment.
问题二:这是什么原因?
Arn*_*rno 26
该GetSystemTimeAsFileTimeAPI可以访问系统的挂钟文件时间格式.
64位FILETIME结构以100ns为单位接收系统时间作为FILETIME,自1601年1月1日起已经过期.该调用GetSystemTimeAsFileTime通常需要10 ns至15 ns.
为了研究此API提供的系统时间的真实准确性,需要讨论随时间值提供的粒度.换句话说:系统时间多久更新一次?隐藏的API调用提供了第一个估计值:
NTSTATUS NtQueryTimerResolution(OUT PULONG MinimumResolution,
OUT PULONG MaximumResolution,
OUT PULONG ActualResolution);
Run Code Online (Sandbox Code Playgroud)
NtQueryTimerResolution由本机Windows NT库NTDLL.DLL导出.此调用报告的ActualResolution表示系统时间的更新周期,以100 ns为单位,不一定与中断周期匹配.该值取决于硬件平台.常见的硬件平台为ActualResolution报告156,250或100,144 ; 较旧的平台可能会报告更多的数字; 更新的系统,特别是当支持HPET(高精度事件计时器)时constant/invariant TSC,可能会为ActualResolution返回156,001.
这是控制系统的心跳之一.该MinimumResolution和ActualResolution是相关的多媒体计时器的配置.
该ActualResolution可以使用API调用来设置
NTSTATUS NtSetTimerResolution(IN ULONG RequestedResolution,
IN BOOLEAN Set,
OUT PULONG ActualResolution);
Run Code Online (Sandbox Code Playgroud)
或通过多媒体计时器界面
MMRESULT timeBeginPeriod(UINT uPeriod);
Run Code Online (Sandbox Code Playgroud)
uPeriod的值来自允许的范围
MMRESULT timeGetDevCaps(LPTIMECAPS ptc, UINT cbtc );
Run Code Online (Sandbox Code Playgroud)
这填补了结构
typedef struct {
UINT wPeriodMin;
UINT wPeriodMax;
} TIMECAPS;
Run Code Online (Sandbox Code Playgroud)
wPeriodMin的典型值为1 ms,wPeriodMax的典型值为1,000,000 ms.
在这里查看最小值/最大值时,有一个令人遗憾的误解:
NtQueryTimerResolution指定分辨率.可获得的最低分辨率(MinimumResolution)在高达约20ms的范围内,而可获得的最高分辨率(MaximumResolution)可以是0.5ms.然而,0.5毫秒的结果是无法访问的timeBeginPeriod.多媒体计时器接口处理句点,NtQueryTimerResolution()处理分辨率(句点的倒数值).
总结: GetSystemTimeAdjustment不是要看的功能.此函数仅说明如何以及是否完成时间更改.根据多媒体计时器界面的设置,timeBeginPeriod可以更频繁地以较小的部分进行时间的进展.使用NtQueryTimerResolution接收的实际时间增量.请注意,多媒体计时器API的设置确实会影响这些值.(例如:媒体播放器播放视频时,时间越来越短.)
我在很大程度上诊断出窗口时间问题.一些结果可以在这里找到.
注意:时间调整:0.0156001清楚地标识系统上HPET和/或constant/invariant TSC系统上的Windows VISTA或更高版本.
实施:如果你想赶上时间转换:
FILETIME FileTime,LastFileTime;
long long DueTime,LastTime;
long FileTimeTransitionPeriod;
GetSystemTimeAsFileTime(&FileTime);
for (int i = 0; i < 20; i++) {
LastFileTime.dwLowDateTime = FileTime.dwLowDateTime;
while (FileTime.dwLowDateTime == LastFileTime.dwLowDateTime) GetSystemTimeAsFileTime(&FileTime);
// enough to just look at the low part to catch the transition
CopyMemory(&DueTime,&FileTime,sizeof(FILETIME));
CopyMemory(&LastTime,&LastFileTime,sizeof(FILETIME));
FileTimeTransitionPeriod = (long)(DueTime-LastTime);
fprintf(stdout,"transition period: % 7.4lf ms)\n",(double)(FileTimeTransitionPeriod)/10000);
}
// WARNING: This code consumes 100% of the cpu for 20 file time increments.
// At the standard file time increment of 15.625 ms this corresponds to 312.5ms!
Run Code Online (Sandbox Code Playgroud)
但是:当文件时间转换非常短(例如设置timeBeginPeriod(wPeriodMin))时,任何输出类似fprintf或std::cout可能会破坏结果,因为它会延迟循环.在这种情况下,我建议将20个结果存储在数据结构中,然后进行输出.
并且:文件时间转换可能并不总是相同.文件时间增量可能与更新周期不匹配.请参阅上面的链接以获取此行为的更多详细信息和示例.
编辑: 调用timeBeginPeriod时要小心,因为频繁的调用会显着影响系统时钟 MSDN.此行为适用于Windows版本7.
调用timeBeginPeriod/ timeEndPeriod或NtSetTimerResolution可能会将系统时间更改为ActualResolution.经常这样做会导致系统时间发生相当大的变化.但是,当在系统时间转换时或接近转换时进行调用时,偏差要小得多.对于像NTP客户端这样要求苛刻的应用程序,建议在调用上述函数之前轮询系统时间转换/增量.当系统时间进程中发生意外跳转时,很难与NTP服务器同步.
| 归档时间: |
|
| 查看次数: |
13568 次 |
| 最近记录: |