Jak efektywnie uzyskać czas wykonania skryptu?

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

Obecnie –

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

Pokazuje tylko czas rozpoczęcia i zakończenia skryptu. Czy byłby to można wyświetlić szczegółowe dane wyjściowe, takie jak czas procesora / czas io itp.?

Komentarze

Odpowiedź

Jus Nie używaj time podczas wywoływania skryptu:

time yourscript.sh 

Komentarze

  • Wyprowadza trzy razy: real, user i sys. Aby poznać ich znaczenie, zobacz tutaj .
  • i ” real to prawdopodobnie to, co ludzie chcą wiedzieć – ” Rzeczywiste to zegar ścienny – czas od początku do końca połączenia ”
  • Czy istnieje sposób na przechwycenie standardowego wyjścia do pliku? Na przykład coś takiego jak time $( ... ) >> out.txt
  • Możesz to również zrobić z sekwencjami poleceń w wierszu poleceń, np .: time (command1 && command2)
  • Albo mógłby w swoim skrypcie po prostu zrobić: echo $ SECONDS zakładając, że to bash ….

Odpowiedź

Jeśli time nie ma „opcji”,

start=`date +%s` stuff end=`date +%s` runtime=$((end-start)) 

Komentarze

  • Pamiętaj, że działa to tylko wtedy, gdy ' nie potrzebujesz precyzji poniżej sekundy. W przypadku niektórych zastosowań może to być dopuszczalne , dla innych nie. Aby uzyskać nieco większą precyzję (' nadal wywołujesz date na przykład dwukrotnie, więc możesz na przykład w najlepiej w praktyce uzyskaj milisekundową precyzję, a prawdopodobnie mniej), spróbuj użyć date +%s.%N. (%N to nanosekundy od całej sekundy .)
  • @ChrisH Oh. Dobrze to wskazać; interpretacja arytmetyczna basha jest tylko liczbami całkowitymi. Widzę dwa obvi wiele opcji; albo porzuć kropkę w ciągu formatu daty (i potraktuj wynikową wartość jako nanosekundy od epoki), więc użyj date +%s%N lub użyj czegoś bardziej wydajnego, takiego jak bc aby obliczyć rzeczywisty czas działania na podstawie dwóch wartości, jak sugeruje jwchew. Mimo to uważam, że to podejście jest nieoptymalnym sposobem zrobienia tego; time jest znacznie lepszy, jeśli jest dostępny, z powodów przedstawionych powyżej.
  • Po prostu zainstaluj bc, a następnie zrób to: runtime=$( echo "$end - $start" | bc -l )
  • Jeśli wykonanie skryptu trwa kilka minut, użyj: echo "Duration: $((($(date +%s)-$start)/60)) minutes
  • Dalej do @ rubo77 komentarz, jeśli twój skrypt trwa kilka godzin, użyj hours=$((runtime / 3600)); minutes=$(( (runtime % 3600) / 60 )); seconds=$(( (runtime % 3600) % 60 )); echo "Runtime: $hours:$minutes:$seconds (hh:mm:ss)"

Odpowiedz

Po prostu wywołaj times bez argumentów po zamknięciu skryptu.

Z ksh lub zsh, możesz też zamiast tego użyć time. W przypadku zsh time oprócz użytkownika i poda również czas zegara ściennego system czas procesora.

Aby zachować stan zakończenia skryptu, możesz to zrobić:

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

Lub ty można również dodać pułapkę na EXIT:

trap times EXIT 

W ten sposób czasy będą wywoływane za każdym razem, gdy powłoka zakończy działanie i status wyjścia zostanie zachowany.

$ 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 

Pamiętaj też, że wszystkie bash, ksh i zsh mają $SECONDS zmienną specjalną, która jest automatycznie zwiększana co sekundę. W obu zsh i ksh93, ta zmienna również może być zmiennoprzecinkowa (z typeset -F SECONDS ), aby uzyskać większą precyzję. To jest tylko czas zegara ściennego, a nie czas procesora.

