我正在将一个最初为Win32 API编写的游戏移植到Linux(很好,是将Win32端口的OS X端口移植到Linux)。
自进程启动以来,我已经通过给出uSeconds实现了QueryPerformanceCounter:
1 2 3 4 5 6 7 8 9
| 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以外的其他软件上,这很好。
也许。但是你有更大的问题。如果系统上的某些进程更改了计时器(例如ntpd),则gettimeofday()可能会导致错误的计时。但是,在"普通" Linux上,我相信gettimeofday()的分辨率为10us。因此,它可以根据系统上运行的进程向前和向后跳转,并可以跳转。有效地回答了您的问题。
您应该查看clock_gettime(CLOCK_MONOTONIC)中的时间间隔。由于诸如多核系统和外部时钟设置之类的问题,它遇到的问题更少。
另外,查看clock_getres()函数。
英特尔处理器的高分辨率,低开销时序
如果您使用的是英特尔硬件,请按照以下方法读取CPU实时指令计数器。它会告诉您自处理器启动以来执行的CPU周期数。这可能是您可以用来进行性能评估的最细粒度的计数器。
请注意,这是CPU周期数。在Linux上,您可以从/ proc / cpuinfo获取CPU速度,然后除以秒数。将其转换为双精度非常方便。
当我在盒子上运行它时,我得到
1 2 3
| 11867927879484732
11867927879692217
it took this long to call printf: 207485 |
这是英特尔开发人员指南,提供了大量细节。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <stdio.h>
#include <stdint.h>
inline uint64_t rdtsc() {
uint32_t lo, hi;
__asm__ __volatile__ (
"xorl %%eax, %%eax
"
"cpuid
"
"rdtsc
"
:"=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
",x);
y = rdtsc();
printf("%lld
",y);
printf("it took this long to call printf: %lld
",y-x);
} |
@伯纳德:
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等,但这似乎是可靠且费用低廉的
(因为它不会使上下文切换到系统空间)方法
时机。
通常它是如何工作的:
-
声明代码块为汇编程序(易失,因此
优化器将不理会它)。
-
执行CPUID指令。除了获取一些CPU信息
(我们不做任何事情)它同步CPU的执行缓冲区
这样时序就不会受到乱序执行的影响。
-
执行rdtsc(读取时间戳)执行。这获取了
自重置处理器以来执行的机器周期。这是一个64位
值,因此以当前的CPU速度,它将每194年左右出现一次。
有趣的是,在原始的Pentium参考中,他们注意到它包裹了每个
5800年左右。
-
最后两行将寄存器中的值存储到
变量hi和lo,然后将其放入64位返回值中。
具体说明:
-
乱序执行可能会导致错误的结果,因此我们执行
" cpuid"指令除了可以给您一些信息
关于CPU的信息也会同步任何乱序的指令执行。
-
大多数操作系统在启动时会同步CPU上的计数器,因此
答案在几纳秒内就可以了。
-
冬眠的评论可能是对的,但实际上
可能不在乎跨越休眠边界的时间。
-
关于速度步伐:较新的Intel CPU补偿速度
更改并返回调整后的计数。我快速浏览了一下
我们网络上的一些盒子,只发现一个盒子
没有它:运行某些旧数据库服务器的Pentium 3。
(这些是linux机器,所以我检查了:grep constant_tsc / proc / cpuinfo)
-
我不确定AMD CPU,主要是英特尔商店,
尽管我知道我们的一些低级系统专家做了
AMD评估。
希望这能满足您的好奇心,这很有趣,而且(恕我直言)
研究不足的编程领域。你知道杰夫和乔尔什么时候
谈论程序员是否应该了解C?我曾是
对他们大喊:"嘿,忘记那些高级C东西了……汇编器
如果您想知道计算机是什么,则应该学习
做!"
您可能对clock_gettime(CLOCK_REALTIME)的Linux常见问题解答感兴趣
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
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()将告诉您时钟的精度。
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.
我从高分辨率时间测量和计时器(第一部分)获得了此答案
这个答案提到了时钟调整的问题。在C ++ 11中,使用库解决了保证滴答单位的问题和调整时间的问题。
保证时钟std::chrono::steady_clock不被调整,而且时钟将以相对于实时的恒定速率前进,因此诸如SpeedStep之类的技术一定不能影响它。
您可以通过转换为std::chrono::duration专业化之一(例如std::chrono::microseconds)来获得类型安全单元。使用此类型时,刻度值使用的单位没有任何歧义。但是,请记住,时钟不一定具有此分辨率。您可以将持续时间转换为阿秒,而实际上并不需要精确的时钟。
根据我的经验以及通过互联网阅读的内容,答案是"不",不能保证。它取决于CPU速度,操作系统,Linux的风格等。
在SMP系统中,读取RDTSC是不可靠的,因为每个CPU都维护自己的计数器,并且不能保证每个计数器都相对于另一个CPU同步。
我可能建议尝试clock_gettime(CLOCK_REALTIME)。 posix手册指示应在所有兼容的系统上实施。它可以提供十亿分之一秒的计数,但是您可能需要检查系统上的clock_getres(CLOCK_REALTIME)以查看实际分辨率是多少。