Come ottenere in modo efficace i tempi di esecuzione di uno script?


342

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 l'ora di inizio e fine della sceneggiatura. Sarebbe possibile visualizzare un output a grana fine come tempo processore / tempo io, ecc.?



Penso che l'attuale risposta accettata non sia davvero sufficiente a causa delle fluttuazioni temporali.
Léo Léopold Hertz

1
@CiroSantilli 烏坎 事件 2016 六四 事件 法轮功 Penso che il tuo thread proposto non sia ancora sufficiente per eliminare l'effetto degli eventi temporali.
Léo Léopold Hertz 준영


Esiste un comando che puoi eseguire e quindi cronometra automaticamente tutti i comandi unix, hai dimenticato come abilitare?
powder366

Risposte:


451

Basta usare timequando si chiama lo script:

time yourscript.sh

61
Questo emette tre volte: real, usere sys. Per i significati di questi, vedi qui .
Garrett,

27
e "reale" è probabilmente ciò che la gente vuole sapere - "Il reale è il tempo dell'orologio da parete - il tempo dall'inizio alla fine della chiamata"
Brad Parks

3
C'è un modo per catturare lo stdout in un file? Ad esempio, qualcosa del generetime $( ... ) >> out.txt
Jon

1
Puoi anche farlo con sequenze di comandi sulla riga di comando, ad esempio:time (command1 && command2)
incontro

1
Oppure potrebbe, nella sua sceneggiatura, semplicemente fare: echo $ SECONDS supponendo che sia bash ....
Crossfit_and_Beer

171

Se timenon è un'opzione,

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

runtime=$((end-start))

31
Nota che funziona solo se non hai bisogno di precisione al secondo. Per alcuni usi che potrebbero essere accettabili, per altri no. Per una precisione leggermente migliore (stai ancora invocando datedue volte, ad esempio, quindi nella migliore delle ipotesi potresti ottenere una precisione di millisecondi, e probabilmente meno), prova a utilizzare date +%s.%N. ( %Nsono i nanosecondi dall'intero secondo.)
un CVn

Buon punto. Ci ho pensato subito dopo aver lasciato la tastiera ma non sono tornato. ^^ Ricorda inoltre, OP, che "date" aggiungerà esso stesso alcuni millisecondi al tempo di esecuzione.
Rob Bos,

2
@ChrisH Oh. Buono indicarlo; l'espansione aritmetica di bash è di soli numeri interi. Vedo due opzioni ovvie; abbandonare il periodo nella stringa del formato della data (e trattare il valore risultante come nanosecondi dall'epoca), quindi utilizzare date +%s%No utilizzare qualcosa di più capace bcdi calcolare il runtime effettivo dai due valori come suggerisce jwchew. Tuttavia, ritengo che questo approccio sia un modo non ottimale di farlo; timeè notevolmente migliore se disponibile, per i motivi sopra indicati.
un CVn il

10
Basta installare bce quindi fare questo:runtime=$( echo "$end - $start" | bc -l )
redolent

10
Se il tuo script richiede diversi minuti, usa:echo "Duration: $((($(date +%s)-$start)/60)) minutes
rubo77

75

Basta chiamare timessenza argomenti all'uscita dalla sceneggiatura.

Con ksho zsh, puoi anche usare timeinvece. Con zsh, timefornirà anche l'ora dell'orologio da parete oltre all'utente e alla CPU del sistema .

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

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

Oppure puoi anche aggiungere una trap su EXIT:

trap times EXIT

In questo modo, i tempi 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

Si noti inoltre che tutti bash, kshe zshhanno una $SECONDSvariabile speciale che viene automaticamente incrementato ogni secondo. In entrambi zshe ksh93, quella variabile può anche essere resa in virgola mobile (con typeset -F SECONDS) per ottenere maggiore precisione. Questo è solo il tempo di clock, non il tempo della CPU.


12
Quella variabile $ SECONDS è molto utile, grazie!
Andybuckley,

Il tuo approccio elimina l'effetto degli eventi temporali? - - Penso che il tuo approccio sia vicino timeall'approccio presentato in precedenza. - - Penso che l' timeitapproccio presentato nel codice MATLAB possa essere utile qui.
Léo Léopold Hertz 준영

2
Ciao, Stéphane Chazelas. Ho scoperto timesche non dà tempo al muro, giusto? ad esempio,bash -c 'trap times EXIT;sleep 2'
user15964,