Komentarze

  • Ta zmienna $ SECONDS jest bardzo przydatna, dziękuję!
  • Czy Twoje podejście eliminuje wpływ wydarzeń czasowych? – – Myślę, że Twoje podejście jest bliskie time podejściu przedstawionemu wcześniej.- – Myślę, że podejście timeit przedstawione w kodzie MATLAB może być tutaj przydatne.
  • Cześć, @St é phane Chazelas. Zauważyłem, że times nie ' nie podaje czasu na ścianie, prawda? na przykład bash -c 'trap times EXIT;sleep 2'
  • @Binarus, tak, ta funkcja pochodzi z ksh. W przeciwieństwie do ksh93 / zsh, poza tym, że bash nie obsługuje zmiennoprzecinkowych, ' jest również uszkodzony przez to, że po ustawieniu SECONDS na 0, zmieni się na 1 z 0 na 1 sekundę później ( ponieważ uwzględnia tylko wynik time(), który ma tylko pełną, sekundową szczegółowość).
  • @Binarus, a nie ' s od 0 do 1. Jeśli ustawisz SECONDS na 0 o godzinie 12:00: 00.000, po jednej sekundzie zmieni się na 1. Ale jeśli ustawisz go na 12: 00: 00.999, zmieni się na 1 milisekundę później. Ale tak, zgadzam się, że prawdopodobnie nie ' nie ma większego znaczenia w praktyce, jeśli ' i tak nie ma precyzji poniżej sekundy.

Odpowiedź

Trochę się spóźniłem, ale chciałem opublikować swoje rozwiązanie (z dokładnością do sekundy ) na wypadek, gdyby ktoś natknął się na ten wątek podczas wyszukiwania. Dane wyjściowe są w formacie dni, godzin, minut i wreszcie 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) LC_NUMERIC=C printf "Total runtime: %d:%02d:%02d:%02.4f\n" $dd $dh $dm $ds 

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

Komentarze

  • Wydaje mi się, że nie ma innego (tylko bash) sposobu poza użyciem ' bc ', aby wykonać obliczenia. Przy okazji, naprawdę dobry skrypt;)
  • Uważaj, FreeBSD ' s date nie obsługuje precyzji poniżej sekundy i po prostu doda literał „N” do sygnatury czasowej.
  • Bardzo ładny skrypt, ale mój bash nie ' t obsłużyć drugą część. Dowiedziałem się również, że / 1 w bc skutecznie usuwa to, więc dodałem @ / 1 @ do obliczenia $ ds i wyświetla teraz bardzo ładnie!
  • bc obsługuje modulo: dt2=$(echo "$dt%86400" | bc). Po prostu mówię … Swoją drogą wolę formularz dt2=$(bc <<< "${dt}%86400"), ale ' jest całkowicie osobisty.
  • Nie ' nie wiem jeszcze nic o bc, ale podział na 86400 sprawia, że jestem nieufny wobec następujący powód: Są dni, które nie mają 86400 sekund, na przykład dni, w których czas jest przełączany z czasu letniego na normalny czas i odwrotnie, lub dni z sekundami przestępnymi. Jeśli takie dni stanowią część przedziału czasu, który chcesz obliczyć, tj. Znajdują się między res1 a res2 (łącznie z tymi wartościami), skrypt prawdopodobnie się nie powiedzie.

Odpowiedź

Moja metoda dla bash :

# Reset BASH time counter SECONDS=0 # # do stuff # ELAPSED="Elapsed: $(($SECONDS / 3600))hrs $((($SECONDS / 60) % 60))min $(($SECONDS % 60))sec" 

Komentarze

  • Pokazuje upływ czasu, ale nie ' t pokaż ” drobnoziarniste dane wyjściowe, takie jak czas procesora, czas we / wy ” zgodnie z żądaniem w pytaniu.
  • Osoby poszukujące odpowiedzi na zadane pytanie prawdopodobnie uznają to rozwiązanie za przydatne. Twój komentarz byłby lepiej zaadresowany do pytania OP.

