Как эффективно определить время выполнения скрипта?

342

Я хотел бы отобразить время завершения сценария.

То, что я сейчас делаю, -

#!/bin/bash
date  ## echo the date at start
# the script contents
date  ## echo the date at end

Это просто показывает время начала и конца сценария. Можно ли отобразить мелкозернистый вывод, например время процессора / ввода-вывода и т. Д.?

холодный морской тропический воздух
источник
2
stackoverflow.com/questions/385408/…
Сиро Сантилли 新疆 改造 中心 法轮功 六四 事件
Я думаю, что текущий принятый ответ не достаточно из-за временных колебаний.
Лео Леопольд Герц 준영
1
@CiroSantilli 事件 事件 2016 六四 事件 法轮功 Я думаю, что ваш предложенный поток все еще не достаточно, чтобы устранить влияние временных событий.
Лео Леопольд Герц 준영
Также связано: stackoverflow.com/questions/5152858/… или несколько unix.stackexchange.com/questions/334145/…
phk
Есть команда, которую вы можете запустить, а затем она автоматически синхронизирует все команды Unix, забыли, как включить?
powder366

Ответы:

451

Просто используйте timeпри вызове сценария:

time yourscript.sh
Trudbert
источник
61
Это выводит три раза: real, userи sys. О значениях этого см. Здесь .
Гаррет
27
и «реальный» - это, вероятно, то, что люди хотят знать - «Реальный - это время настенных часов - время от начала до конца разговора»
Брэд Паркс
3
Есть ли способ записать стандартный вывод в файл? Например, что-то вродеtime $( ... ) >> out.txt
Jon
1
Вы также можете сделать это с последовательностями команд в командной строке, например:time (command1 && command2)
meetar
1
Или он мог бы в своем сценарии просто сделать: echo $ SECONDS, предполагая, что это bash ....
Скотт Прайв
171

Если timeне вариант,

start=`date +%s`
stuff
end=`date +%s`

runtime=$((end-start))
Роб Бос
источник
31
Обратите внимание, что это работает, только если вам не нужна точность ниже секунды. Для некоторых целей это может быть приемлемо, для других - нет. Для немного более высокой точности (вы все еще вызова dateдважды, например, чтобы вы могли в лучшем получить точность миллисекунды на практике, и , возможно , меньше), попробуйте использовать date +%s.%N. ( %Nэто наносекунды со всей второй секунды.)
CVn
Хорошая точка зрения. Я думал об этом сразу после того, как покинул клавиатуру, но не вернулся. ^^ Также помните, OP, что «дата» сама добавит несколько миллисекунд к времени выполнения.
Роб Бос
2
@ChrisH Ох. Хорошо, указывая на это; Арифметическое расширение Bash только для целых чисел. Я вижу два очевидных варианта; либо вычеркните период в строке формата даты (и обработайте полученное значение как наносекунды с начала эпохи), так что используйте date +%s%Nили используйте что-то более подходящее, например, bcдля вычисления фактического времени выполнения из двух значений, как предлагает jwchew. Тем не менее, я чувствую, что этот подход - неоптимальный способ сделать это; timeзначительно лучше, если доступно, по причинам, изложенным выше.
CVn
10
Просто установите bcи сделайте следующее:runtime=$( echo "$end - $start" | bc -l )
redolent
10
Если ваш сценарий занимает несколько минут, используйте:echo "Duration: $((($(date +%s)-$start)/60)) minutes
rubo77
75

Просто позвоните timesбез аргументов при выходе из сценария.

С kshили zshвы также можете использовать timeвместо. С zsh, timeтакже даст вам время настенные часы в дополнение к времени процессора пользователя и системы .

Чтобы сохранить статус выхода вашего скрипта, вы можете сделать это:

ret=$?; times; exit "$ret"

Или вы также можете добавить ловушку EXIT:

trap times EXIT

Таким образом, время будет вызываться всякий раз, когда оболочка выходит и статус выхода будет сохранен.

$ bash -c 'trap times EXIT; : {1..1000000}'
0m0.932s 0m0.028s
0m0.000s 0m0.000s
$ zsh -c 'trap time EXIT; : {1..1000000}'
shell  0.67s user 0.01s system 100% cpu 0.677 total
children  0.00s user 0.00s system 0% cpu 0.677 total

Также обратите внимание , что все bash, kshи zshимеют $SECONDSспециальную переменную , которая автоматически инкрементируется каждый второй. В обоих случаях zshи ksh93эта переменная также может быть сделано с плавающей точкой (с typeset -F SECONDS) , чтобы получить более высокую точность. Это только время настенных часов, а не время процессора.

