Jak skutecznie uzyskać czas wykonania skryptu?

339

Chciałbym wyświetlić czas zakończenia skryptu.

Obecnie robię to -

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

Ten program to czas rozpoczęcia i zakończenia skryptu. Czy byłoby możliwe wyświetlanie drobnoziarnistych danych wyjściowych, takich jak czas procesora / czas io itp.?

Mt.
źródło
2
stackoverflow.com/questions/385408/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件 事件
Myślę, że obecnie przyjęta odpowiedź nie jest wystarczająca ze względu na wahania czasowe.
Léo Léopold Hertz -
1
@CiroSantilli 事件 事件 2016 六四 事件 法轮功 Myślę, że twój proponowany wątek wciąż nie jest wystarczający, aby wyeliminować efekt zdarzeń czasowych.
Léo Léopold Hertz -
Istnieje polecenie, które można uruchomić, a następnie automatycznie odmierza wszystkie polecenia unixowe, zapomniałeś jak włączyć?
proszek 366

Odpowiedzi:

448

Po prostu użyj, timegdy wywołasz skrypt:

time yourscript.sh
Trudbert
źródło
61
To generuje trzykrotnie: real, useri sys. Ich znaczenie znajduje się tutaj .
Garrett,
27
a „prawdziwe” to prawdopodobnie to, co ludzie chcą wiedzieć - „Prawdziwy to zegar ścienny - czas od początku do końca połączenia”
Brad Parks
3
Czy istnieje sposób na przechwycenie standardowego wejścia do pliku? Na przykład coś takiegotime $( ... ) >> out.txt
Jon
1
Możesz to również zrobić z sekwencjami poleceń w wierszu poleceń, np .:time (command1 && command2)
meetar
1
Lub może, w swoim skrypcie, po prostu zrobić: powtórzyć $ SECONDS, zakładając, że to bash ....
Crossfit_and_Beer
169

Jeśli timenie ma takiej opcji,

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

runtime=$((end-start))
Rob Bos
źródło
30
Pamiętaj, że działa to tylko wtedy, gdy nie potrzebujesz precyzji poniżej sekundy. Dla niektórych zastosowań, które mogą być do zaakceptowania, dla innych nie. Dla nieco lepszej precyzji (jesteś ciągle powołując się datedwa razy, na przykład, więc może w najlepszym uzyskać dokładność milisekundy w praktyce, a prawdopodobnie mniej), spróbuj użyć date +%s.%N. ( %Nto nanosekundy od całej sekundy.)
CVn
Słuszna uwaga. Myślałem o tym zaraz po opuszczeniu klawiatury, ale nie wróciłem. ^^ Pamiętaj też, OP, że „data” sama doda kilka milisekund do czasu wykonywania.
Rob Bos
2
@ChrisH Oh. Dobrze to zaznaczać; interpretacja arytmetyczna bash jest liczbą całkowitą. Widzę dwie oczywiste opcje; albo porzuć kropkę w ciągu formatu daty (i traktuj wynikową wartość jako nanosekundę od epoki), więc użyj date +%s%Nlub użyj czegoś bardziej zdolnego bcdo obliczenia rzeczywistego czasu działania z dwóch wartości, jak sugeruje jwchew. Mimo to uważam, że takie podejście nie jest optymalne; timejest znacznie lepszy, jeśli jest dostępny, z powodów opisanych powyżej.
CVn
10
Wystarczy zainstalować, bca następnie wykonaj następujące czynności:runtime=$( echo "$end - $start" | bc -l )
redolent
10
Jeśli twój skrypt zajmuje kilka minut, użyj:echo "Duration: $((($(date +%s)-$start)/60)) minutes
rubo77
75

Wystarczy timeswyjść bez argumentów po wyjściu ze skryptu.

Za pomocą kshlub zshmożesz także użyć timezamiast tego. Za pomocą zsh, timeda Ci także zegar ścienny oprócz czasu użytkownika i procesora systemu .

Aby zachować status wyjścia skryptu, możesz go zrobić:

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

Możesz też dodać pułapkę na EXIT:

