我正在将一个最初为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以外的东西。
答案 0 :(得分:55)
也许。但是你有更大的问题。如果系统上有进程更改计时器(即ntpd),gettimeofday()
会导致错误的计时。但是,在“正常”的Linux上,我相信gettimeofday()
的分辨率为10us。因此,它可以根据系统上运行的进程向前和向后跳转。这有效地回答了你的问题。
您应该查看clock_gettime(CLOCK_MONOTONIC)
的时间间隔。由于多核系统和外部时钟设置等原因,它会受到几个问题的影响。
另外,请查看clock_getres()
函数。
答案 1 :(得分:40)
英特尔处理器的高分辨率,低开销时序
如果您使用的是英特尔硬件,请按以下步骤阅读CPU实时指令计数器。它将告诉您自处理器启动以来执行的CPU周期数。这可能是您可以获得的最佳粒度计数器。
请注意,这是CPU周期数。在linux上,您可以从/ proc / cpuinfo获取CPU速度并除以获得秒数。将其转换为double是非常方便的。
当我在我的盒子上运行时,我得到了
11867927879484732
11867927879692217
it took this long to call printf: 207485
这是提供大量详细信息的Intel developer's guide。
#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);
}
答案 2 :(得分:18)
@Bernard:
我不得不承认,你的大多数例子都是我的头脑。它确实可以编译,但似乎也可以工作。这对SMP系统或SpeedStep是否安全?
这是一个很好的问题......我认为代码没问题。 从实际的角度来看,我们每天都在公司使用它, 我们在相当多的盒子上运行,所有盒子都是2-8芯。 当然,YMMV等,但它似乎是一个可靠和低开销 (因为它不会使上下文切换到系统空间)方法 时机。
一般来说它的工作原理是:
具体说明:
乱序执行会导致错误的结果,所以我们执行 “cpuid”指令除了给你一些信息外 关于cpu也会同步任何无序指令执行。
大多数操作系统在启动时会同步CPU上的计数器,所以 答案很好,只需几纳秒。
冬眠评论可能属实,但在实践中你 可能不关心冬眠边界的时间安排。
关于speedstep:较新的Intel CPU可以补偿速度 更改并返回调整后的计数。我做了一个快速扫描 我们网络上的一些盒子,只发现一个盒子 没有它:奔腾3运行一些旧的数据库服务器。 (这些是linux盒子,所以我查了一下:grep constant_tsc / proc / cpuinfo)
我不确定AMD CPU,我们主要是英特尔商店, 虽然我知道我们的一些低级系统专家做了一个 AMD评估。
希望这能满足你的好奇心,这是一个有趣的和(恕我直言) 未充分研究的编程领域。你知道Jeff和Joel什么时候 谈论程序员是否应该知道C?我曾是 对他们大喊大叫,“嘿,忘了高级C的东西......汇编 如果你想知道电脑是什么,你应该学习什么 这样做!“
答案 3 :(得分:14)
答案 4 :(得分:11)
Wine实际上使用gettimeofday()来实现QueryPerformanceCounter(),众所周知,许多Windows游戏都可以在Linux和Mac上运行。
答案 5 :(得分:9)
所以它明确地说微秒,但是说系统时钟的分辨率是未指定的。我想在这种情况下解决方案意味着它将增加的最小量是多少?
数据结构定义为以微秒为单位测量,但这并不意味着时钟或操作系统实际上能够精确测量。
与其他人的建议一样,gettimeofday()
很糟糕,因为设置时间会导致时钟偏差并导致计算失败。 clock_gettime(CLOCK_MONOTONIC)
就是你想要的,clock_getres()
会告诉你时钟的精确度。
答案 6 :(得分:8)
获得了这个答案gettimeofday()的实际分辨率取决于硬件架构。英特尔处理器和SPARC机器提供高分辨率的定时器,可以测量微秒。其他硬件架构可以回退到系统的定时器,通常设置为100 Hz。在这种情况下,时间分辨率将不太准确。
答案 7 :(得分:5)
This answer提到正在调整时钟的问题。在C ++ 11中使用<chrono>
库解决了保证计时单元和时间调整问题的问题。
时钟std::chrono::steady_clock
保证不会被调整,而且它将以相对于实时的恒定速率前进,因此SpeedStep等技术不得影响它。
您可以通过转换为std::chrono::duration
专精之一来获得类型安全单位,例如std::chrono::microseconds
。对于这种类型,tick值使用的单位没有歧义。但请记住,时钟不一定具有此分辨率。您可以将持续时间转换为阿秒,而无需实际具有准确的时钟。
答案 8 :(得分:4)
根据我的经验,以及我在互联网上阅读的内容,答案是“不”,但不能保证。它取决于CPU速度,操作系统,Linux的风格等。
答案 9 :(得分:3)
在SMP系统中读取RDTSC是不可靠的,因为每个CPU都维护自己的计数器,并且不保证每个计数器与另一个CPU同步。
我可能会建议您尝试 clock_gettime(CLOCK_REALTIME)
。 posix手册表明这应该在所有兼容系统上实现。它可以提供纳秒计数,但您可能需要检查系统上的 clock_getres(CLOCK_REALTIME)
以查看实际分辨率。