Стефан Шазелас
источник
12
Эта переменная $ SECONDS очень полезна, спасибо!
Эндибакли
Ваш подход устраняет влияние временных событий? - Я думаю, что ваш подход близок timeподходу, представленному ранее. - Я думаю, что timeitподход, представленный в коде MATLAB, может быть полезен здесь.
Лео Леопольд Герц 준영
2
Привет, @ Стефан Шазелас. Я обнаружил, timesчто не дает время на стену, верно? например,bash -c 'trap times EXIT;sleep 2'
user15964
Аналогично unix.stackexchange.com/questions/269789/…
Ахан,
32

Я немного опоздал к победе, но хотел опубликовать свое решение (с точностью до секунды) на случай, если другие натолкнутся на эту тему через поиск. Выходные данные имеют формат дней, часов, минут и, наконец, секунд:

res1=$(date +%s.%N)

# do stuff in here

res2=$(date +%s.%N)
dt=$(echo "$res2 - $res1" | bc)
dd=$(echo "$dt/86400" | bc)
dt2=$(echo "$dt-86400*$dd" | bc)
dh=$(echo "$dt2/3600" | bc)
dt3=$(echo "$dt2-3600*$dh" | bc)
dm=$(echo "$dt3/60" | bc)
ds=$(echo "$dt3-60*$dm" | bc)

printf "Total runtime: %d:%02d:%02d:%02.4f\n" $dd $dh $dm $ds

Надеюсь, кто-то найдет это полезным!

jwchew
источник
1
Я думаю, что нет другого (только bash) способа, кроме использования 'bc' для выполнения вычислений. Кстати, действительно хороший сценарий;)
ТВ
1
Имейте в виду, что FreeBSD dateне поддерживает точность до секунды и просто добавляет литерал « N» к метке времени.
Антон Самсонов
1
Очень хороший сценарий, но мой bash не обрабатывает вторую часть. Я также узнал, что / 1 в bc эффективно удаляет это, поэтому я добавил @ / 1 @ в вычисление $ ds, и теперь он отображает очень хорошие вещи!
Даниэль
1
Ьс поддерживает по модулю: dt2=$(echo "$dt%86400" | bc). Просто говорю ... Кстати, я предпочитаю форму, dt2=$(bc <<< "${dt}%86400")но это полностью личное.
Dani_l
16

Мой метод для bash:

# Reset BASH time counter
SECONDS=0
    # 
    # do stuff
    # 
ELAPSED="Elapsed: $(($SECONDS / 3600))hrs $((($SECONDS / 60) % 60))min $(($SECONDS % 60))sec"
отметка
источник
Это показывает прошедшее время, но не показывает «мелкозернистый вывод, такой как время процессора, время ввода-вывода», как было запрошено в вопросе.
roaima
3
Те, кто ищет ответ на поставленный вопрос, могут найти это решение полезным. Ваш комментарий будет лучше адресован вопросу ОП.
Марк
5
#!/bin/bash
start=$(date +%s.%N)

# HERE BE CODE

end=$(date +%s.%N)    
runtime=$(python -c "print(${end} - ${start})")

echo "Runtime was $runtime"

Да, это вызывает Python, но если вы можете с этим смириться, то это довольно хорошее и лаконичное решение.

Alex
источник
5
Помните, что выполнение этой pythonкоманды в подоболочке и чтение ее выходных данных в большинстве современных систем займет несколько миллионов наносекунд. То же самое для бега date.
Стефан Шазелас
7
«Несколько миллионов наносекунд» - это несколько миллисекунд. Люди, синхронизирующие сценарии bash, обычно не очень обеспокоены этим (если они не запускают несколько миллиардов сценариев в мегасекунду).
Зилк
2
Также обратите внимание, что, даже если вычисление выполняется внутри процесса Python, значения были полностью собраны до вызова Python. Время выполнения скрипта будет измерено правильно, без накладных расходов в «миллионы наносекунд».
Виктор Шредер
5

Этот вопрос довольно старый, но, пытаясь найти мой любимый способ сделать это, эта тема поднялась высоко ... и я удивлен, что никто не упомянул об этом:

perf stat -r 10 -B sleep 1

'perf' - это инструмент анализа производительности, включенный в ядро ​​в разделе 'tools / perf' и часто доступный для установки в виде отдельного пакета ('perf' в CentOS и 'linux-tools' в Debian / Ubuntu). В Linux Kernal perf Wiki гораздо больше информации об этом.

