我正在使用 QueryPerformanceCounter ()函数来测量我的应用程序中的时间。此功能还用于提供我的应用寿命的时间线。最近我注意到其他时间功能的时间有所不同。最后,我写了一个小测试来检查漂移是否真实。 (我使用VS2013编译器)
#include <Windows.h>
#include <chrono>
#include <thread>
#include <cstdio>
static LARGE_INTEGER s_freq;
using namespace std::chrono;
inline double now_WinPerfCounter()
{
LARGE_INTEGER tt;
if (TRUE != ::QueryPerformanceCounter(&tt))
{
printf("Error in QueryPerformanceCounter() - Err=%d\n", GetLastError());
return -1.;
}
return (double)tt.QuadPart / s_freq.QuadPart;
}
inline double now_WinTick64()
{
return (double)GetTickCount64() / 1000.;
}
inline double now_WinFileTime()
{
FILETIME ft;
::GetSystemTimeAsFileTime(&ft);
long long * pVal = reinterpret_cast<long long *>(&ft);
return (double)(*pVal) / 10000000. - 11644473600LL;
}
int _tmain(int argc, _TCHAR* argv[])
{
if (TRUE != ::QueryPerformanceFrequency(&s_freq))
{
printf("Error in QueryPerformanceFrequency() - Err=#d\n", GetLastError());
return -1;
}
// save all timetags at the beginning
double t1_0 = now_WinPerfCounter();
double t2_0 = now_WinTick64();
double t3_0 = now_WinFileTime();
steady_clock::time_point t4_0 = steady_clock::now();
for (int i = 0;; ++i) // forever
{
double t1 = now_WinPerfCounter();
double t2 = now_WinTick64();
double t3 = now_WinFileTime();
steady_clock::time_point t4 = steady_clock::now();
printf("%03d\t %.3lf %.3lf %.3lf %.3lf \n",
i,
t1 - t1_0,
t2 - t2_0,
t3 - t3_0,
duration_cast<nanoseconds>(t4 - t4_0).count() * 1.e-9
);
std::this_thread::sleep_for(std::chrono::seconds(10));
}
return 0;
}
输出令人困惑:
000 0.000 0.000 0.000 0.000
...
001 10.001 10.000 10.002 10.002
...
015 150.006 150.010 150.010 150.010
...
024 240.009 240.007 240.015 240.015
...
025 250.010 250.007 250.015 250.015
...
026 260.010 260.007 260.016 260.016
...
070 700.027 700.039 700.041 700.041
为什么会有区别?使用不同的API函数时,看起来一秒的持续时间不一样?此外,在一天中,差异不是恒定的......
答案 0 :(得分:2)
这是正常的,经济实惠的时钟永远不会有无限精度。默认情况下,会定期从Internet时间服务器time.windows.com重新校准GetSystemTime()和GetTickCount()。它使用一个负担不起的原子钟来保持时间。为了避免软件受到心脏病发作,逐渐调整以便赶上或减慢。他们报告的时间将离开地球的旋转最多几秒钟,定期重新校准会限制长期漂移误差。
但是QPF没有校准,其分辨率太高,不允许这种调整不影响它通常用于的短间隔测量。它源自芯片组上可用的频率源,由系统构建者选择。根据典型的电子部件公差,温度尤其会影响精度并导致较长时间内的可变漂移。因此,QPF仅适用于测量相对较短的间隔。
两个时钟不可避免地不一样,你会看到它们分开。你必须选择一个或另一个作为“主”时钟。如果长期准确性很重要,那么你必须选择校准源,如果高分辨率但不精确到QPF。如果两者都很重要,那么您需要自定义硬件,如GPS时钟。
答案 1 :(得分:1)
编辑:您观察到的漂移的主要原因是性能计数器频率的不准确性。系统为您提供QueryPerformanceFrequency
返回的常量值。该恒定频率仅接近真实频率。它伴随着偏移和可能的漂移。现代平台(Windows&gt; 7,不变TSC)几乎没有偏移和小漂移。
示例:如果使用性能计数器频率将性能计数器值缩放到时间,则5 ppm偏移将导致时间向前移动5 us / s或比预期快432 ms /天。
常规强>
QueryPerformanceCounter
和GetSystemTimeAsFileTime
正在使用不同的资源(硬件)。现代平台从CPU时间戳计数器(TSC)派生QueryPerformanceCounter
,GetSystemTimeAsFileTime
使用PIT计时器,ACPI PM计时器或HPET硬件。有关详细信息,请参阅Intel 64® and IA-32 Architectures Software Developer's Manual, Volume 3B: System Programming Guide, Part 2。
当两个API使用不同的硬件时,处理漂移是不可避免的。但是,您可以扩展测试代码以校准漂移。
频率产生硬件通常对温度敏感。因此,漂移可以根据负载而变化。看到
The Windows Timestamp Project了解详情。
答案 2 :(得分:0)
可能不同的功能以不同的方式衡量时间。
QueryPerformanceCounter - 是一个高分辨率的时间戳或测量时间间隔。
GetSystemTimeAsFileTime - 以UTC格式检索当前系统日期和时间。
因此,使用此函数并比较它们的时间并不完全正确。
答案 3 :(得分:0)
使用多种测量方法可能是一个坏主意;不同的功能有不同的分辨率,你可以有效地看到来自值四舍五入到它们精度的噪音。当然,设置t1..t4会花费少量时间,因为函数需要时间来执行,所以漂移是不可避免的。
答案 4 :(得分:0)
您可能希望查看GetSystemTimePreciseAsFileTime API。它具有高精度,可以从外部时间服务器定期调整。