trap times EXIT

W ten sposób czasy będą wywoływane przy każdym wyjściu powłoki i zachowaniu statusu wyjścia.

$ 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

Należy również pamiętać, że wszystko bash, kshi zshposiada $SECONDSspecjalną zmienną, która zostaje automatycznie zwiększana co drugi. W obu zshi ksh93, że zmienna może być również wykonane zmiennoprzecinkowej (Z typeset -F SECONDS), aby uzyskać większą precyzję. Jest to tylko zegar ścienny, a nie czas procesora.

Stéphane Chazelas
źródło
12
Ta zmienna $ SECONDS jest bardzo przydatna, dzięki!
andybuckley
Czy twoje podejście eliminuje wpływ zdarzeń czasowych? - - Myślę, że twoje podejście jest bliskie timepodejściu zaprezentowanemu wcześniej. - - Myślę, że timeitpodejście przedstawione w kodzie MATLAB może być przydatne tutaj.
Léo Léopold Hertz -
2
Cześć, @ Stéphane Chazelas. Znalazłem, timesże nie daje czasu na ścianę, prawda? na przykładbash -c 'trap times EXIT;sleep 2'
user15964
32

Jestem trochę spóźniony na modę, ale chciałem opublikować moje rozwiązanie (dla precyzji poniżej sekundy) na wypadek, gdyby inni natknęli się na ten wątek podczas wyszukiwania. Dane wyjściowe mają format dni, godzin, minut, a na koniec sekund:

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

Mam nadzieję, że ktoś tam uzna to za przydatne!

jwchew
źródło
1
Wydaje mi się, że nie ma innego (tylko bash) sposobu niż użycie „bc” do wykonania obliczeń. BTW naprawdę dobry skrypt;)
tvl
1
Uważaj, że FreeBSD datenie obsługuje precyzji poniżej sekundy i po prostu doda dosłowne „ N” do znacznika czasu.
Anton Samsonov,
1
Bardzo fajny skrypt, ale mój bash nie obsługuje drugiej części. Dowiedziałem się również, że / 1 w bc skutecznie usuwa to, więc dodałem @ / 1 @ do obliczeń $ ds i wyświetla teraz bardzo fajnie!
Daniel
1
bc obsługuje modulo: dt2=$(echo "$dt%86400" | bc). Tylko mówię ... BTW Wolę formę, dt2=$(bc <<< "${dt}%86400")ale to całkowicie osobista.
Dani_l
16

Moja metoda na bash:

# Reset BASH time counter
SECONDS=0
    # 
    # do stuff
    # 
ELAPSED="Elapsed: $(($SECONDS / 3600))hrs $((($SECONDS / 60) % 60))min $(($SECONDS % 60))sec"
znak
źródło
To pokazuje upływ czasu, ale nie pokazuje „drobnoziarnistych danych wyjściowych, takich jak czas procesora, czas wejścia / wyjścia”, zgodnie z żądaniem w pytaniu.
roaima
3
Ci, którzy szukają odpowiedzi na postawione pytanie, prawdopodobnie uznają to rozwiązanie za przydatne. Twój komentarz byłby lepiej zaadresowany do pytania PO.
Mark
5
#!/bin/bash
start=$(date +%s.%N)

# HERE BE CODE

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

echo "Runtime was $runtime"

Tak, to wywołuje Python, ale jeśli możesz z tym żyć, jest to całkiem fajne, zwięzłe rozwiązanie.

Alex
źródło
5
Uwaga: uruchomienie tego pythonpolecenia w podpowłoce i odczytanie jego wyniku zajmie kilka milionów nanosekund w większości obecnych systemów. To samo dotyczy biegania date.
Stéphane Chazelas
7
„Kilka milionów nanosekund” to kilka milisekund. Ludzie mierzący skrypty bash zwykle nie przejmują się tym zbytnio (chyba że uruchamiają kilka miliardów skryptów na megasekundę).
Zilk
2
Zauważ również, że nawet jeśli obliczenia są wykonywane w procesie Pythona, wartości zostały w całości zebrane przed wywołaniem Pythona. Czas wykonania skryptu będzie mierzony poprawnie, bez narzutu „milionów nanosekund”.
Victor Schröder
5

