计时器精度:c clock( ) vs. WinAPI 的 QPC 或 timeGetTime( )

timer accuracy: c clock( ) vs. WinAPI's QPC or timeGetTime( )

本文关键字:QPC timeGetTime WinAPI vs clock 精度 计时器      更新时间:2023-10-16

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

我研究过c函数clock(),以及WinAPI的函数QPC和timeGetTime,我知道它们都依赖于硬件。

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

虽然更高的精度是首选,但我宁愿知道精度很差(500ms)并解释它,也不愿相信精度更好(1ms)但无法记录它。

有人对如何描述软件时钟的准确性有什么建议吗?

感谢

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

clock()、GetTickCount和timeGetTime()是从校准的硬件时钟派生的。分辨率不是很高,它们是由时钟滴答中断驱动的,默认情况下每秒滴答64次或每15.625毫秒滴答一次。您可以使用timeBeginPeriod()将其降低到1.0毫秒。准确性非常好,时钟是从NTP服务器校准的,你通常可以指望它在一个月内不会关闭超过一秒钟。

QPC的分辨率要高得多,在某些机器上总是优于一微秒,甚至只有半纳秒。然而,它的准确性很差,时钟源是从芯片组某处提取的频率。它没有经过校准,并且具有典型的电子公差。仅用于短时间间隔。

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

一个相当不错的计时器是从timeSetEvent()中获得的多媒体计时器。它的设计目的是为需要可靠定时器的程序提供良好的服务。你可以让它在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至20ppm的公差。更严格的公差通常有军用版本或根据要求提供。这个来源的偏差是5倍低于HPET。然而,它仍然是0.86秒/天。

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

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

有关计时器的更多信息:时钟和计时器电路。

对于QPC,你可以调用QueryPerformanceFrequency来获得它的更新速率。除非你使用time,否则你无论如何都会获得超过0.5秒的计时精度,但clock并不那么准确——通常是10毫秒的分段[尽管显然CLOCKS_PER_SEC标准化为100万,使数字看起来更准确]。

如果你沿着这些线做一些事情,你可以计算出你可以测量的间隙有多小[尽管在非常高的频率下,你可能无法注意到有多小,例如,每个时钟周期更新的时间戳计数器,读取它需要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时间]

在窗口中,clock()测量实际时间,与您的手表一样,而不仅仅是使用的CPU,因此等待用户输入的5秒算作5秒时间。我不确定它有多准确。

你会发现的另一个问题是,现代系统通常不太擅长可重复的计时——无论你做什么,操作系统、CPU和内存都会协同工作,让两次运行的时间相同,生活变得很痛苦。如今,CPU通常使用有意可变的时钟运行(允许其漂移约0.1-0.5%),以减少EMC的电磁辐射,(电磁兼容性)测试可以"偷偷溜出"密封良好的计算机机箱的尖峰。

换句话说,即使你能得到一个非常标准化的时钟,你的测试结果也会有所不同,这取决于你无能为力的其他因素。。。

总之,除非你想在表格中填写一个数字,要求你的时钟精度有一个ppm数字,而且这是一个政府表格,你不能不填写这些信息,否则我不完全相信知道用于测量时间的计时器本身的准确性是非常有用的。因为其他因素至少也会起到同样大的作用。