Запуск «perf stat» дает довольно много подробностей, включая среднее время выполнения в конце:

1.002248382 seconds time elapsed                   ( +-  0.01% )
Zaahid
источник
2
Что такое perf?
слой B
@B Layer - отредактировал мой ответ, чтобы дать краткое описание «perf».
Заахид
1
perf statтеперь жалуется на «Возможно, у вас нет разрешения на сбор статистики». если вы не запускаете некоторые команды sudo, что делает его бесполезным во всех сценариях, когда вы не полностью владеете целевой машиной.
Аламар
4

Небольшая функция оболочки, которая может быть добавлена ​​перед командами для измерения их времени:

tm() {
  s=$(date +%s)
  $@
  rc=$?
  echo "took $[$(date +%s)-$s] seconds. return code $rc" >&2
  return $rc
}

Затем используйте его в своем скрипте или в командной строке следующим образом:

tm the_original_command with all its parameters
Евгений
источник
Стоит добавить миллисекунды, попробовал ilke s=$(date +%s000), не уверен, работает ли он.
Лоретопариси
3

Вот вариант ответа Алекса. Меня волнуют только минуты и секунды, но я также хотел, чтобы он был отформатирован по-другому. Итак, я сделал это:

start=$(date +%s)
end=$(date +%s)
runtime=$(python -c "print '%u:%02u' % ((${end} - ${start})/60, (${end} - ${start})%60)")
mpontillo
источник
1
Почему отрицательный голос? У меня есть ошибка?
mpontillo
3
#!/bin/csh
#PBS -q glean
#PBS -l nodes=1:ppn=1
#PBS -l walltime=10:00:00
#PBS -o a.log
#PBS -e a.err
#PBS -V
#PBS -M shihcheng.guo@gmail.com
#PBS -m abe
#PBS -A k4zhang-group
START=$(date +%s)
for i in {1..1000000}
do
echo 1
done
END=$(date +%s)
DIFF=$(echo "$END - $START" | bc)
echo "It takes DIFF=$DIFF seconds to complete this task..."
Шичэн Го
источник
7
Чем это на самом деле отличается от других ответов, которые уже даны, которые используют dateдо и после сценария и выдают разницу между ними?
Эрик Ренуф
3

Лично мне нравится оборачивать весь мой код скрипта в какую-то «основную» функцию, например:

main () {
 echo running ...
}

# stuff ...

# calling the function at the very end of the script
time main

Обратите внимание, насколько просто использовать timeкоманду в этом сценарии. Очевидно, вы не измеряете точное время, включая время синтаксического анализа, но я нахожу его достаточно точным в большинстве ситуаций.

LeZuse
источник
2

Используя только bash, можно также измерить и рассчитать продолжительность времени для части сценария оболочки (или истекшее время для всего сценария):

start=$SECONDS

... # do time consuming stuff

end=$SECONDS

Теперь вы можете либо просто распечатать разницу:

echo "duration: $((end-start)) seconds."

если вам нужна только дополнительная длительность, сделайте:

echo "duration: $((SECONDS-start)) seconds elapsed.."

Вы также можете сохранить продолжительность в переменной:

let diff=end-start
gauteh
источник
^ Это ответ, ребята. Или проще: $ SECONDS в конце. Это встроенная переменная Bash. Все остальные ответы просто делают дополнительную работу, чтобы заново изобрести это колесо ...
Скотт Прайв
1
#!/bin/bash
begin=$(date +"%s")

Script

termin=$(date +"%s")
difftimelps=$(($termin-$begin))
echo "$(($difftimelps / 60)) minutes and $(($difftimelps % 60)) seconds elapsed for Script Execution."
Арун Биной
источник
1

Функция синхронизации, основанная SECONDSтолько на гранулярности второго уровня, не использует никаких внешних команд:

time_it() {
  local start=$SECONDS ts ec
  printf -v ts '%(%Y-%m-%d_%H:%M:%S)T' -1
  printf '%s\n' "$ts Starting $*"
  "$@"; ec=$?
  printf -v ts '%(%Y-%m-%d_%H:%M:%S)T' -1
  printf '%s\n' "$ts Finished $*; elapsed = $((SECONDS-start)) seconds"
  # make sure to return the exit code of the command so that the caller can use it
  return "$ec"
}

Например:

time_it sleep 5

дает

2019-03-30_17:24:37 Starting sleep 5 2019-03-30_17:24:42 Finished
sleep 5; elapsed = 5 seconds
codeforester
источник