我正在阅读OSTEP的single.dvi一章。在家庭作业部分,它说:
您必须考虑的一件事是计时器的精度和准确性。可以使用的典型计时器是
gettimeofday();
阅读手册页了解详细信息。你会看到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(¤t_time[i], NULL);
}
printf("seconds: %ldnmicro_seconds: %ldn", current_time[0].tv_sec, current_time[0].tv_usec);
printf("seconds: %ldnmicro_seconds: %ldn", 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 usn", (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通常以千兆赫的速度运行,即每秒数十亿条指令,因此两条连续的指令应该是纳秒级的,而不是微秒级的(显然,对gettimeofday
这样的函数的两次调用比两个简单的操作码更复杂,但它仍然应该是几十纳秒级,而不是更多)。
但您正在执行int
s的除法-将(current_time[MAX_TIMES - 1].tv_usec - current_time[0].tv_usec)
除以MAX_TIMES
-在C中,它也将返回int
,在本例中为0。
要获得实际测量值,请除以(double)MAX_TIMES
(并将结果打印为双精度):
printf("the average time of a gettimeofday function call is: %f usn", (current_time[MAX_TIMES - 1].tv_usec - current_time[0].tv_usec) / (double)MAX_TIMES);
在Linux系统上,gettimeofday
之所以如此快速(你可能会认为它是一个更复杂的函数,调用内核并产生系统调用的开销),要归功于一个名为vdso的特殊功能,它可以让内核在根本不经过内核的情况下向用户空间提供信息。
gettimeofday(2)
已被宣布过时,代表分辨率比旧版本更好的clock_gettime(2)
(它使用纳秒分辨率)
精确性是另一个问题(不同),因为它取决于硬件如何允许您获得时间戳以及操作系统如何实现它
在基于linux/intel的系统中,通常有很好的硬件可用,并且它在linux上得到了很好的实现,所以在处理时间戳时,通常可以获得真正的纳秒精度。但不要试图在石英振荡器不好且没有PPS烧结的机器中获得这种精度。你没有指定你需要获取哪种时间戳,但如果你需要获取绝对时间戳,与官方时间进行比较,不要期望它们接近几百毫秒(基于带有普通石英振荡器的NTP同步机器)
无论如何,要想得到你安排的电话的平均时间,你有两个问题:
- 您需要调用
MAX_TIMES + 1
次gettimeofday(2)
系统调用,当你测量两个时间戳之间的时间时(所以你计算调用系统调用之间的时间,它能够获取时间戳,从时间戳获取到返回值的时间被传递到调用例程——但顺序相反)最好的方法是在开始时获取时间戳t0
,最后在CCD_ 22中加上CCD_。只有这样,您才能确定t0
到t1
之间的时间,并在MAX_TIMES
之间进行划分。要做到这一点,请从t1.tv_usec
中减去t0.tv_usec
,如果结果小于零,则将1000000
相加,并增加t1.tv_sec - t0.tv_sec
中的差。tv_sec
将具有以秒为单位的差值,而tv_usec
将具有超过微秒的秒 - 这假设系统调用开销不会改变,但事实并非如此。有时系统调用比其他调用花费更多的时间,并且您感兴趣的值不是它们的平均值,而是它所能达到的最小值。但你可以符合平均值,因为你不会陷入使用不足的分辨率
无论如何,我建议您使用clock_gettime(2)
系统调用,因为它具有纳秒分辨率。
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"