定时器精度:c clock()与WinAPI的QPC或timeGetTime()

时间:2013-08-21 00:07:52

标签: c++ c winapi timer

我想描述软件定时器的准确性。我不太关心它是如何准确的,但确实需要知道准确性是什么。

我已经研究了c函数clock()和WinAPI函数QPC和timeGetTime,我知道它们都依赖于硬件。

我正在测量一个可能需要大约5-10秒的过程,而且我的要求很简单:我只需要0.1秒精度(分辨率)。但我确实需要知道准确性是什么,最坏的情况。

虽然更优先考虑更准确,但我宁愿知道准确性差(500毫秒)并且考虑到它,而不是相信准确度更好(1毫秒)但不能记录它。

有没有人对如何表征软件时钟准确性有任何建议?

由于

3 个答案:

答案 0 :(得分:10)

您需要区分准确性,分辨率和延迟。

clock(),GetTickCount和timeGetTime()来自校准的硬件时钟。分辨率不是很好,它们由时钟节拍中断驱动,每秒默认为64次或每15.625毫秒一次。您可以使用timeBeginPeriod()将其降低到1.0毫秒。准确度非常好,时钟是从NTP服务器校准的,通常可以指望它在一个月内不会超过一秒钟。

QPC具有更高的分辨率,总是优于1微秒,在某些机器上只有半纳秒。然而,它的精度很差,时钟源是从某个地方的芯片组中拾取的频率。它没有经过校准,具有典型的电子公差。仅用于短时间间隔。

延迟是处理计时时最重要的因素。如果您无法以足够快的速度读取,则无法使用高精度的定时源。当您在受保护模式操作系统上以用户模式运行代码时,这始终是一个问题。总是有比代码更高优先级的代码。特别是设备驱动程序尤其是故障制造者,视频和音频驱动程序。您的代码也会被换出RAM,需要加载页面错误。在负载很重的机器上,无法运行数百毫秒的代码并不罕见。您需要将此故障模式考虑在您的设计中。如果您需要保证亚毫秒精度,那么只有具有实时优先级的内核线程才能为您提供。

一个相当不错的计时器是你从timeSetEvent()获得的多媒体计时器。它旨在为需要可靠计时器的程序提供良好的服务。你可以在1毫秒内打勾,它会在可能的情况下赶上延迟。请注意,它是一个异步计时器,回调是在一个单独的工作线程上进行的,所以你必须小心处理正确的线程同步。

答案 1 :(得分:1)

既然你已经要求提供确凿的事实,那么它们就是:

控制HPETs的典型频率设备是CB3LV-3I-14M31818 它规定了在-40°C和+ 85°C之间的+/- 50ppm的频率稳定性。 更便宜的筹码是CB3LV-3I-66M6660。该器件在-20°C和70°C之间的频率稳定性为+/- 100 ppm。

如您所见,50至100ppm将导致50至100 us / s,180至360 ms /小时或4.32至8.64 s /天的漂移!

控制RTC的设备通常会更好一些:RV-8564-C2 RTC 模块提供+/- 10至20 ppm的公差。更严格的公差通常以军事版本或要求提供。该来源的偏差是5倍 少于HPET。但是,它仍然是0.86秒/天。

以上所有值均为数据表中指定的最大值。如我的评论所述,典型值可能要小得多,它们在几个ppm的范围内。

频率值也伴随着热漂移。 QueryPerformanceCounter()的结果可能受到使用ACPI电源管理定时器芯片(example)运行的系统的热漂移的严重影响。

有关计时器的更多信息:Clock and Timer Circuits

答案 2 :(得分:0)

对于QPC,您可以致电QueryPerformanceFrequency以获取其更新的费率。除非您使用的是time,否则无论如何都会获得超过0.5秒的计时准确度,但clock并不是那么准确 - 通常是10毫秒段[虽然显然CLOCKS_PER_SEC已标准化为100万,使数字APPEAR更准确]。

如果你沿着这些方向做某事,你可以弄清楚你可以测量的间隙有多小[虽然在极高的频率下你可能无法注意到有多小,例如时间戳计数器更新每个时钟周期,读取它需要20-40个时钟周期]:

 time_t t, t1;

 t = time();
 // wait for the next "second" to tick on. 
 while(t == (t1 = time()))  /* do nothing */ ;

 clock_t old = 0;
 clock_t min_diff = 1000000000;
 clock_t start, end;
 start = clock();
 int count = 0;
 while(t1 == time())
 {
    clock_t c = clock();
    if (old != 0 && c != old)
    {
       count ++;
       clock_t diff;
       diff = c - old;
       if (min_diff > diff) min_diff = diff;
    }
    old = c;
}
end = clock();
cout << "Clock changed " << count << " times" << endl;
cout << "Smallest differece " << min_diff << " ticks" << endl;
cout << "One second ~= " << end - start << " ticks" << endl; 

显然,你可以将同样的原则应用于其他时间源。

(没有经过编译测试,但希望不会错过错字和错误)

编辑: 因此,如果您测量的时间在10秒范围内,那么以100Hz运行的计时器将为您提供1000“滴答”。但它可能是999或1001,取决于你的运气而你正好/错误地捕获它,因此那里的2000 ppm - 那么时钟输入也可能会有所不同,但它的变化要小得多〜最多100ppm。对于Linux,clock()以100Hz更新(运行操作系统的实际计时器可能以更高的频率运行,但Linux中的clock()将以100Hz或10ms的间隔更新[并且它仅在更新时更新正在使用CPU,因此等待用户输入的5秒钟是0时间。

在Windows中,clock()测量实际时间,与手表相同,而不仅仅是正在使用的CPU,因此等待用户输入的5秒计为5秒。我不确定它有多准确。

你会发现另一个问题是现代系统一般都不能很好地处理可重复的时间 - 无论你做什么,操作系统,CPU和内存都密谋在一起,让生活变得痛苦两次运行的时间量。如今,CPU经常使用有意的可变时钟(允许漂移约0.1-0.5%)以减少电磁辐射(EMC,电磁兼容性)测试尖峰,可以“偷偷溜出”那个密封良好的电脑盒。

换句话说,即使您可以获得非常标准化的时钟,您的测试结果也会有所不同,具体取决于您无法做任何事情的其他因素......

总之,除非您正在寻找一个数字来填写一个表格,要求您的时钟精度达到ppm编号,并且这是一个政府表格,您不能将这些信息填入,我是并不完全相信知道用于测量时间本身的计时器的准确性是非常有用的。因为其他因素将至少扮演重要角色。