【问题标题】:What is the precision of the bash builtin time command?bash 内置 time 命令的精度是多少?
【发布时间】:2016-05-19 07:04:04
【问题描述】:

我有一个脚本,它使用 bash 内置命令 time 测量程序的执行时间。

我试图了解此命令的精度:据我所知,它以毫秒为单位返回精度,但它使用getrusage() 函数返回一个以微秒为单位的值。但是读取this paper,实际精度仅为10ms,因为getrusage 对时间的采样依赖于滴答声(= 100Hz)。这篇论文真的很老了(它提到了在奔腾 166Mhz 和 96Mb 内存上运行的 Linux 2.2.14)。

time 是否仍在使用getrusage() 和 100 Hz 刻度,还是在现代系统上更精确?

测试机运行Linux 2.6.32。

编辑:这是 muru 的代码的略微修改版本(也应该在旧版本的 GCC 上编译):修改变量 'v' 的值也改变措施之间的延迟,以发现最小粒度。大约 500,000 的值应该会在相对较新的 cpu 上触发 1ms 的变化(i5/i7 的第一个版本 @~2.5Ghz)

#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 上可能在 1ms 左右,可能还取决于特定的发行版。我想这种差异与在最近的 linux 版本上使用 HRT 而不是 Tick 有关。

在任何情况下,所有最近和不太最近的机器上的最大时间精度都是 1 毫秒。

【问题讨论】:

    标签: linux bash performance time


    【解决方案1】:

    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 具有微秒精度。来自manpage of 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 µs 和 0(最小值)。由于它确实显示了 1 µs 的间隙,因此滴答声必须最多为 1 µs。

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

    【讨论】:

    • 谢谢,看起来很合理。查看我关于 RTOS 的旧笔记似乎 Linux 滴答可以配置为 100、250 和 1000Hz,此外还存在比 1 毫秒更精确的高分辨率计时器。也许我错过了一些东西,但我认为你应该乘以 1,000,000,因为 1s = 10^-6s 但是出于我的目的,我可以假设时间报告的最大精度(1ms)
    • @AlessandroValentini 哎呀。这就是重用测试代码的结果。 :D
    • 在你运行短程序之前它不会影响测试;)
    • 嗨,我已经在我的笔记本电脑上运行了你的测试,它运行良好,然后我在我们用于测试的机器上尝试了它,但它总是返回 0 0。我的机器运行 Fedora 22 (Linux 4.0 .4) 当桌面运行 Red Hat 6(内核 2.6.32)时,您对这种奇怪的行为有任何想法吗?两台机器在单线程中应该有非常接近的性能
    • @AlessandroValentini 您的笔记本电脑运行的是哪个内核?我在 3.13 左右。
    猜你喜欢
    • 1970-01-01
    • 2016-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-14
    • 1970-01-01
    • 1970-01-01
    • 2010-10-03
    相关资源
    最近更新 更多