32

Sono un po 'in ritardo con il carrozzone, ma volevo pubblicare la mia soluzione (per una precisione inferiore al secondo) nel caso in cui altri si imbattessero in questo thread attraverso la ricerca. L'output è in 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)

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

Spero che qualcuno la trovi utile!


1
Immagino non ci sia altro modo (solo bash) se non quello di usare "bc" per fare i calcoli. A proposito, sceneggiatura davvero buona;)
tvl

1
Fai attenzione che FreeBSD datenon supporta la precisione dei secondi inferiori e aggiungerà semplicemente " N" letterale al timestamp.
Anton Samsonov,

1
Script molto bello, ma il mio bash non gestisce la parte dei secondi. Ho anche appreso che / 1 in bc lo elimina effettivamente, quindi ho aggiunto @ / 1 @ al calcolo $ ds e ora mostra cose molto belle!
Daniel,

1
bc supporta modulo: dt2=$(echo "$dt%86400" | bc). Sto solo dicendo ... A proposito, preferisco il modulo dt2=$(bc <<< "${dt}%86400")ma è del tutto personale.
Dani_l

16

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"

Questo mostra il tempo trascorso, ma non mostra "output a grana fine come tempo del processore, tempo I / O" come richiesto nella domanda.
roaima,

3
Chi è alla ricerca di una risposta alla domanda proposta troverà probabilmente utile questa soluzione. Il tuo commento verrebbe indirizzato meglio alla domanda dei PO.
Segna il

5
#!/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, questa è una soluzione piuttosto piacevole e concisa.


5
Attenzione che l'esecuzione di quel pythoncomando in una subshell e la lettura del suo output richiederà diversi milioni di nanosecondi sulla maggior parte dei sistemi attuali. Lo stesso per la corsa date.
Stéphane Chazelas,

7
"Diversi milioni di nanosecondi" sono diversi millisecondi. La gente che cronometra gli script bash di solito non è molto preoccupata (a meno che non eseguano diversi miliardi di script per megasecondo).
Zilk,

2
Si noti inoltre che, anche se il calcolo viene eseguito all'interno 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 il sovraccarico di "milioni di nanosecondi".
Victor Schröder,

5

Questa domanda è piuttosto vecchia ma nel tentativo di trovare il mio modo preferito di farlo questa discussione è emersa in alto ... e sono sorpreso che nessuno lo abbia 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 l'installazione come pacchetto separato ('perf' in CentOS e 'linux-tools' su Debian / Ubuntu). Il wiki di Kernal perf di Linux ha molte più informazioni al riguardo.

L'esecuzione di 'perf stat' fornisce molti dettagli, incluso il tempo medio di esecuzione alla fine:

1.002248382 seconds time elapsed                   ( +-  0.01% )

2
Che cosa è perf?
B Layer,

@B Layer: ho modificato la mia risposta per fornire una breve descrizione di "perf".
Zaahid,

1
perf statora si lamenta di "Potresti non avere il permesso di raccogliere statistiche". a meno che non si eseguano alcuni comandi sudo, il che lo rende inutile in tutti gli scenari in cui non si possiede completamente la macchina target.
alamar,

4

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

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

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

tm the_original_command with all its parameters

Varrebbe la pena aggiungere millisecondi s=$(date +%s000), ho provato ilke , non sono sicuro che funzioni.
Loretoparisi,

3

Ecco una variante della risposta di Alex. Mi preoccupo solo di minuti e secondi, ma volevo anche che fosse formattato diversamente. Quindi ho fatto questo:

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

1
Perché il downvote? Ho un bug?
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
In che modo è davvero diverso dalle altre risposte già fornite che usano dateprima e dopo lo script e producono la differenza tra loro?
Eric Renouf,

2

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

start=$SECONDS

... # do time consuming stuff

end=$SECONDS

ora puoi semplicemente stampare la differenza:

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

se hai solo bisogno di una durata incrementale fai:

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

Puoi anche memorizzare la durata in una variabile:

let diff=end-start

^ QUESTA è la risposta gente. O più semplicemente: $ SECONDI alla fine. È una variabile Bash INCORPORATA. Tutte le altre risposte stanno solo facendo un lavoro extra per reinventare questa ruota ...
Crossfit_and_Beer

2

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

Notare quanto è facile utilizzare il timecomando 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.


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

La funzione di temporizzazione basata su SECONDS, limitata 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"
}

Per esempio:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.