如何调整时间命令来测量程序经过的时间

Ini*_*Ini 4 c time benchmark

我正在尝试获取我的程序完成所需的时间(又名经过时间),因此我使用了常见的time.

这样做我得到的是 3 个度量:用户、系统和总数。这很好,但我发现我感兴趣的用户时间只有两位小数,我需要更多。有什么办法可以从我的时间命令中获得更多小数位吗?

示例:time ./myCProgram
输出:0.17s 用户 1.21s 系统 130% cpu 0.187 总计

想要的输出:0.17000s 用户 1.21s 系统 130% cpu 0.187 总计(或更多小数位)

Sté*_*las 5

如果您想要的只是经过的时间,那么使用 zsh 或 ksh93:

$ typeset -F SECONDS=0; sleep 1; print "$SECONDS"
1.0012850761
Run Code Online (Sandbox Code Playgroud)

现在,这种精度是否有意义是另一回事。


F. *_*uri 5

计算时间纳秒GNU/Linux,使用

警告这篇文章被那里讨论的一个新的好方法过时了:Profiling bash (3 answers) and with full ready-to-use source file there: Elap-bash V3

检索可靠值的方法

有一些方法可以以比一秒更细的粒度查询时间。

首先,但不是最好的:1/100 秒

对于 1/100 秒,您可以简单地参考/proc/uptime

sed 's/ .*$//' /proc/uptime 
276440.58
Run Code Online (Sandbox Code Playgroud)

为计算实际 UTC 时间而添加的时间偏移可以通过以下方式之一获得:

ps ho lstart 1 | date -f - +%s
1357193821
Run Code Online (Sandbox Code Playgroud)

或者

date +%s.%N-$(sed 's/ .*$//' /proc/uptime ) | bc -l
1357193821.088187101
Run Code Online (Sandbox Code Playgroud)

由于偏移量是静态的,因此该值只能在脚本开始时计算 1 次。

1/1000000000 秒,proc仅使用条目:

对于纳秒计算,有一个proc entry, 命名/proc/timer_list,其中包含uptimeoffset

sed < /proc/timer_list -ne '/now at/p;/offset/{p;q}'
now at 276350507668586 nsecs
  .offset:     1357193821075753443 nsecs
Run Code Online (Sandbox Code Playgroud)

所以这

