bash内置的time命令精度是多少?

4
我有一个脚本,使用bash内置命令time来测量程序的执行时间。我想了解这个命令的精度:据我所知,它返回以毫秒为单位的精度,但它使用getrusage()函数返回微秒级的值。但是根据这篇论文的阅读,实际精度只有10毫秒,因为getrusage基于时钟滴答(= 100Hz)对时间进行采样。该论文非常古老(它提到在Pentium 166Mhz上运行Linux 2.2.14,并具有96Mb的RAM)。 time是否仍然使用getrusage()和100 Hz的时钟滴答或在现代系统上更加精确?
测试机器正在运行Linux 2.6.32。 编辑:这是muru代码的稍微修改版本(应该也可以在旧版本的GCC上编译):修改变量“v”的值,同时更改测量之间的延迟,以便发现最小粒度。在相对较新的CPU上(第一版i5/i7 @~2.5Ghz),大约500,000的值应该会触发1毫秒的变化。
#include <sys/time.h>
#include <sys/resource.h>
#include <stdio.h>

void dosomething(){
    long v = 1000000; 
        while (v > 0)
          v--;
}

int main()
{
    struct rusage r1, r2;
    long t1, t2, min, max;
    int i;

    printf("t1\tt2\tdiff\n");

    for (i = 0; i<5; i++){
        getrusage(RUSAGE_SELF, &r1);
        dosomething();
        getrusage(RUSAGE_SELF, &r2);

        t1 = r1.ru_stime.tv_usec + r1.ru_stime.tv_sec*1000000 + r1.ru_utime.tv_usec + r1.ru_utime.tv_sec*1000000;
        t2 = r2.ru_stime.tv_usec + r2.ru_stime.tv_sec*1000000 + r2.ru_utime.tv_usec + r2.ru_utime.tv_sec*1000000;

        printf("%ld\t%ld\t%ld\n",t1,t2,t2-t1);

        if ((t2-t1 < min) | (i == 0))
            min = t2-t1;

        if ((t2-t1 > max) | (i == 0))
            max = t2-t1;
        dosomething();

    }

    printf("Min = %ldus Max = %ldus\n",min,max);

    return 0;
}

然而,精度受Linux版本限制:在Linux 3及以上版本中,精度为微秒级别,而在Linux 2.6.32上可能为1毫秒左右,这可能也取决于具体的发行版。我猜这种差异与近期Linux版本使用HRT而不是Tick有关。

无论如何,所有新旧机器的最大时间精度都为1毫秒。

1个回答

5

bash 内置的 time 命令仍使用 getrusage(2)。在 Ubuntu 14.04 系统上:

$ bash --version
GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ strace -o log bash -c 'time sleep 1'

real    0m1.018s
user    0m0.000s
sys 0m0.001s
$ tail log
getrusage(RUSAGE_SELF, {ru_utime={0, 0}, ru_stime={0, 3242}, ...}) = 0
getrusage(RUSAGE_CHILDREN, {ru_utime={0, 0}, ru_stime={0, 530}, ...}) = 0
write(2, "\n", 1)                       = 1
write(2, "real\t0m1.018s\n", 14)        = 14
write(2, "user\t0m0.000s\n", 14)        = 14
write(2, "sys\t0m0.001s\n", 13)         = 13
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
exit_group(0)                           = ?
+++ exited with 0 +++

strace输出中可以看出,它调用了getrusage函数。

就精度而言,getrusage使用的rusage结构包括timeval对象,而timeval具有微秒级精度。来自getrusage手册页

ru_utime
      This is the total amount of time spent executing in user mode,
      expressed in a timeval structure (seconds plus microseconds).

ru_stime
      This is the total amount of time spent executing in kernel
      mode, expressed in a timeval structure (seconds plus
      microseconds).

我认为它的速度要比10毫秒更快。以以下示例文件为例:

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

int main()
{
    struct rusage now, then;
    getrusage(RUSAGE_SELF, &then);
    getrusage(RUSAGE_SELF, &now);
    printf("%ld %ld\n",
        then.ru_stime.tv_usec + then.ru_stime.tv_sec*1000000 + then.ru_utime.tv_usec + then.ru_utime.tv_sec*1000000
        now.ru_stime.tv_usec + now.ru_stime.tv_sec*1000000 + now.ru_utime.tv_usec + now.ru_utime.tv_sec*1000000);
}

现在:

$ make test
cc     test.c   -o test
$ for ((i=0; i < 5; i++)); do ./test; done
447 448
356 356
348 348
347 347
349 350

getrusage函数连续调用的差异为1微秒和0(最小值)。由于它显示了1微秒的间隔,因此时钟滴答声必须最多为1微秒。

如果它有10毫秒的滴答声,则差异将为零,或者至少为10000。


1
谢谢,看起来很合理。查看我关于RTOS的旧笔记,似乎Linux tick可以配置为100、250和1000Hz,此外还存在比1毫秒更精确的高分辨率定时器。也许我错过了什么,但我认为你应该乘以1,000,000,因为1秒=10^-6秒然而,对于我的目的,我可以假设时间报告的最大精度为1毫秒。 - Alessandro Valentini
@AlessandroValentini 哎呀,这就是重复使用测试代码的后果。 :D - muru
无论如何,除非您运行短程序,否则它不会影响测试 ;) - Alessandro Valentini
抱歉,我误按了回车键,更多信息在编辑中。 - Alessandro Valentini
@AlessandroValentini 是的,我可以在一个2.6.32 Ubuntu 10.04系统上重现你的结果。:/ 看起来你需要为那个老内核安装其他东西。 - muru
显示剩余3条评论

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