Odpowiedź

Osobiście chciałbym podsumować wszystkie moje kod skryptu w jakiejś „głównej” funkcji, takiej jak ta:

main () { echo running ... } # stuff ... # calling the function at the very end of the script time main 

Zwróć uwagę, jak łatwo jest użyć polecenia time 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.

Komentarze

  • Świetne rozwiązanie, nie ' nie wymaga dodatkowych narzędzi i jest samodzielne.

Odpowiedź

#!/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 Pythona, ale jeśli możesz z tym żyć, to jest to całkiem fajne, zwięzłe rozwiązanie.

Komentarze

  • Uważaj, że uruchomienie tego polecenia python w podpowłoce i odczytanie jego danych wyjściowych zajmie kilka milionów nanosekund w większości obecnych systemów. To samo przy uruchomieniu date.
  • ” Kilka milionów nanosekund ” to kilka milisekund . Ludzi synchronizujących skrypty bash zwykle nie przejmują się tym zbytnio (chyba że uruchamiają kilka miliardów skryptów na megasekundę).
  • Zauważ też, ż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 zostanie zmierzony poprawnie, bez ” milionów nanosekund ” narzutu.
  • time main powyżej jest o wiele bardziej przejrzysty …tak, możemy żyć z dużą degradacją, ale ' jest o wiele lepiej bez, więc niech ' zostanie inżynierami!
  • $(echo $end - $start | bc) zrobi to samo bez Pythona

Odpowiedź

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 wspomniał:

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 oddzielny pakiet („perf” w CentOS i „linux-tools” w Debianie / Ubuntu). Linux Kernal perf Wiki zawiera znacznie więcej informacji na ten temat.

Uruchomienie „statystyki perf” daje sporo szczegółów, w tym średni czas wykonywania zaraz na końcu:

1.002248382 seconds time elapsed ( +- 0.01% ) 

