Come ottenere in modo efficace il tempo di esecuzione di uno script?

Vorrei visualizzare il tempo di completamento di uno script.

Quello che faccio attualmente è –

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

Questo mostra solo lora di inizio e fine dello script. Sarebbe possibile visualizzare un output a grana fine come tempo processore / ora io, ecc.?

Commenti

Risposta

Jus t utilizzare time quando chiami lo script:

time yourscript.sh 

Commenti

  • Viene restituito tre volte: real, user e sys. Per il significato di questi, vedere qui .
  • e ” real ” è probabilmente ciò che le persone vogliono sapere: ” Il tempo reale è lora dellorologio da parete: il tempo dallinizio alla fine della chiamata ”
  • Cè un modo per catturare lo stdout in un file? Ad esempio, qualcosa come time $( ... ) >> out.txt
  • Puoi anche eseguire questa operazione su sequenze di comandi sulla riga di comando, ad esempio: time (command1 && command2)
  • Oppure potrebbe, nel suo script, fare semplicemente: echo $ SECONDS assumendo che sia bash ….

Risposta

Se time non è “unopzione,

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

Commenti

  • Tieni presente che funziona solo se ‘ non necessita di una precisione inferiore al secondo. Per alcuni usi potrebbe essere accettabile , per altri no. Per una precisione leggermente migliore (‘ stai ancora invocando date due volte, ad esempio, quindi potresti a meglio ottenere una precisione in millisecondi nella pratica, e probabilmente meno), prova a utilizzare date +%s.%N. (%N è nanosecondi dallintero secondo .)
  • @ChrisH Oh. Bene sottolinearlo; lespansione aritmetica di bash è di soli numeri interi. Vedo due obvi ous opzioni; o elimina il periodo nella stringa del formato della data (e considera il valore risultante come nanosecondi dallepoca), quindi utilizza date +%s%N o utilizza qualcosa di più capace come bc per calcolare il tempo di esecuzione effettivo dai due valori come suggerisce jwchew. Tuttavia, ritengo che questo approccio sia un modo non ottimale per farlo; time è notevolmente migliore se disponibile, per i motivi sopra descritti.
  • Installa bc e poi procedi in questo modo: runtime=$( echo "$end - $start" | bc -l )
  • Se lo script richiede diversi minuti, utilizza: echo "Duration: $((($(date +%s)-$start)/60)) minutes
  • Oltre a @ rubo77 commento, se lo script richiede diverse ore, utilizza hours=$((runtime / 3600)); minutes=$(( (runtime % 3600) / 60 )); seconds=$(( (runtime % 3600) % 60 )); echo "Runtime: $hours:$minutes:$seconds (hh:mm:ss)"

Answer

Basta chiamare times senza argomenti alluscita dallo script.

Con ksh o zsh, puoi anche utilizzare time. Con zsh, time ti fornirà anche lora dellorologio da parete oltre a utente e sistema tempo CPU.

Per preservare lo stato di uscita del tuo script, puoi farlo:

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

Oppure tu può anche aggiungere una trap su EXIT:

trap times EXIT 

In questo modo, gli orari verranno chiamati ogni volta che la shell esce e lo stato di uscita verrà preservato.

$ 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 

Tieni presente inoltre che tutti i bash, ksh e zsh hanno una $SECONDS variabile speciale che viene incrementata automaticamente ogni secondo. In entrambi zsh e ksh93, quella variabile può anche essere resa in virgola mobile (con typeset -F SECONDS ) per ottenere una maggiore precisione. Questo è solo il tempo dellorologio da parete, non il tempo della CPU.

Commenti

  • Quella variabile $ SECONDS è molto utile, grazie!
  • Il tuo approccio elimina leffetto degli eventi temporali? – – Penso che il tuo approccio sia vicino allapproccio time presentato in precedenza.- – Penso che lapproccio timeit presentato nel codice MATLAB possa essere utile qui.
  • Ciao, @St é phane Chazelas. Ho trovato che times non ‘ fornisce lora del muro, giusto? ad esempio, bash -c 'trap times EXIT;sleep 2'
  • @Binarus, sì, quella funzione proviene da ksh. Contrariamente a ksh93 / zsh, oltre a bash che non supporta il punto mobile, ‘ è anche rotto in quanto dopo aver impostato SECONDS su 0, cambierà in 1 da 0 a 1 secondo più tardi ( poiché considera solo il risultato di time() che ha solo un secondo granularità completo).
  • @Binarus, non quello ‘ s tra 0 e 1. Se si imposta SECONDS su 0 alle 12: 00: 00.000, cambierà in 1 un secondo dopo. Ma se lo imposti a 12: 00: 00.999, cambierà in 1 un millisecondo dopo. Ma sì, sono daccordo che probabilmente ‘ non importa molto in pratica se ‘ non ha una precisione inferiore al secondo.

Risposta

Sono “arrivato un po in ritardo sul carrozzone, ma volevo pubblicare la mia soluzione (per una precisione inferiore al secondo ) nel caso in cui altri si imbattano in questo thread durante la ricerca. Loutput è nel formato di giorni, ore, minuti e infine secondi:

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 

