gettimeofday()是否保证为微秒分辨率?
我正在将一个最初为Win32 API编写的游戏移植到Linux(嗯,将Win32端口的OS X端口移植到Linux)。
自程序启动以来,我通过提供uSeconds来实现QueryPerformanceCounter:
BOOL QueryPerformanceCounter(LARGE_INTEGER* performanceCount) { gettimeofday(¤tTimeVal, NULL); performanceCount->QuadPart = (currentTimeVal.tv_sec - startTimeVal.tv_sec); performanceCount->QuadPart *= (1000 * 1000); performanceCount->QuadPart += (currentTimeVal.tv_usec - startTimeVal.tv_usec); return true; }
再加上QueryPerformanceFrequency()给出一个恒定的1000000作为频率,在我的机器上运行良好,自程序启动以来给了我一个包含uSeconds
的64位变量。
这是便携式的吗?我不想发现以某种方式或者类似方式编译内核时,它的工作原理有所不同。我可以将它移植到Linux以外的其他软件上,这很好。
解决方案:
根据我的经验以及通过互联网阅读的内容,答案是"不",不能保证。它取决于CPU速度,操作系统,Linux的风格等。
可能是。但是你有更大的问题。如果系统上的某些进程更改了计时器(例如ntpd),则gettimeofday()可能导致错误的计时。但是,在"普通" Linux上,我相信gettimeofday()
的分辨率为10us。因此,它可以根据系统上运行的进程向前和向后跳转,也可以在时间上跳转。这有效地回答了问题。
我们应该查看clock_gettime(CLOCK_MONOTONIC)
中的时间间隔。由于诸如多核系统和外部时钟设置之类的问题,它遇到的问题要少一些。
另外,查看Clock_getres()函数。
The actual resolution of gettimeofday() depends on the hardware architecture. Intel processors as well as SPARC machines offer high resolution timers that measure microseconds. Other hardware architectures fall back to the system’s timer, which is typically set to 100 Hz. In such cases, the time resolution will be less accurate.
我从高分辨率时间测量和计时器(第一部分)获得了此答案
英特尔处理器的高分辨率,低开销时序
如果我们使用的是Intel硬件,则以下是读取CPU实时指令计数器的方法。它会告诉我们自处理器启动以来执行的CPU周期数。这可能是我们可以用来进行性能评估的最细粒度的计数器。
请注意,这是CPU周期数。在Linux上,我们可以从/ proc / cpuinfo获取CPU速度,然后除以秒数。将其转换为双精度非常方便。
当我在盒子上运行它时,我得到
11867927879484732 11867927879692217 it took this long to call printf: 207485
这是英特尔开发人员指南,其中提供了大量详细信息。
#include <stdio.h> #include <stdint.h> inline uint64_t rdtsc() { uint32_t lo, hi; __asm__ __volatile__ ( "xorl %%eax, %%eax\n" "cpuid\n" "rdtsc\n" : "=a" (lo), "=d" (hi) : : "%ebx", "%ecx"); return (uint64_t)hi << 32 | lo; } main() { unsigned long long x; unsigned long long y; x = rdtsc(); printf("%lld\n",x); y = rdtsc(); printf("%lld\n",y); printf("it took this long to call printf: %lld\n",y-x); }
So it says microseconds explicitly, but says the resolution of the system clock is unspecified. I suppose resolution in this context means how the smallest amount it will ever be incremented?
数据结构被定义为以微秒为度量单位,但这并不意味着时钟或者操作系统实际上能够精确地进行测量。
就像其他人建议的那样," gettimeofday()"很糟糕,因为设置时间可能会导致时钟偏斜并影响计算。我们想要的是Clock_gettime(CLOCK_MONOTONIC),而Clock_getres()会告诉我们时钟的精度。
@伯纳德:
I have to admit, most of your example went straight over my head. It does compile, and seems to work, though. Is this safe for SMP systems or SpeedStep?
这是一个很好的问题……我认为代码还可以。
从实际的角度来看,我们每天都在我的公司中使用它,
而且我们运行在各种不同的设备上,从2到8个内核。
当然是YMMV等,但这似乎是可靠且费用低廉的
(因为它不会使上下文切换到系统空间)方法
时机。
我们可能对Clock_gettime(CLOCK_REALTIME)的Linux FAQ感兴趣。
通常它是如何工作的:
具体说明:
乱序执行可能会导致错误的结果,因此我们执行
" cpuid"指令除了可以为我们提供一些信息之外
关于CPU的信息也会同步任何乱序的指令执行。
大多数操作系统在启动时会同步CPU上的计数器,因此
答案在几纳秒内就可以了。
冬眠的评论可能是正确的,但实际上
可能不在乎跨越休眠边界的时间。
关于速度步伐:较新的Intel CPU补偿了速度
更改并返回调整后的计数。我快速浏览了一下
我们网络上的一些盒子,只发现一个盒子
没有它:运行某些旧数据库服务器的Pentium 3.
(这些是linux机器,所以我检查了:grep constant_tsc / proc / cpuinfo)
我不确定AMD CPU,我们主要是英特尔商店,
尽管我知道我们的一些低级系统专家做了
AMD评估。
希望这能满足好奇心,这很有趣,而且(恕我直言)
未被充分研究的编程领域。你知道杰夫和乔尔什么时候
谈论程序员是否应该了解C?我曾是
对他们大喊:"嘿,忘记那些高级C东西了……汇编器
如果我们想知道计算机是什么,则应该学习
正在做!"
Wine实际上是使用gettimeofday()实现QueryPerformanceCounter()的,众所周知,它可以使许多Windows游戏在Linux和Mac上运行。
启动http://source.winehq.org/source/dlls/kernel32/cpu.c#L312
导致http://source.winehq.org/source/dlls/ntdll/time.c#L448
在SMP系统中,读取RDTSC是不可靠的,因为每个CPU都维护自己的计数器,并且不能保证每个计数器都相对于另一个CPU同步。
我可能建议尝试使用" clock_gettime(CLOCK_REALTIME)"。 posix手册指示应在所有兼容的系统上实施。它可以提供十亿分之一秒的计数,但是我们可能希望检查系统上的" clock_getres(CLOCK_REALTIME)",以了解实际的分辨率是多少。
分块数量不匹配