Komentarze

  • Co to jest perf?
  • @B Layer – edytowałem moją odpowiedź t podaj krótki opis ' perf '.
  • perf stat teraz narzeka na ” Możesz nie mieć uprawnień do gromadzenia statystyk. „, chyba że uruchomisz niektóre polecenia z sudo, co czyni go bezużytecznym we wszystkich scenariuszach, w których nie ' nie posiadasz całkowicie maszyny docelowej.
  • Niesamowite, dzięki! O wiele bardziej szczegółowe rozwiązanie niż sugerują time inni. W szczególności time nie ma zastosowania do pomiaru rozwiązań konkurencji kodu (tak jak nie ' t czasu drukowania w milisekundach i mniej) , podczas gdy Twoje rozwiązanie tak.

Odpowiedź

Mała funkcja powłoki, którą można dodać przed polecenia do mierzenia ich czasu:

tm() { local start=$(date +%s) $@ local exit_code=$? echo >&2 "took ~$(($(date +%s)-${start})) seconds. exited with ${exit_code}" return $exit_code } 

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

tm the_original_command with all its parameters 

Komentarze

  • Warto byłoby dodać milisekundy, próbowano ilke s=$(date +%s000), nie jestem pewien jeśli to zadziała.
  • @loretoparisi przez milisekundy, możesz spróbować date +%s%N. Zobacz serverfault.com/questions/151109/…
  • To jest świetne, ale bezpieczniejsze użyj "$@"

Odpowiedz

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

Komentarze

  • Czym to się naprawdę różni od innych już udzielonych odpowiedzi, w których zastosowano date przed i po skrypt i wypisuje różnicę między nimi?

Odpowiedź

  1. Po prostu użyj time [any command] . Na przykład: time sleep 1 będzie spać w czasie rzeczywistym (tj. Według czasu stopera) od ~ 1.000 do ~ 1.020 s, jak pokazano tutaj:

      $ time sleep 1 real 0m1.011s user 0m0.004s sys 0m0.000s  

    Cóż za piękna rzecz. Możesz umieścić po nim dowolne polecenie, a wynik zostanie wyświetlony w ładnej, czytelnej dla człowieka formie. Naprawdę lubię używać go do kompilacji czasu. Np .:

      # time your "make" build time make # time your "Bazel" build time bazel build //path/to/some:target  

    … lub dla operacji git, które potencjalnie mogą być naprawdę długi, więc mogę rozwinąć realistyczne oczekiwania mentalne:

      # time how long it takes to pull from a massive repo when # I"m working from home during COVID-19. NB: `git pull` # is sooooo much slower than just pulling the one branch # you need with `git pull origin <branch>`, so just fetch # or pull what you need! time git pull origin master  
  2. Dla bardziej dostosowanych potrzeb czasowych , gdzie może być konieczne zmodyfikowanie wyniku lub przekonwertowanie go na inne formy w bash użyj wewnętrznej zmiennej $SECONDS. Oto „demo, w tym konwersja tych sekund na inne jednostki, takie jak minuty zmiennoprzecinkowe:

    Zwróć uwagę, że dt_min jest zaokrąglane z 0.01666666666... (1 sekunda = tyle minut) do 0.017 w tym przypadku, ponieważ używam funkcji printf do zaokrąglania. Poniższa sleep 1; część jest miejscem, w którym „wezwałbyś swój skrypt do uruchomienia i czasu, ale ja” zamiast tego śpię tylko przez 1 sekundę ze względu na to demo.

    Polecenie:

      start=$SECONDS; sleep 1; end=$SECONDS; dt_sec=$(( end - start )); \ dt_min=$(printf %.3f $(echo "$dt_sec/60" | bc -l)); \ echo "dt_sec = $dt_sec; dt_min = $dt_min"  

    Wynik:

      dt_sec = 1; dt_min = 0.017  

Powiązane:

  1. Czytaj więcej około bc i printf w mojej odpowiedzi tutaj: https://stackoverflow.com/questions/12722095/how-do-i-use-floating-point-division-in-bash/58479867#58479867
  2. Nie pamiętam już, gdzie po raz pierwszy dowiedziałem się o poleceniu time, ale mogło to pochodzić z @Trudbert „jest tutaj odpowiedzią .

Odpowiedź

Używając tylko bash 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 

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

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

jeśli potrzebujesz tylko przyrostowego czasu trwania, wykonaj:

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

Czas trwania możesz również zapisać w zmiennej:

let diff=end-start 

Komentarze

  • ^ TO jest odpowiedź dla ludzi. Lub prościej: na koniec $ SECONDS. Jest to ' zmienna typu BUILT-IN Bash. Wszystkie inne odpowiedzi wymagają tylko dodatkowej pracy, aby ponownie wymyślić to koło …
  • To tylko powtórzenie wcześniejszej odpowiedzi Marksa unix.stackexchange .com / a / 340156/155362 .

Odpowiedź

Funkcja pomiaru czasu oparta na SECONDS, ograniczone tylko do szczegółowości drugiego poziomu, nie używa żadnych poleceń zewnętrznych:

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 

Odpowiedź

Czy używać wbudowanego czasu bash?

time: time [-p] PIPELINE Execute PIPELINE and print a summary of the real time, user CPU time, and system CPU time spent executing PIPELINE when it terminates. The return status is the return status of PIPELINE. The `-p" option prints the timing summary in a slightly different format. This uses the value of the TIMEFORMAT variable as the output format. 

Przykład:

TIMEFORMAT="The command took %Rs" time { sleep 0.1 } 

Wynik:

The command took 0.108s 

Odpowiedź

Oto odmiana Alexa ”odpowiedź. Zależy mi tylko na minutach i sekundach, ale chciałem też mieć inny format. Zrobiłem więc tak:

start=$(date +%s) end=$(date +%s) runtime=$(python -c "print "%u:%02u" % ((${end} - ${start})/60, (${end} - ${start})%60)") 

Komentarze

  • Dlaczego głos przeciw? Czy mam błąd?
  • Używanie python do tego obliczenia też wygląda na błąd, przepraszam. Dlaczego ludzie mogą ' po prostu zadzwonić pod numer time jest poza mną.

Odpowiedź

#!/bin/bash begin=$(date +"%s") Script termin=$(date +"%s") difftimelps=$(($termin-$begin)) echo "$(($difftimelps / 60)) minutes and $(($difftimelps % 60)) seconds elapsed for Script Execution." 

Odpowiedź

Zaakceptowane rozwiązanie przy użyciu time zapisuje do stderr.
Rozwiązanie korzystające z times zapisuje do stdout.
Rozwiązania używające $SECONDS nie mają dokładności poniżej sekundy.
Inne rozwiązania obejmują wywoływanie programów zewnętrznych, takich jak date lub perf, co nie jest wydajne.
Jeśli jesteś w porządku z którymkolwiek z nich, użyj go.

Ale jeśli potrzebujesz wydajnego rozwiązania , aby uzyskać czasy z dokładnością do milisekund i potrzebujemy ich w zmiennych takich, że oryginalne wyjście pozostaje niezakłócone możesz łączyć podstawianie procesu z niektórymi przekierowaniami wokół time, co jest znacznie szybsze niż wywołanie zewnętrznych programów i zezwala na przekierowania wokół skryptu opakowującego taktowanie, jak w oryginalnym poleceniu / skrypcie.

# Preparations: Cmd=vgs # example of a program to be timed which is writing to stdout and stderr Cmd="eval { echo stdout; echo stderr >&2; sleep 0.1; }" # other example; replace with your own TIMEFORMAT="%3R %3U %3S" # make time output easy to parse 

Wybierz jeden z następujących wariantów analizowania danych wyjściowych time odpowiedni do Twoich potrzeb:
Najkrótszy wariant, w którym stdout z $Cmd jest zapisany w stderr i nic do stdout:

read Elapsed User System < <({ time $Cmd 2>&3; } 3>&2 2>&1 >&3) 

Dłuższy wariant, który zachowuje oryginalne stdout i stderr oddzielone od siebie:

{ read Elapsed User System < <({ time $Cmd 2>&4; } 4>&2 2>&1 >&3); } 3>&1 

Najbardziej skomplikowany wariant obejmuje zamknięcie dodatkowych deskryptorów plików w taki sposób, że $Cmd jest wywoływane tak, jakby nie było otaczającego go otoki czasowej i , takie jak vgs, nie narzekają na wyciekły deskryptory plików:

{ read Elapsed User System < <({ time $Cmd 2>&4 4>&-; } 4>&2 2>&1 >&3 3>&-); } 3>&1 

Ty może nawet sfałszować dodawanie zmiennoprzecinkowe w bash bez wywoływania bc, co byłoby znacznie wolniejsze:

CPU=`printf %04d $((10#${User/.}+10#${System/.}))` # replace with your own postprocessing echo CPU ${CPU::-3}.${CPU: -3} s, Elapsed $Elapsed s >&2 # redirected independent of $Cmd 

Możliwe wyniki z dwoma przykładami $Cmd na wolnym procesorze:

File descriptor 3 (/dev/pts/1) leaked on vgs invocation. Parent PID 10756: bash File descriptor 4 (/dev/pts/1) leaked on vgs invocation. Parent PID 10756: bash VG #PV #LV #SN Attr VSize VFree b3 3 24 0 wz--n- 1.31t 1.19t CPU 0.052 s, Elapsed 0.056 s 

Lub:

stdout stderr CPU 0.008 s, Elapsed 0.109 s 

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *