gettimeofday 函数的精度是多少?

What is the precision of the gettimeofday function?

我正在阅读 OSTEP 的第 single.dvi 章。在作业部分,它说:

One thing you’ll have to take into account is the precision and accuracy of your timer. A typical timer that you can use is gettimeofday(); read the man page for details. What you’ll see there is that gettimeofday() returns the time in microseconds since 1970; however, this does not mean that the timer is precise to the microsecond. Measure back-to-back calls to gettimeofday() to learn something about how precise the timer re- ally is; this will tell you how many iterations of your null system-call test you’ll have to run in order to get a good measurement result. If gettimeofday() is not precise enough for you, you might look into using the rdtsc instruction available on x86 machines

我写了一些代码来测试调用 gettimeofday() 函数的成本如下:

#include <stdio.h>
#include <sys/time.h>

#define MAX_TIMES 100000

void m_gettimeofday() {
    struct timeval current_time[MAX_TIMES];
    int i;
    for (i = 0; i < MAX_TIMES; ++i) {
        gettimeofday(&current_time[i], NULL);
    }
    printf("seconds: %ld\nmicro_seconds: %ld\n", current_time[0].tv_sec, current_time[0].tv_usec);
    printf("seconds: %ld\nmicro_seconds: %ld\n", current_time[MAX_TIMES - 1].tv_sec, current_time[MAX_TIMES - 1].tv_usec);
    printf("the average time of a gettimeofday function call is: %ld us\n", (current_time[MAX_TIMES - 1].tv_usec - current_time[0].tv_usec) / MAX_TIMES);
}

int main(int argc, char *argv[]) {
    m_gettimeofday();
    return 0;
}

但是,输出将始终为 0 微秒。 gettimeofday() 函数的精度似乎恰好是一微秒。我的测试代码有什么问题?还是我误解了作者的意思?感谢您的帮助!

连续调用 gettimeofday 的平均微秒通常 小于一微秒 - 在我的机器上它介于 0.05 和 0.15 之间。

现代 CPU 通常 运行 GHz 速度 - 即每秒 十亿 条指令,因此两条连续指令的数量级应为纳秒,而不是微秒(显然,对 gettimeofday 这样的函数的两次调用比两个简单的操作码更复杂,但它仍应占用数十纳秒的数量级,而不是更多。

但是你正在执行 ints 的除法 - 用 (current_time[MAX_TIMES - 1].tv_usec - current_time[0].tv_usec) 除以 MAX_TIMES - 在 C 中也将 return 和 int,在这种情况下 0.


要获得实际测量值,除以 (double)MAX_TIMES(并将结果打印为双精度):

printf("the average time of a gettimeofday function call is: %f us\n", (current_time[MAX_TIMES - 1].tv_usec - current_time[0].tv_usec) / (double)MAX_TIMES);

作为奖励 - 在 Linux 系统上 gettimeofday 如此之快的原因(您可能会认为它是一个更复杂的函数,调用内核并产生系统调用的开销)这要归功于一个名为 vdso 的特殊功能,它允许内核向用户 space 提供信息,而无需通过内核。

gettimeofday(2) 已代表 clock_gettime(2) 宣布过时,它比旧版本具有更好的分辨率(它使用纳秒级分辨率)

精度是另一个问题(不同),因为它取决于硬件如何允许您获取时间戳以及操作系统如何实现它。

在基于 linux/intel 的系统中,通常有很好的硬件可用,并且 linux 很好地实现了它,因此在处理时间戳时通常可以获得真正的纳秒级精度。但是不要试图在石英振荡器较差且未经过 PPS sincronized 的机器中获得这种精度。你没有指定你需要获取什么样的时间戳,但是如果你需要获取绝对时间戳,与官方时间进行比较,不要期望它们接近几百毫秒(基于 NTP syncronized带有普通石英振荡器的机器)

无论如何,要获得您安排的通话的平均时间,您有两个问题:

  • 你需要调用 MAX_TIMES + 1 次你的 gettimeofday(2) 系统调用,因为你正在测量两个时间戳之间的时间(所以你计算你调用系统调用和它能够获取时间戳,并且从时间戳到 return 值的时间被传递给调用例程 --- 但顺序相反)执行此操作的最佳方法是获取时间戳 t0在开头,MAX_TIMES 时间戳在 t1 中。只有这样,您才能确定 t0t1 之间的时间并将其划分为 MAX_TIMES。为此,从 t1.tv_usec 中减去 t0.tv_usec,如果结果小于零,则添加 1000000,并增加 t1.tv_sec - t0.tv_sec 中的差值。 tv_sec 将有秒的差异,而 tv_usec 将有超过秒的微秒。
  • 这是假设系统调用开销不变,但事实并非如此。有时系统调用比其他的花费更多的时间,你感兴趣的值不是它们的平均值,而是它能达到的最小值。但是你可以符合平均值,因为你不会进入 usec 分辨率下。

无论如何,我建议您使用 clock_gettime(2) 系统调用,因为它具有纳秒分辨率。