Cum să obțineți în mod eficient timpul de execuție al unui script?

Aș dori să afișez timpul de finalizare a unui script.

Ceea ce fac în prezent este –

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

Aceasta arată doar ora de începere și de sfârșit a scriptului. este posibil să afișați o ieșire cu granulație fină, cum ar fi timpul procesorului / ora io, etc?

Comentarii

Răspuns

Jus nu utilizați time când apelați scriptul:

time yourscript.sh 

Comentarii

  • Aceasta rezultă de trei ori: real, user și sys. Pentru semnificațiile acestora, consultați aici .
  • și ” real este probabil ceea ce oamenii vor să știe – ” Real este timpul ceasului de perete – timpul de la începutul până la sfârșitul apelului ”
  • Există o modalitate de a captura stdout-ul într-un fișier? De exemplu, ceva de genul time $( ... ) >> out.txt
  • Puteți face acest lucru și secvențelor de comenzi de pe linia de comandă, de exemplu: time (command1 && command2)
  • Sau ar putea, în scenariul său, să facă: ecou $ SECONDS presupunând că este bash ….

Răspuns

Dacă time nu este o opțiune,

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

Comentarii

  • Rețineți că acest lucru funcționează numai dacă nu aveți ‘ de nevoie de precizie în secundă. Pentru unele utilizări care ar putea fi acceptabile , pentru alții nu. Pentru o precizie puțin mai bună (‘ invocați în continuare date de două ori, de exemplu, așa că s-ar putea la cel mai bun obțineți precizie de milisecundă în practică și probabil mai puțină), încercați să utilizați date +%s.%N. (%N este nanosecunde din întreaga secundă .)
  • @ChrisH Oh. Bine arătându-l; expansiunea aritmetică bash este doar în întregime. Văd două obvi multe opțiuni; fie renunțați la perioadă în șirul de format de dată (și tratați valoarea rezultată ca nanosecunde din epocă), deci utilizați date +%s%N, sau folosiți ceva mai capabil ca bc pentru a calcula timpul real de rulare din cele două valori, așa cum sugerează jwchew. Totuși, consider că această abordare este un mod suboptim de a o face; time este considerabil mai bun dacă este disponibil, din motivele prezentate mai sus.
  • Instalați doar bc și apoi faceți acest lucru: runtime=$( echo "$end - $start" | bc -l )
  • Dacă scriptul dvs. durează câteva minute, utilizați: echo "Duration: $((($(date +%s)-$start)/60)) minutes
  • Mai departe de @ rubo77 comentariu, dacă scriptul dvs. durează câteva ore, utilizați hours=$((runtime / 3600)); minutes=$(( (runtime % 3600) / 60 )); seconds=$(( (runtime % 3600) % 60 )); echo "Runtime: $hours:$minutes:$seconds (hh:mm:ss)"

Răspundeți

Apelați doar times fără argumente la ieșirea din script.

Cu ksh sau zsh, puteți utiliza și time. Cu zsh, time vă va oferi, de asemenea, ora ceasului de perete, în plus față de utilizator și system CPU time.

Pentru a păstra starea de ieșire a scriptului, îl puteți face:

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

Sau dvs. poate adăuga, de asemenea, o capcană pe EXIT:

trap times EXIT 

În acest fel, orele vor fi apelate de fiecare dată starea de ieșire va fi păstrată.

$ 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 

De asemenea, rețineți că toate bash, ksh și zsh au o variabilă specială $SECONDS care se incrementează automat în fiecare secundă. Atât în zsh cât și în ksh93, acea variabilă poate fi transformată și în virgulă mobilă (cu typeset -F SECONDS ) pentru a obține mai multă precizie. Aceasta este doar ora ceasului de perete, nu timpul procesorului.

