gettimeofday函数的精度是多少?

5

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

你需要考虑的一件事情是计时器的精度和准确性。你可以使用的典型计时器是 gettimeofday();,请查看 man 页面以获取详细信息。你会发现,gettimeofday() 返回自 1970 年以来的微秒数;但是,这并不意味着计时器精确到微秒。通过测量连续调用 gettimeofday() 得出如何确切地计时;这将告诉你有多少次迭代你需要运行你的 null 系统调用测试,才能获得良好的测量结果。如果 gettimeofday() 对你来说不够精确,你可以尝试使用 x86 计算机上提供的 rdtsc 指令。

我编写了一些代码来测试调用 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()函数的精度正好为一微秒。我的测试代码有什么问题吗?还是我误解了作者的意思?感谢帮助!


2
系统时钟的分辨率未指定。应用程序应该使用clock_gettime()函数代替已过时的gettimeofday()函数。 - pmg
这是否意味着函数的准确性不确定? - shino
您可以在CLOCK_REALTIME时钟上使用clock_getres函数来获取纳秒级分辨率。 - Ian Abbott
一个不保证准确的函数可能仍然会给出完全准确的结果,只是不能依赖它这样做。 - Yun
分辨率在POSIX标准中未指定,但这并不意味着它在您的环境中未指定。在您的计算机上,使用当前版本的操作系统/C库,它是一个特定的值;您需要查阅相关文档。 - pmg
3个回答

6

gettimeofday函数调用之间的平均微秒数通常不到一毫秒——在我的机器上,它大约在0.05到0.15之间。

现代CPU通常以GHz速度运行,即每秒执行数十亿条指令,因此两个连续的指令应该花费纳秒级别的时间,而不是微秒级别(显然,像gettimeofday这样的函数的两个调用比两个简单命令更复杂,但其执行时间仍应为几十纳秒,而不是更长)。

但您正在执行int的除法操作——将(current_time[MAX_TIMES-1].tv_usec-current_time[0].tv_usec)除以MAX_TIMES——在C中也会返回一个int,在这种情况下为0。


要获取真正的测量值,请除以(double)MAX_TIMES(并将结果作为double类型打印):

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的特殊功能,该功能使内核可以在根本不经过内核的情况下向用户空间提供信息。

那么这是否意味着在我的机器上使用getTimeofday没有精度问题?尽管我知道我应该使用clock_gettime代替它。 - shino
是的,你只需要修正计算平均值的方式。 - Daniel Kleinstein

3

gettimeofday(2)已经被宣布过时,推荐使用clock_gettime(2),因为它的分辨率比旧版本更好(它使用纳秒分辨率)

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

在Linux / Intel 系统中,通常有良好的硬件可用,并且由 Linux 很好地实现,因此通常可以获得真正的纳秒精度。 但是,不要试图在具有质量差的石英振荡器且未进行 PPS 同步的机器上获得此精度。 您没有指定需要获取哪种时间戳,但如果您需要获取绝对时间戳,以与官方时间进行比较,请不要期望它们能够比某些百毫秒更接近(在 NTP 同步的机器上,在普通石英振荡器的基础上)

无论如何,要获取您安排的调用的平均时间,您有两个问题:

  • 您需要调用 MAX_TIMES + 1 次您的 gettimeofday(2) 系统调用,因为您正在测量两个时间戳之间的时间(因此您计算发出系统调用的时间和它能够获取时间戳的时间,以及从时间戳取到返回值被传递给调用例程的时间 --- 但是顺序相反)。最好的方法是在开始时获取一个时间戳 t0,然后在结束时获取t1中的 MAX_TIMES 个时间戳。只有这样,您才能确定从 t0t1 的时间,并将其分成 MAX_TIMES 部分。为此,请从 t1.tv_usec - t0.tv_usec 减去,在结果小于零时将其加上 1000000,并增加 t1.tv_sec - t0.tv_sec 中的差异。 tv_sec 将包含秒数差异,而 tv_usec 将包含超过秒的微秒数。
  • 这假设系统调用开销不会改变,但这并不正确。有时,系统调用需要的时间比其他时间长,您感兴趣的值不是它们的平均值,而是它可以达到的最小值。但您可以满足平均值,基于您不会进入下面usec的分辨率。

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


0
    z=x=gettimeofday()
    for(i=1; x==z; i++){ z=gettimeofday()}
    # >10,000 loops until diff gettimeofday()
    y=sprintf("%.24f",z-x);
    #: 0.015600204 = diff gettimeofday() on my desktop. Your mileage may vary.
    #:     ^  ^  ^
    #: ns=nanosecond= 10^-9; 1/1,000,000,000=> fmt=".9f"
    #: us=microsecond= 10^-6; 1/1,000,000=> fmt=".6f"
    #: ms=millisecond= 10^-3; 1/1,000 => fmt=".3f"

1
请编辑此答案以提高清晰度。 - user16217248
在我的桌面上,我能够获得的最佳精度约为1微秒的2/10。这意味着当我进行两次读数并相减(t1-t0)时,最小差异几乎为1微秒。如果我只执行了几条指令,即使我知道我使用了一些CPU时间,差异也会显示为0。请注意:当系统加载备份等内容时,时间戳的表现精度要低得多,可能只有1/2秒,因为我的程序只获得了一小部分CPU时间,而其他程序(备份等)也在占用资源,导致计算机变得“缓慢”。这样是否更清楚? - SuserJohn2
是的,这样做确实使答案更清晰,但最好将该信息作为答案本身的编辑而不是评论包含在内。 - user16217248

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接