To pytanie jest dość stare, ale próbując znaleźć mój ulubiony sposób zrobienia tego, ten wątek pojawił się wysoko ... i jestem zaskoczony, że nikt o nim nie wspominał:

perf stat -r 10 -B sleep 1

„perf” to narzędzie do analizy wydajności zawarte w jądrze pod „tools / perf” i często dostępne do zainstalowania jako osobny pakiet („perf” w CentOS i „linux-tools” w Debian / Ubuntu). Wiki systemu Linux Kernal perf zawiera o wiele więcej informacji na ten temat.

Uruchamianie „perf stat” podaje sporo szczegółów, w tym średni czas wykonania na samym końcu:

1.002248382 seconds time elapsed                   ( +-  0.01% )
Zaahid
źródło
2
Co to jest perf?
B Warstwa
@B Layer - zredagowałem moją odpowiedź, aby podać krótki opis „perf”.
Zaahid
1
perf statteraz skarży się na „Możesz nie mieć pozwolenia na zbieranie statystyk”. chyba że uruchomisz kilka poleceń sudo, co czyni go bezużytecznym we wszystkich scenariuszach, w których nie jesteś całkowicie właścicielem maszyny docelowej.
alamar
4

Mała funkcja powłoki, którą można dodać przed komendami, aby zmierzyć ich czas:

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

Następnie użyj go w skrypcie lub w wierszu poleceń w następujący sposób:

tm the_original_command with all its parameters
Jewgienij
źródło
Warto byłoby dodać milisekundy, wypróbować ilke s=$(date +%s000), nie jestem pewien, czy to działa.
loretoparisi
3

Oto odmiana odpowiedzi Alexa. Zależy mi tylko na minutach i sekundach, ale chciałem też, żeby był inaczej sformatowany. Więc zrobiłem to:

start=$(date +%s)
end=$(date +%s)
runtime=$(python -c "print '%u:%02u' % ((${end} - ${start})/60, (${end} - ${start})%60)")
mpontillo
źródło
1
Dlaczego głosowanie negatywne? Czy mam błąd?
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 [email protected]
#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..."
Shicheng Guo
źródło
7
Czym tak naprawdę różni się od innych już udzielonych odpowiedzi, które wykorzystują dateprzed i po skrypcie i generują różnicę między nimi?
Eric Renouf,
2

Używając tylko basha można również zmierzyć i obliczyć czas trwania części skryptu powłoki (lub czas, jaki upłynął dla całego skryptu):

start=$SECONDS

... # do time consuming stuff

end=$SECONDS

teraz możesz po prostu wydrukować różnicę:

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

jeśli potrzebujesz tylko przyrostowego czasu trwania:

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

Możesz także zapisać czas trwania w zmiennej:

let diff=end-start
gauteh
źródło
^ TO jest odpowiedź ludzie. Lub prościej: $ SECONDS na końcu. Jest to zmienna Bash BUILT-IN. Wszystkie pozostałe odpowiedzi tylko wykonują dodatkową pracę, aby ponownie wynaleźć to koło ...
Crossfit_and_Beer
2

Osobiście lubię zawijać cały mój kod skryptu w jakąś funkcję „główną”, taką jak:

main () {
 echo running ...
}

# stuff ...

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

Zauważ, jak łatwe jest użycie timepolecenia w tym scenariuszu. Oczywiście nie mierzysz dokładnego czasu, w tym czasu analizy skryptu, ale uważam, że jest on wystarczająco dokładny w większości sytuacji.

LeZuse
źródło
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."
Arun Binoy
źródło
1

Funkcja synchronizacji oparta na SECONDSograniczeniu do szczegółowości drugiego poziomu nie korzysta z żadnych zewnętrznych poleceń:

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"
}

Na przykład:

time_it sleep 5

daje

2019-03-30_17:24:37 Starting sleep 5 2019-03-30_17:24:42 Finished
sleep 5; elapsed = 5 seconds
codeforester
źródło