Comentarii

  • Această variabilă $ SECONDS este foarte utilă, mulțumesc!
  • Abordarea dvs. elimină efectul evenimentelor temporale? – – Cred că abordarea dvs. este aproape de time abordarea prezentată mai devreme.- – Cred că abordarea timeit prezentată în codul MATLAB poate fi utilă aici.
  • Bună ziua, @St é phane Chazelas. Am găsit că times nu ‘ nu oferă timp de perete, nu? de exemplu, bash -c 'trap times EXIT;sleep 2'
  • @Binarus, da, această caracteristică provine de la ksh. Spre deosebire de ksh93 / zsh, în afară de bash care nu suportă virgulă mobilă, ‘ s-a rupt și după ce ați setat SECONDS la 0, se va schimba la 1 de la 0 la 1 secundă mai târziu ( deoarece are în vedere doar rezultatul time(), care are doar o a doua granularitate completă).
  • @Binarus, nu că ‘ s între 0 și 1. Dacă setați SECONDS la 0 la 12: 00: 00.000, se va schimba la 1 o secundă mai târziu. Dar dacă îl setați la 12: 00: 00.999, acesta se va schimba la 1 milisecundă mai târziu. Dar da, sunt de acord că probabil ‘ nu contează prea mult în practică dacă ‘ oricum nu aveți precizie sub-secundă.

Răspuns

Am „întârziat la bandwagon, dar am vrut să postez soluția mea (pentru precizie în secundă) ) în cazul în care se întâmplă ca alții să dea peste acest fir prin căutare. Rezultatul este în format de zile, ore, minute și, în final, secunde:

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 

Sper că cineva acolo găsește acest lucru util!

Comentarii

  • Cred că nu există altă cale (numai bash) decât utilizarea ‘ bc ‘ pentru a face calculele. BTW script foarte bun;)
  • Atenție la FreeBSD ‘ s date nu acceptă precizia sub-secundă și va adăuga doar literal „N” la marcajul de timp.
  • Script foarte frumos, dar bash-ul meu nu ‘ t se ocupă de partea secundară. De asemenea, am aflat că / 1 în bc elimină efectiv acest lucru, așa că am adăugat @ / 1 @ la calculul $ ds și afișează lucruri foarte frumoase acum!
  • bc acceptă modulul: dt2=$(echo "$dt%86400" | bc). Spunând doar … BTW prefer forma dt2=$(bc <<< "${dt}%86400"), dar ‘ este complet personal.
  • Nu ‘ nu știu încă nimic despre bc, dar împărțirea cu 86400 mă face să nu am încredere în următorul motiv: Există zile care nu au 86400 de secunde, de exemplu zile în care timpul este comutat de la ora de vară la ora normală și invers și zile cu secunde de salt. Dacă astfel de zile fac parte din intervalul de timp pe care doriți să îl calculați, adică sunt între res1 și res2 (inclusiv aceste valori), scriptul dvs. probabil va eșua.

Răspuns

Metoda mea pentru bash :

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

Comentarii

  • Aceasta arată timpul scurs, dar nu ‘ nu se afișează ” ieșire cu granulație fină precum timpul procesorului, timpul I / O ” așa cum a fost solicitat în întrebare.
  • Cei care caută un răspuns la întrebarea așa cum este pusă vor găsi această soluție utilă. Comentariul dvs. ar fi mai bine adresat întrebării OP-urilor.

Răspuns

Personal, îmi place să înfășur toate codul scriptului în unele funcții „principale” de genul:

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

Observați cât de ușor este să folosiți comanda time în acest scenariu. Evident, nu măsurați timpul precis, inclusiv timpul de analiză a scriptului, dar consider că este suficient de corect în majoritatea situațiilor.

Comentarii

  • Soluție excelentă, ‘ nu necesită instrumente suplimentare și este autonom.

Răspuns

#!/bin/bash start=$(date +%s.%N) # HERE BE CODE end=$(date +%s.%N) runtime=$(python -c "print(${end} - ${start})") echo "Runtime was $runtime" 

Da, apelează Python, dar dacă poți trăi cu asta, atunci aceasta este o soluție destul de drăguță, concisă.

