bash 用于 shell 脚本的性能分析工具

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4336035/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 23:01:49  来源:igfitidea点击:

Performance profiling tools for shell scripts

performancebash

提问by bradtgmurray

I'm attempting to speed up a collection of scripts that invoke subshells and do all sorts of things. I was wonder if there are any tools available to time the execution of a shell script and its nested shells and report on which parts of the script are the most expensive.

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

For example, if I had a script like the following.

例如,如果我有一个如下所示的脚本。

#!/bin/bash

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

I would like to know how long each of the three lines took. timewill only only give me total time for the script. bash -xis interesting but does not include timestamps or other timing information.

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

回答by Paused until further notice.

You can set PS4to show the time and line number. Doing this doesn't require installing any utilities and works without redirecting stderr to stdout.

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

For this script:

对于这个脚本:

#!/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

The output looks like:

输出看起来像:

+ 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

This assumes GNU date, but you can change the output specification to anything you like or whatever matches the version of date that you use.

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

Note: If you have an existing script that you want to do this with without modifying it, you can do this:

注意:如果您有一个现有脚本想要在不修改它的情况下执行此操作,则可以执行以下操作:

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

In the upcoming Bash 5, you will be able to save forking date(but you get microseconds instead of nanoseconds):

在即将发布的 Bash 5 中,您将能够节省分叉date(但您得到的是微秒而不是纳秒):

PS4='+ $EPOCHREALTIME ($LINENO) '

回答by Emil Sit

You could pipe the output of running under -xthrough to something that timestamps each line when it is received. For example, tai64nfrom djb's daemontools. At a basic example,

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

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

This conflates stdout and stderr but it does give everything a timestamp. You'd have to then analyze the output to find expensive lines and correlate that back to your source.

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

You might also conceivably find using stracehelpful. For example,

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

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

This will produce a very detailed report, with lots of timing information in /tmp/analysis.txt, but at a per-system call level, which might be too detailed.

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

回答by sorpigal

Sounds like you want to time each echo. If echo is all that you're doing this is easy

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

alias echo='time echo'

If you're running other command this obviously won't be sufficient.

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

回答by Vitali

Updated

更新

See enable_profiler/disable_profiler in https://github.com/vlovich/bashrc-wrangler/blob/master/bash.d/000-setup

请参阅https://github.com/vlovich/bashrc-wrangler/blob/master/bash.d/000-setup 中的enable_profiler/disable_profiler

which is what I use now. I haven't tested on all version of BASH & specifically but if you have the tsutility installed it works very well with low overhead.

这就是我现在使用的。我还没有在所有版本的 BASH 上进行过专门测试,但是如果您安装了该ts实用程序,它可以很好地运行,而且开销很低。

Old

老的

My preferred approach is below. Reason is that it supports OSX as well (which doesn't have high precision date) & runs even if you don't have bc installed.

我的首选方法如下。原因是它也支持 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
            PROFILE_HIGH_PRECISION=y
        else
            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="$(_profiler_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]*$//')"
    else
        ELAPSED=$((NOW - _PROF_START))
    fi

    echo "$ELAPSED"
}

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

回答by DigitalRoss

I'm not aware of any shell profiling tools.

我不知道任何外壳分析工具。

Historically one just rewrites too-slow shell scripts in Perl, Python, Ruby, or even C.

从历史上看,人们只是用 Perl、Python、Ruby 甚至 C 重写太慢的 shell 脚本。

A less drastic idea would be to use a faster shell than bash. Dashand ashare available for all Unix-style systems and are typically quite a bit smaller and faster.

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