echo $(($(sed < /proc/timer_list -ne '
    /^now at/{s/now at \([0-9]*\) ns.*$/\1/;H};
    /offset/{s/^.*: *\([0-9]*\) ns.*$/\1/;G;s/\n\+/+/;p;q}'))) 
1357470173543424035
Run Code Online (Sandbox Code Playgroud)

是从 开始经过的整数纳秒1970-1-1 00:00:00 UTC

为了在下计算整数,不需要使用bc和解析 proc 文件,我们可以使用mapfilewhich 是一个内置bash

# first, some static variables (with fork, but only 1 time, at begin;):
nowAtLine=$(($(sed -ne < /proc/timer_list '/now at/{=;q}')-1))
offset=$(sed -ne < /proc/timer_list '
    /offset/{s/^.*: *\([0-9]*\) n.*$/\1/p;q}')

# than this will be a lot quicker than a fork:
mapfile -n 1 -s $nowAtLine timerList </proc/timer_list &&
    timerList=($timerList) &&
    echo $((${timerList[2]}+offset))
1357470173543424035
Run Code Online (Sandbox Code Playgroud)

1/1000000000 秒,使用date二进制工具:

最后,如果它们不存在,我们可以date记住它fork比阅读花费更多的时间proc files,只停留在一个 bash 会话中。

date +%s%N
1357470074808968375
Run Code Online (Sandbox Code Playgroud)

我的elap.bash功能

基于此,我编写了一个elap bash 函数,它有两个计数器:1 个用于每次调用,另一个用于overall duration.

用法:

首先,这是一个函数,而不是脚本。您必须source在当前的 bash 会话中使用它们才能使用它。

. elap.bash
Run Code Online (Sandbox Code Playgroud) 语法:
elap [ [ -r | -R ] | [ -n ] [ -t | -T ] [<simple text report>] ]
    -r reset first counter only, don't output anything, like -R...
    -R reset both counters, (both -r and -R must by unique argument)
    -n don't reset any counter (just print)
    -t print both counters (reset first counter)
    -T print and reset
Run Code Online (Sandbox Code Playgroud)

在使用它之前重置两个计数器:

elap -R
find /usr/bin >/dev/null
elap browsing /usr/bin

     0.025389543 browsing /usr/bin
Run Code Online (Sandbox Code Playgroud)

所以你可以在脚本中添加一些标记:

#!/bin/bash
. elap.bash
  elap -R
tar -cf /tmp/test.tar -C / bin
  elap making a tarball of $(stat -c %s /tmp/test.tar) bytes
gzip /tmp/test.tar
  elap compressing them to $(stat -c %s /tmp/test.tar.gz) bytes
scp /tmp/test.tar.gz backup@elswhere.net:backups/
  elap sending file to backup server
rm /tmp/test.tar.gz
  elap removing compressed tarball
elap -t total duration

     0.043223957 making a tarball of 5877760 bytes
     0.667249628 compressing them to 2742537 bytes
test.tar.gz                                  100% 2678KB 2.6MB/s 00:00
     0.380779818 sending file to backup server
     0.010262259 removing compressed tarball
     0.003566335      1.105081997 total duration
Run Code Online (Sandbox Code Playgroud)

使用trap debug了下面的步骤一步

带或不带-t开关,trap debug一步一步使用,减少脚本改动(在脚本顶部添加四行,在底部添加一行):

由于执行之前会发生调试陷阱,我们需要将变量内容存储到变量中以便正确打印,并在脚本底部添加一个虚拟命令。 $BASH_COMMAND$BASH_LAST

#!/bin/bash

. elap.bash
elap -R
export BASH_LAST=Starting
trap 'elap -t $BASH_LAST;BASH_LAST=$BASH_COMMAND' debug 

tar -cf /tmp/test.tar -C / bin
gzip /tmp/test.tar
scp /tmp/test.tar.gz backup@elswhere.net:backups/
rm /tmp/test.tar.gz

exit $?
Run Code Online (Sandbox Code Playgroud)

exit $?需要该命令在最后一个命令之后转储统计信息rm

     0.001011086      0.001011086 Starting
     0.045175969      0.046187055 tar -cf /tmp/test.tar -C / bin
     0.651394209      0.697581264 gzip /tmp/test.tar
test.tar.gz                                  100% 2678KB 2.6MB/s 00:00
     0.374499354      1.072080618 scp /tmp/test.tar.gz backup@elswhere.net:backups/
     0.007160101      1.079240719 rm /tmp/test.tar.gz
Run Code Online (Sandbox Code Playgroud)

函数源

uptime如果您更喜欢使用 forkdate +%s%N而不是使用 1/100 秒的粒度,那么您可以在哪里剪切(或重新排序)部分。

#
# Bash source file for fine elapsed time reporting
# based on /proc/timer_list, display elapsed time in nanosecs
# or /proc/uptime if timer_list not present, for 100th of secs.
# if none of them if present, fall back to *fork* `date +%s%N`.
#
# (C) 2011-2012 Felix Hauri - felix@f-hauri.ch
# Licensed under terms of LGPL v3. www.gnu.org

# Usage:
#   load script into bash using ``source elap.bash''
#
# Syntaxe: elap [ [ -r | -R ] | [ -n ] [ -t | -T ] [<simple text report>] ]
#   -r reset first counter only, don't output anything, like -R...
#   -R reset both counters, (both -r and -R must by unique argument)
#   -n don't reset any counter (just print)
#   -t print both counters (reset first counter)
#   -T print and reset
#
# nota: using ``-n'' in combinaison with any of ``-r'' or ``-R'' is buggy.

export _elaP_now _elaP_last _elaP_elap _elaP_last2 _elaP_dec

if [ -r /proc/timer_list ] ;then
    _elaP_file=/proc/timer_list
    _elaP_dec=9
    _elaP_field=2
    mapfile < $_elaP_file _elaP_now
    _elaP_line=0
    while [ "${_elaP_now[_elaP_line]}" == \
    "${_elaP_now[_elaP_line]#*now at}" ] ;do
    ((_elaP_line++))
    done
    eval 'function elap_getNow() {
     mapfile -n 1 -s '$_elaP_line' <'$_elaP_file' _elaP_now
         _elaP_now=($_elaP_now)
         _elaP_now=${_elaP_now[_elaP_field]}
         }'
else
    #      --- To be removed for nanoseconds only
    if [ -r /proc/uptime ] ;then
    _elaP_dec=2
    function elap_getNow() {
        read -a _elaP_now </proc/uptime _elaP_now
            _elaP_now=${_elaP_now//./}
    }
    else # --- End of part to be removed for ns only.
    _elaP_dec=9
    function elap_getNow() { _elaP_now=$(date +%s%N) ;}
    fi   # --- Remove this line too for ns only.
fi

export -f elap_getNow

elap() {
    local _Z9=000000000
    local _elaP_setLast=true
    [ "$1" == "-n" ] && shift && _elaP_setLast=false
    [ "$2" == "-n" ] && set -- $1 ${@:3} && _elaP_setLast=false

    elap_getNow

    _elaP_elap=$((_elaP_now - _elaP_last))
    [ ${#_elaP_elap} -lt $_elaP_dec ] && \
    _elaP_elap=${_Z9:0:_elaP_dec-${#_elaP_elap}}$_elaP_elap
    [ "${*}" == "-R" ] &&  _elaP_last2=$_elaP_now || \
    [ "${*}" == "-r" ] || if [ "$1" == "-t" ] || [ "$1" == "-T" ] ;then
    local _elaP_setLast2=false
    [ "$1" == "-T" ] && _elaP_setLast2=true
    shift
            _elaP_elap2=$((_elaP_now - _elaP_last2))
    [ ${#_elaP_elap2} -lt $_elaP_dec ] && \
        _elaP_elap2="${_Z9:0:_elaP_dec-${#_elaP_elap2}}$_elaP_elap2"
        printf "%6d.%s %6d.%s %s\n" \
        "${_elaP_elap:0:${#_elaP_elap}-$_elaP_dec}" \
        "${_elaP_elap:${#_elaP_elap}-_elaP_dec}" \
        "${_elaP_elap2:0:${#_elaP_elap2}-$_elaP_dec}" \
        "${_elaP_elap2:${#_elaP_elap2}-_elaP_dec}"      "${*}"
    $_elaP_setLast2 && _elaP_last2=$_elaP_now
    else
        printf "%6d.%s %s\n" \
        "${_elaP_elap:0:${#_elaP_elap}-$_elaP_dec}" \
        "${_elaP_elap:${#_elaP_elap}-_elaP_dec}"        "${*}"
    fi
    $_elaP_setLast && _elaP_last=$_elaP_now
}

export -f elap
Run Code Online (Sandbox Code Playgroud)