Comentarii

  • Feriți-vă că rularea acelei comenzi python într-un subshell și citirea ieșirii sale vor dura câteva milioane de nanosecunde pe majoritatea sistemelor actuale. Același lucru pentru rularea date.
  • ” Câteva milioane de nanosecunde ” este de câteva milisecunde . Persoanele care sincronizează scripturile bash nu sunt de obicei prea îngrijorate de acest lucru (cu excepția cazului în care rulează câteva miliarde de scripturi pe megasecundă).
  • Observați, de asemenea, că, chiar dacă calculul se face în cadrul unui proces python, valorile au fost colectate în întregime înainte de a fi invocat python. Timpul de execuție al scriptului va fi măsurat corect, fără ” milioane de nanosecunde ” cheltuieli generale.
  • time main de mai sus este mult mai curat …da, putem trăi cu o mulțime de degradări, dar ‘ e mult mai bine, fără ca ‘ să rămână ingineri!
  • $(echo $end - $start | bc) va face același lucru fără python

Răspunde

Această întrebare este destul de veche, dar în încercarea de a găsi modalitatea mea preferată de a o face, acest fir a apărut … și m-am mirat că nimeni nu a menționat-o:

perf stat -r 10 -B sleep 1 

„perf” este un instrument de analiză a performanței inclus în kernel sub „tools / perf” și adesea disponibil pentru a fi instalat ca pachet separat („perf” în CentOS și „linux-tools” pe Debian / Ubuntu). Linux Kernal perf Wiki are mai multe informații despre aceasta.

Rularea „perf stat” oferă destul de multe detalii, inclusiv timpul mediu de execuție chiar la final:

1.002248382 seconds time elapsed ( +- 0.01% ) 

Comentarii

  • Ce este perf?
  • @B Layer – a editat răspunsul meu t o dați o scurtă descriere a ‘ perf ‘.
  • perf stat acum se plânge de ” Este posibil să nu aveți permisiunea de a colecta statistici. ” decât dacă executați unele comenzi cu sudo, ceea ce îl face inutil în toate scenariile în care nu ‘ dețineți complet mașina țintă.
  • Uimitor, mulțumesc! Soluție mult mai fină decât time sugerează alții. În special, time nu se aplică soluțiilor de măsurare a concurenței codurilor (deoarece nu face ‘ timp de imprimare în milisecunde și mai puțin) , în timp ce soluția dvs. nu.

Răspuns

O funcție de shell mică care poate fi adăugată înainte comenzi pentru a-și măsura timpul:

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

Apoi folosiți-l în scriptul dvs. sau în linia de comandă astfel:

tm the_original_command with all its parameters 

Comentarii

  • Ar merita să adăugați milisecunde, încercat ilke s=$(date +%s000), nu sunt sigur dacă funcționează.
  • @loretoparisi timp de milisecunde, puteți încerca date +%s%N. Consultați serverfault.com/questions/151109/…
  • Acest lucru este minunat, cu excepția faptului că este mai sigur utilizați "$@"

Răspuns

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

Comentarii

  • Cum este acest lucru cu adevărat diferit de celelalte răspunsuri deja date care folosesc date înainte și după scriptul și ieșirea diferenței dintre ele?

Răspuns

  1. Doar folosiți time [any command] . Ex: time sleep 1 va dormi pentru un timp real (adică, așa cum este cronometrat de un cronometru) de la ~ 1.000 la ~ 1.020 sec, așa cum se arată aici:

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

    Ce lucru frumos. Puteți pune orice comandă după ea, iar rezultatul rezultă într-o formă drăguță, lizibilă de om. Îmi place foarte mult să-l folosesc pentru construcțiile de sincronizare. Ex:

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

    … sau pentru operații git care pot fi cu adevărat reale lung, astfel încât să pot dezvolta așteptări mentale realiste:

      # 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. Pentru necesități de sincronizare mai personalizate unde poate fi necesar să manipulați ieșirea sau să o convertiți în alte forme, în bash , utilizați variabila $SECONDS internă. Aici „o demonstrație, inclusiv conversia acestor secunde în alte unități, cum ar fi minutele cu virgulă mobilă:

    Rețineți că dt_min se rotunjește de la 0.01666666666... (1 secundă = atât de multe minute) la 0.017 în acest caz, deoarece „folosesc funcția printf pentru a rotunji. Partea sleep 1; de mai jos este locul în care ați fi chemat scriptul să ruleze și să se aleagă, dar doar dorm doar 1 secundă, de dragul acestei demonstrații.

    Comandă:

      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"  

    Ieșire:

      dt_sec = 1; dt_min = 0.017  

Related:

  1. Citiți mai multe despre bc și printf în răspunsul meu aici: https://stackoverflow.com/questions/12722095/how-do-i-use-floating-point-division-in-bash/58479867#58479867
  2. Nu-mi amintesc de unde am aflat mai întâi despre comanda time, dar este posibil să fi fost de la Răspunsul @Trudbert chiar aici .

Răspuns

Folosind numai bash este de asemenea posibil să măsurați și să calculați durata de timp pentru o porțiune din scriptul shell (sau timpul scurs pentru întregul script):

start=$SECONDS ... # do time consuming stuff end=$SECONDS 

acum puteți imprima diferența:

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

dacă aveți nevoie doar de durată incrementală, faceți:

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

De asemenea, puteți stoca durata într-o variabilă:

let diff=end-start 

Comentarii

  • ^ Acesta este răspunsul, oameni buni. Sau mai simplu: $ SECONDS la final. ‘ este o variabilă Bash BUILT-IN. Toate celelalte răspunsuri fac doar o muncă suplimentară pentru a reinventa această roată …
  • Aceasta este doar o repetare a răspunsului Marks anterior unix.stackexchange .com / a / 340156/155362 .

Răspuns

Funcția de sincronizare bazată pe SECONDS, limitat doar la granularitatea de nivel secundar, nu folosește nicio comandă externă:

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

De exemplu:

time_it sleep 5 

oferă

2019-03-30_17:24:37 Starting sleep 5 2019-03-30_17:24:42 Finished sleep 5; elapsed = 5 seconds 

Răspuns

Utilizați bash time builtin?

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. 

Exemplu:

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

Ieșire:

The command took 0.108s 

Răspuns

Aici „o variantă a lui Alex Răspunsul. Îmi pasă doar de minute și secunde, dar am vrut să fie formatat diferit. Așa că am făcut acest lucru:

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

Comentarii

  • De ce votul negativ? Am o eroare?
  • Folosirea python pentru calculul respectiv mi se pare o eroare, scuze omule. De ce oamenii nu pot ‘ pur și simplu suna la time este dincolo de mine.

Răspuns

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

Răspuns

Soluția acceptată folosind time scrie la stderr.
Soluția care utilizează times scrie la stdout.
Soluțiile care utilizează $SECONDS lipsesc precizie de secundă.
Celelalte soluții implică apelarea de programe externe precum date sau perf care nu este eficient.
Dacă sunteți bine cu oricare dintre acestea, utilizați-l.

Dar dacă aveți nevoie de o soluție eficientă pentru a obține timpii cu precizie milisecundă și au nevoie de ele în variabile astfel încât ieșirea originală rămâne netulburată puteți combina substituirea procesului cu unele redirecționări în jurul time care este mult mai rapid decât apel programe externe și permite redirecționări în jurul scriptului de împachetare de sincronizare ca în comanda / scriptul original.

# 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 

Selectați una dintre următoarele variante analizând rezultatul time adecvat nevoilor dvs.:
Cea mai scurtă variantă în care se scrie stdout din $Cmd div id = „555ddc9905”>

și nimic pentrustdout:

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

Varianta mai lungă care păstrează stdout și stderr originale separate între ele:

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

Cea mai complicată variantă include închiderea descriptorilor de fișiere suplimentare, astfel încât $Cmd este chemat ca și cum ar fi fără acest înveliș de sincronizare în jurul său și comenzile precum vgs nu se plâng de descriptorii de fișiere scurși:

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

Tu poate chiar să falsifice o adăugare în virgulă mobilă în bash fără a apela bc ceea ce ar fi mult mai lent:

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 

Ieșiri posibile cu cele două exemple de $Cmd pe un procesor lent:

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 

Sau:

stdout stderr CPU 0.008 s, Elapsed 0.109 s 

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *