shell脚本的性能分析工具

时间:2010-12-02 14:28:06

标签: performance bash

我正在尝试加速调用子shell并执行各种操作的脚本集合。我很想知道是否有任何工具可用于执行shell脚本及其嵌套shell的时间,并报告脚本的哪些部分是最昂贵的。

例如,如果我有一个类似以下的脚本。

#!/bin/bash

echo "hello"
echo $(date)
echo "goodbye"

我想知道三条线中的每一条花了多长时间。 time只会给我一些脚本的总时间。 bash -x很有意思,但不包括时间戳或其他时间信息。

7 个答案:

答案 0 :(得分:44)

您可以设置PS4以显示时间和行号。这样做不需要安装任何实用程序和工作,而无需将stderr重定向到stdout。

对于这个脚本:

#!/bin/bash -x
# Note the -x flag above, it is required for this to work
PS4='+ $(date "+%s.%N ($LINENO) ")'
for i in {0..2}
do
    echo $i
done
sleep 1
echo done

输出如下:

+ PS4='+ $(date "+%s.%N ($LINENO) ")'
+ 1291311776.108610290 (3) for i in '{0..2}'
+ 1291311776.120680354 (5) echo 0
0
+ 1291311776.133917546 (3) for i in '{0..2}'
+ 1291311776.146386339 (5) echo 1
1
+ 1291311776.158646585 (3) for i in '{0..2}'
+ 1291311776.171003138 (5) echo 2
2
+ 1291311776.183450114 (7) sleep 1
+ 1291311777.203053652 (8) echo done
done

这假设GNU日期,但您可以将输出规范更改为您喜欢的任何内容或与您使用的日期版本匹配的任何内容。

注意:如果您希望在不修改现有脚本的情况下执行此操作,则可以执行以下操作:

PS4='+ $(date "+%s.%N ($LINENO) ")' bash -x scriptname

在即将推出的Bash 5中,您将能够保存分叉date(但是你会得到微秒而不是纳秒):

PS4='+ $EPOCHREALTIME ($LINENO) '

答案 1 :(得分:10)

您可以将在-x下运行的输出通过管道传输到收到每行时间戳的内容。例如,来自djb daemontoolstai64n。 在一个基本的例子中,

sh -x slow.sh 2>&1 | tai64n | tai64nlocal

这会混淆stdout和stderr,但它确实为所有内容提供了时间戳。 然后,您必须分析输出以查找昂贵的行并将其关联回源。

您可能还会发现使用strace很有帮助。例如,

strace -f -ttt -T -o /tmp/analysis.txt slow.sh

这将生成一份非常详细的报告,其中包含/tmp/analysis.txt中的大量时间信息,但是在每个系统的呼叫级别上,这可能过于详细。

答案 2 :(得分:1)

听起来你想要为每个回声计时。如果回声就是你所做的一切,这很容易

alias echo='time echo'

如果你正在运行其他命令,这显然是不够的。

答案 3 :(得分:1)

我的首选方法如下。原因是它也支持OSX(没有高精度日期)&即使你没有安装bc也会运行。

#!/bin/bash

_profiler_check_precision() {
    if [ -z "$PROFILE_HIGH_PRECISION" ]; then
        #debug "Precision of timer is unknown"
        if which bc > /dev/null 2>&1 && date '+%s.%N' | grep -vq '\.N$'; then
            export PROFILE_HIGH_PRECISION=y
        else
            export PROFILE_HIGH_PRECISION=n
        fi
    fi
}


_profiler_ts() {
    _profiler_check_precision

    if [ "y" = "$PROFILE_HIGH_PRECISION" ]; then
        date '+%s.%N'
    else
        date '+%s'
    fi
}

profile_mark() {
    _PROF_START="$(_profiler_ts)"
}

profile_elapsed() {
    _profiler_check_precision

    local NOW="$(_profile_ts)"
    local ELAPSED=

    if [ "y" = "$PROFILE_HIGH_PRECISION" ]; then
        ELAPSED="$(echo "scale=10; $NOW - $_PROF_START" | bc | sed 's/\(\.[0-9]\{0,3\}\)[0-9]*$/\1/')"
    else
        ELAPSED=$((NOW - _PROF_START))
    fi

    echo "$ELAPSED"
}

do_something() {
    local _PROF_START
    profile_mark()
    sleep 10
    echo "Took $(profile_elapsed()) seconds"
}

答案 4 :(得分:0)

here复制:

自从我到这里结束了至少两次之后,我实施了一个解决方案:

https://github.com/walles/shellprof

它运行您的脚本,透明地计时所有打印的行,最后显示屏幕上最长的行的前10个列表:

~/s/shellprof (master|✔) $ ./shellprof ./testcase.sh
quick
slow
quick

Timings for printed lines:
1.01s: slow
0.00s: <<<PROGRAM START>>>
0.00s: quick
0.00s: quick
~/s/shellprof (master|✔) $

答案 5 :(得分:0)

这里有一个简单的方法,几乎​​适用于所有 Unix,不需要特殊软件:

  • 启用外壳跟踪,例如set -x
  • 通过logger管道输出脚本:
sh -x ./slow_script 2>&1 | logger

这会将输出写入系统日志,系统日志会自动为每条消息添加时间戳。如果您使用带有 journald 的 Linux,您可以使用

获得高精度时间戳
journalctl -o short-monotonic _COMM=logger

许多传统的 syslog 守护进程还提供高精度时间戳(对于 shell 脚本来说,毫秒应该足够了)。

这是我刚刚以这种方式分析的脚本中的一个示例:

[1940949.100362] bremer root[16404]: + zcat /boot/symvers-5.3.18-57-default.gz
[1940949.111138] bremer root[16404]: + '[' -e /var/tmp/weak-modules2.OmYvUn/symvers-5.3.18-57-default ']'
[1940949.111315] bremer root[16404]: + args=(-E $tmpdir/symvers-$krel)
[1940949.111484] bremer root[16404]: ++ /usr/sbin/depmod -b / -ae -E /var/tmp/weak-modules2.OmYvUn/symvers-5.3.18-57-default 5.3.18-57>
[1940952.455272] bremer root[16404]: + output=
[1940952.455738] bremer root[16404]: + status=0

在这里您可以看到“depmod”命令花费了大量时间。

答案 6 :(得分:-3)

我不知道任何shell分析工具。

从历史上看,只需在Perl,Python,Ruby甚至C中重写太慢的shell脚本。

一个不太激烈的想法是使用比bash更快的shell。 Dashash适用于所有Unix风格的系统,通常更小更快。