Spero che qualcuno là fuori lo trova utile!

Commenti

  • Immagino che non ci sia altro (solo bash) modo tranne usare ‘ bc ‘ per fare i calcoli. BTW script davvero buono;)
  • Attenti a FreeBSD ‘ s date non supporta la precisione inferiore al secondo e aggiunge semplicemente “N” al timestamp.
  • Script molto carino, ma la mia bash non ‘ t gestire la parte inferiore al secondo. Ho anche imparato che / 1 in bc lo elimina in modo efficace, quindi ho aggiunto @ / 1 @ al calcolo $ ds e ora mostra cose molto carine!
  • bc supporta modulo: dt2=$(echo "$dt%86400" | bc). Sto solo dicendo … A proposito, preferisco il modulo dt2=$(bc <<< "${dt}%86400") ma ‘ è del tutto personale.
  • Non ‘ Non so ancora nulla di bc, ma la divisione per 86400 mi rende diffidente nei confronti del motivo seguente: ci sono giorni che non hanno 86400 secondi, ad esempio giorni in cui lora passa dallora legale allora normale e viceversa, o giorni con secondi intercalari. Se tali giorni fanno parte dellintervallo di tempo che desideri calcolare, ovvero sono compresi tra res1 e res2 (compresi questi valori), il tuo script probabilmente fallirà.

Risposta

Il mio metodo per bash :

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

Commenti

  • Questo mostra il tempo trascorso, ma non ‘ t mostra ” output a grana fine come il tempo del processore, il tempo di I / O ” come richiesto nella domanda.
  • Coloro che cercano una risposta alla domanda posta probabilmente troveranno utile questa soluzione. Il tuo commento sarebbe meglio indirizzato alla domanda dei PO.

Risposta

Personalmente, mi piace racchiudere tutto il mio codice di script in alcune funzioni “principali” in questo modo:

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

Nota quanto sia facile utilizzare il comando time in questo scenario. Ovviamente non stai misurando il tempo preciso, incluso il tempo di analisi dello script, ma lo trovo abbastanza accurato nella maggior parte delle situazioni.

Commenti

  • Ottima soluzione, non ‘ non richiede strumenti aggiuntivi ed è autonomo.

Risposta

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

Sì, questo chiama Python, ma se riesci a conviverci, allora questa è una soluzione piuttosto carina e concisa.

Commenti

  • Fai attenzione che lesecuzione del comando python in una subshell e la lettura del suo output richiederà diversi milioni di nanosecondi sulla maggior parte dei sistemi attuali. Lo stesso vale per lesecuzione date.
  • ” Diversi milioni di nanosecondi ” sono diversi millisecondi Le persone che temporizzano gli script bash di solito non sono molto preoccupati per questo (a meno che non eseguano diversi miliardi di script per megasecondo).
  • Notare anche che, anche se il il calcolo viene eseguito allinterno di un processo python, i valori sono stati interamente raccolti prima che fosse invocato python. Il tempo di esecuzione dello script verrà misurato correttamente, senza ” milioni di nanosecondi ” overhead.
  • time main sopra è molto più pulito …sì, possiamo convivere con un sacco di degrado, ma ‘ è molto meglio senza, quindi lascia che ‘ rimanga ingegneri!
  • $(echo $end - $start | bc) farà la stessa cosa senza Python

Risposta

Questa domanda è piuttosto vecchia ma nel tentativo di trovare il mio modo preferito di farlo questo thread è arrivato in alto … e “sono sorpreso che nessuno labbia menzionato:

perf stat -r 10 -B sleep 1 

“perf” è uno strumento di analisi delle prestazioni incluso nel kernel sotto “tools / perf” e spesso disponibile per linstallazione come pacchetto separato (“perf” in CentOS e “linux-tools” su Debian / Ubuntu). Il Linux Kernal perf Wiki ha molte più informazioni al riguardo.

Lesecuzione di “perf stat” fornisce un bel po di dettagli, incluso il tempo medio di esecuzione proprio alla fine:

1.002248382 seconds time elapsed ( +- 0.01% ) 

Commenti

  • Che cosè perf?
  • @B Layer – modificato la mia risposta t o fornire una breve descrizione di ‘ perf ‘.
  • perf stat ora si lamenta di ” Potresti non essere autorizzato a raccogliere statistiche. ” a meno che tu non esegua alcuni comandi con sudo, il che lo rende inutile in tutti gli scenari in cui non ‘ possiedi completamente la macchina di destinazione.
  • Incredibile, grazie! Soluzione molto più dettagliata rispetto a time suggerita da altri. In particolare, time non è applicabile alla misurazione di soluzioni di concorrenza tra codici (in quanto ‘ t tempo di stampa in millisecondi e inferiore) , mentre la tua soluzione sì.

Answer

Una piccola funzione di shell che può essere aggiunta prima comandi per misurare il loro tempo:

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

Quindi usalo nel tuo script o sulla riga di comando in questo modo:

tm the_original_command with all its parameters 

Commenti

  • Varrebbe la pena aggiungere millisecondi, ho provato ilke s=$(date +%s000), non sono sicuro se funziona.
  • @loretoparisi per millisecondi, potresti provare date +%s%N. Vedi serverfault.com/questions/151109/…
  • È fantastico, ma più sicuro utilizza "$@"

Risposta

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

Commenti

  • In che modo è davvero diverso dalle altre risposte già fornite che utilizzano date prima e dopo lo script e visualizza la differenza tra loro?

Answer

  1. Solo utilizza time [any command] . Es: time sleep 1 dormirà per un tempo reale (ad esempio: come cronometrato da un cronometro) da ~ 1.000 a ~ 1.020 sec, come mostrato qui:

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

    Che bella cosa. Puoi inserire qualsiasi comando dopo di esso e restituisce il risultato in una bella forma leggibile dalluomo. Mi piace molto usarlo per le build temporali. Es:

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

    … o per operazioni git che potenzialmente possono essere davvero lungo, così posso sviluppare aspettative mentali realistiche:

      # 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. Per tempi più personalizzati è necessario dove potresti dover manipolare loutput o convertirlo in altre forme, in bash , utilizza la variabile interna $SECONDS. Ecco “una demo, inclusa la conversione di questi secondi in altre unità, come i minuti in virgola mobile:

    Tieni presente che dt_min viene arrotondato da 0.01666666666... (1 secondo = quel numero di minuti) a 0.017 in questo caso, poiché “sto utilizzando la funzione printf per arrotondare. La parte sleep 1; di seguito è dove “chiameresti il tuo script per lesecuzione e il tempo, ma io sto solo dormendo per 1 secondo invece per il bene di questa demo.

    Comando:

      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"  

    Risultato:

      dt_sec = 1; dt_min = 0.017  

Correlati:

  1. Ulteriori informazioni circa bc e printf nella mia risposta qui: https://stackoverflow.com/questions/12722095/how-do-i-use-floating-point-division-in-bash/58479867#58479867
  2. Non ricordo più dove ho imparato a conoscere il comando time, ma potrebbe essere stato da @Trudbert “la risposta proprio qui .

Risposta

Utilizzando solo bash è anche possibile misurare e calcolare la durata temporale per una parte dello script della shell (o il tempo trascorso per lintero script):

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

ora puoi semplicemente stampare la differenza:

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

se hai bisogno solo di una durata incrementale fai:

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

Puoi anche memorizzare la durata in una variabile:

let diff=end-start 

Commenti

  • ^ QUESTA è la risposta, gente. O più semplicemente: $ SECONDS alla fine. ‘ è una variabile Bash INTEGRATA. Tutte le altre risposte stanno solo facendo un lavoro extra per reinventare questa ruota …
  • Questa è solo una ripetizione della risposta precedente di Marks unix.stackexchange .com / a / 340156/155362 .

Risposta

Funzione di temporizzazione basata su SECONDS, limitato solo alla granularità di secondo livello, non “utilizza alcun comando esterno:

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

Ad esempio:

time_it sleep 5 

restituisce

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

Risposta

Utilizzare 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. 

Esempio:

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

Risultato:

The command took 0.108s 

Risposta

Ecco “una variazione di Alex “s. Mi interessano solo i minuti e i secondi, ma volevo anche che fosse formattato in modo diverso. Così ho fatto questo:

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

Commenti

  • Perché il voto negativo? Ho un bug?
  • Anche luso di python per quel calcolo sembra un bug, mi dispiace amico. Perché le persone non possono ‘ semplicemente chiamare time è al di là di me.

Rispondi

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

Risposta

La soluzione accettata utilizzando time scrive in stderr.
La soluzione che utilizza times scrive in stdout.
Le soluzioni che utilizzano $SECONDS mancano di una precisione inferiore al secondo.
Le altre soluzioni prevedono la chiamata di programmi esterni come date o perf che non è efficiente.
Se stai bene con uno di questi, usalo.

Ma se hai bisogno di una soluzione efficiente per ottenere i tempi con una precisione al millisecondo e necessitano nelle variabili in modo che loutput originale rimane indisturbato puoi combinare la sostituzione del processo con alcuni reindirizzamenti intorno a time, che è molto più veloce della chiamata programmi esterni e consente reindirizzamenti attorno allo script wrapper di temporizzazione come nel comando / script originale.

# 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 

Seleziona una delle seguenti varianti analizzando loutput di time appropriato alle tue esigenze:
Variante più breve dove stdout di $Cmd è scritto in stderr e nulla da stdout:

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

Variante più lunga che mantiene originali stdout e stderr separati luno dallaltro:

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

La variante più complicata include la chiusura dei descrittori di file aggiuntivi in modo tale che $Cmd venga chiamato come se non avesse questo wrapper temporale attorno ad esso e lvm comandi come vgs non si lamentano dei descrittori di file trapelati:

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

Tu può persino simulare unaggiunta in virgola mobile in bash senza chiamare bc, il che sarebbe molto più lento:

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 

Possibili output con i due esempi di $Cmd su una CPU lenta:

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 

Oppure:

stdout stderr CPU 0.008 s, Elapsed 0.109 s 

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *