agc*_*agc 10 shell shell-script timestamps benchmark portability
为了比较不同 shell 之间脚本的运行时间,一些 SE 答案建议使用bash
的内置 time
命令,如下所示:
time bash -c 'foo.sh'
time dash -c 'foo.sh'
Run Code Online (Sandbox Code Playgroud)
...等,用于每个要测试的外壳。此类基准测试无法消除每个 shell 加载和初始化自身所需的时间。例如,假设上述两种命令被存储在一缓慢的设备具有早期软盘的读取速度,(124KB /秒), dash
(一〜150K可执行程序)将加载大约7倍的速度比bash
(〜1M),所述壳加载时间会扭曲time
数字——这些炮弹的预装载时间与测量炮弹加载后foo.sh
每个炮弹下方的运行时间无关。
什么是最好的可移植和通用的 util 来运行脚本计时,可以从每个 shell内运行?所以上面的代码看起来像:
bash -c 'general_timer_util foo.sh'
dash -c 'general_timer_util foo.sh'
Run Code Online (Sandbox Code Playgroud)
注意:没有 shell内置 time
命令,因为没有一个是可移植的或通用的。
如果 util 还能够对 shell 的内部命令和管道所花费的时间进行基准测试,而无需用户首先将它们包装在脚本中,那就更好了。像这样的人工语法会有所帮助:
general_timer_util "while read x ; do echo x ; done < foo"
Run Code Online (Sandbox Code Playgroud)
一些shell'time
可以管理这个。例如bash -c "time while false ; do : ; done"
作品。要查看系统上哪些有效(和无效),请尝试:
tail +2 /etc/shells |
while read s ; do
echo $s ; $s -c "time while false ; do : ; done" ; echo ----
done
Run Code Online (Sandbox Code Playgroud)
mur*_*uru 10
您应该注意time
POSIX 指定的,并且 AFAICT POSIX 提到的唯一选项 ( -p
) 被各种外壳正确支持:
$ bash -c 'time -p echo'
real 0.00
user 0.00
sys 0.00
$ dash -c 'time -p echo'
real 0.01
user 0.00
sys 0.00
$ busybox sh -c 'time -p echo'
real 0.00
user 0.00
sys 0.00
$ ksh -c 'time -p echo'
real 0.00
user 0.00
sys 0.00
Run Code Online (Sandbox Code Playgroud)
time
正如您所注意到的,该实用程序通常内置于 shell 中,这使得它无法用作“中立”计时器。
但是,该实用程序通常也可作为外部实用程序使用,/usr/bin/time
很可能用于执行您建议的计时实验。
$ bash -c '/usr/bin/time foo.sh'
Run Code Online (Sandbox Code Playgroud)
我使用支持高分辨率计时器的GNU date
命令:
START=$(date +%s.%N)
# do something #######################
"$@" &> /dev/null
#######################################
END=$(date +%s.%N)
DIFF=$( echo "scale=3; (${END} - ${START})*1000/1" | bc )
echo "${DIFF}"
Run Code Online (Sandbox Code Playgroud)
然后我像这样调用脚本:
/usr/local/bin/timing dig +short unix.stackexchange.com
141.835
Run Code Online (Sandbox Code Playgroud)
输出单位为毫秒。
这是一个解决方案:
消除 [s] 每个 shell 加载和初始化自身所需的时间
可以在每个 shell 中运行
没有 shell 内置
time
命令,因为没有一个是可移植的或通用的
有两个部分:一个简短的 C 程序gettimeofday
,它被弃用,但仍然比clock_gettime
,以及一个简短的 shell 脚本,它使用该程序获取读取脚本两端的微秒精度时钟。C 程序是在时间戳上获得亚秒级精度的唯一可移植且开销最小的方法。
这是 C 程序epoch.c
:
#include <sys/time.h>
#include <stdio.h>
int main(int argc, char **argv) {
struct timeval time;
gettimeofday(&time, NULL);
printf("%li.%06i", time.tv_sec, time.tv_usec);
}
Run Code Online (Sandbox Code Playgroud)
和 shell 脚本timer
:
#!/bin/echo Run this in the shell you want to test
START=$(./epoch)
. "$1"
END=$(./epoch)
echo "$END - $START" | bc
Run Code Online (Sandbox Code Playgroud)
这是标准的shell 命令语言和bc
应该在任何 POSIX 兼容的 shell 下作为脚本工作。
您可以将其用作:
$ bash timer ./test.sh
.002052
$ dash timer ./test.sh
.000895
$ zsh timer ./test.sh
.000662
Run Code Online (Sandbox Code Playgroud)
它不测量系统或用户时间,只测量非单调挂钟经过时间。如果在脚本执行期间系统时钟发生变化,这将给出错误的结果。如果系统在负载下,结果将是不可靠的。我认为没有更好的东西可以在 shell 之间移植。
可以使用修改后的计时器脚本eval
来运行脚本之外的命令。
由于agc的输入,在很大程度上使用/proc/uptime
和dc
//多次修改了解决方案:bc
awk
#!/bin/sh
read -r before _ < /proc/uptime
sleep 2s # do something...
read -r after _ < /proc/uptime
duration=$(dc -e "${after} ${before} - n")
# Alternative using bc:
# duration=$(echo "${after} - ${before}" | bc)
# Alternative using awk:
# duration=$(echo "${after} ${before}" | awk '{print $1 - $2}')
echo "It took $duration seconds."
Run Code Online (Sandbox Code Playgroud)
显然假设/proc/uptime
存在并且具有某种形式。