Comment obtenir le temps d'exécution d'un script efficacement?

342

Je voudrais afficher le temps d'achèvement d'un script.

Ce que je fais actuellement c'est -

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

Cela montre simplement l'heure du début et de la fin du script. Serait-il possible d'afficher une sortie à granularité fine telle que temps processeur / temps io, etc.?

mtk
la source
2
stackoverflow.com/questions/385408/…
Ciro Santilli a annoncé le
Je pense que la réponse acceptée actuellement ne suffit pas vraiment à cause des fluctuations temporelles.
Léo Léopold Hertz 준영
1
@CiroSantilli 事件 事件 2016 事件 法轮功 Je pense que le fil de discussion que vous proposez n'est toujours pas suffisant pour éliminer l'effet d'événements temporels.
Léo Léopold Hertz 준영
Il y a une commande que vous pouvez exécuter et ensuite il chronomètre automatiquement toutes les commandes unix, vous avez oublié comment activer?
Poudre366

Réponses:

451

Il suffit d'utiliser timelorsque vous appelez le script:

time yourscript.sh
Trudbert
la source
61
Ceci produit trois fois: real, useret sys. Pour la signification de ceux-ci, voir ici .
Garrett
27
et "real" est probablement ce que les gens veulent savoir - "Le temps réel est l'heure - l'heure du début à la fin de l'appel"
Brad Parks
3
Y at-il un moyen de capturer la sortie standard dans un fichier? Par exemple, quelque chose commetime $( ... ) >> out.txt
Jon
1
Vous pouvez également le faire pour des séquences de commandes sur la ligne de commande, par exemple:time (command1 && command2)
meetar
1
Ou bien, dans son script, il suffit de faire: echo $ SECONDS en supposant que ce soit bash ...
Scott Prive
171

Si ce timen'est pas une option,

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

runtime=$((end-start))
Rob Bos
la source
31
Notez que cela ne fonctionne que si vous n'avez pas besoin d'une précision inférieure à la seconde. Pour certaines utilisations, cela pourrait être acceptable, pour d'autres non. Pour une précision légèrement meilleure (vous invoquez encore datedeux fois, par exemple, afin d' obtenir au mieux une précision de l'ordre de la milliseconde et probablement moins), essayez d'utiliser date +%s.%N. ( %Nest en nanosecondes depuis la seconde entière.)
CVn
Bon point. J'y ai pensé juste après avoir quitté le clavier, mais je ne suis pas revenu. ^^ Rappelez-vous aussi, OP, que la "date" ajoutera elle-même quelques millisecondes au temps d'exécution.
Rob Bos
2
@ChrisH Oh. Bon en le soulignant; L'expansion arithmétique de bash est un entier uniquement. Je vois deux options évidentes; soit abandonner la période dans la chaîne de format de date (et traiter la valeur résultante en nanosecondes depuis l'époque), utilisez donc date +%s%N, ou utilisez quelque chose de plus capable comme bcde calculer le temps d'exécution réel à partir des deux valeurs suggérées par jwchew. Néanmoins, j'estime que cette approche est une manière sous-optimale de le faire; timeest considérablement meilleur si disponible, pour les raisons exposées ci-dessus.
un CVn
10
Il suffit d’installer bc, puis de faire ceci:runtime=$( echo "$end - $start" | bc -l )
édifiant
10
Si votre script prend plusieurs minutes, utilisez:echo "Duration: $((($(date +%s)-$start)/60)) minutes
rubo77
75

Appelez simplement timessans argument lorsque vous quittez votre script.

Avec kshou zsh, vous pouvez également utiliser à la timeplace. Avec zsh, vous timeobtiendrez également le temps de l'horloge murale en plus du temps de l' utilisateur et du temps système .

Pour conserver le statut de sortie de votre script, vous pouvez le faire:

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

Ou vous pouvez également ajouter un piège sur EXIT:

trap times EXIT

De cette façon, les heures seront appelées à chaque fois que le shell se fermera et le statut de sortie sera préservé.

$ 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

Notez également que tous bash, kshet zshune $SECONDSvariable spéciale qui obtient automatiquement incrémenté à chaque seconde. Dans les deux cas zshet ksh93, cette variable peut également être faite à virgule flottante (avec typeset -F SECONDS) pour obtenir plus de précision. Ce n'est que l'heure du mur, pas le temps du processeur.

Stéphane Chazelas
la source
12
Cette variable $ SECONDS est très utile, merci!
andybuckley
Votre approche élimine-t-elle l'effet des événements temporels? - - Je pense que votre approche est proche de l' timeapproche présentée plus tôt. - - Je pense que l' timeitapproche présentée dans le code MATLAB peut être utile ici.
Léo Léopold Hertz 준영
2
Bonjour, @ Stéphane Chazelas. J'ai trouvé timesne donne pas le temps de mur, non? par exemple,bash -c 'trap times EXIT;sleep 2'
user15964
32

Je suis un peu en retard, mais je voulais poster ma solution (pour une précision inférieure à la seconde) au cas où d’autres tomberaient sur ce fil en cherchant. La sortie est au format jours, heures, minutes et enfin secondes:

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

J'espère que quelqu'un trouve cela utile!

jwchew
la source
1
J'imagine qu'il n'y a pas d'autre moyen (uniquement bash), sauf d'utiliser "bc" pour effectuer les calculs. BTW vraiment bon script;)
tvl
1
Attention, FreeBSD datene supporte pas la précision inférieure à la seconde et ne fera qu'ajouter un “ N” littéral à l'horodatage.
Anton Samsonov
1
Très beau script, mais mon bash ne gère pas la partie inférieure à la seconde. J'ai aussi appris que / 1 sur bc supprime efficacement cela, alors j'ai ajouté @ / 1 @ au calcul de $ ds, et il affiche des trucs très bien maintenant!
Daniel
1
bc prend en charge modulo: dt2=$(echo "$dt%86400" | bc). Juste en disant ... BTW je préfère la forme dt2=$(bc <<< "${dt}%86400")mais c'est tout à fait personnel.
Dani_l
16

Ma méthode pour bash:

# Reset BASH time counter
SECONDS=0
    # 
    # do stuff
    # 
ELAPSED="Elapsed: $(($SECONDS / 3600))hrs $((($SECONDS / 60) % 60))min $(($SECONDS % 60))sec"
marque
la source
Cela indique le temps écoulé, mais pas "une sortie à granularité fine telle que le temps de traitement, le temps d'E / S" comme demandé dans la question.
roaima
3
Ceux qui cherchent une réponse à la question posée trouveront probablement cette solution utile. Votre commentaire serait mieux adressé à la question des 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"

Oui, cela s'appelle Python, mais si vous pouvez vivre avec cela, alors c'est une solution plutôt agréable et laconique.

Alex
la source
5
Attention, l'exécution de cette pythoncommande dans un sous-shell et la lecture de sa sortie prendront plusieurs millions de nanosecondes sur la plupart des systèmes actuels. Pareil pour courir date.
Stéphane Chazelas
7
"Plusieurs millions de nanosecondes" est plusieurs millisecondes. Les gens qui écrivent des scripts bash ne sont généralement pas très inquiets à ce sujet (à moins qu’ils n’exécutent plusieurs milliards de scripts par méga-seconde).
Zilk
2
Notez également que, même si le calcul est effectué dans un processus python, les valeurs ont été entièrement collectées avant l'appel de python. Le temps d'exécution du script sera mesuré correctement, sans les "millions de nanosecondes".
Victor Schröder
5

Cette question est assez ancienne, mais en essayant de trouver ma façon préférée de le faire, ce fil est arrivé très haut ... et je suis étonné que personne ne l'ait mentionné:

perf stat -r 10 -B sleep 1

'perf' est un outil d'analyse des performances inclus dans le noyau sous 'tools / perf' et souvent disponible pour l'installation en tant que paquet séparé ('perf' dans CentOS et 'linux-tools' dans Debian / Ubuntu). Le Linux Wiki perf Wiki contient beaucoup plus d’informations à ce sujet.

Lancer 'perf stat' donne pas mal de détails, y compris le temps d'exécution moyen à la fin:

1.002248382 seconds time elapsed                   ( +-  0.01% )
Zaahid
la source
2
C'est quoi perf?
Couche B
@B Layer - a modifié ma réponse pour donner une brève description de 'perf'.
Zaahid
1
perf statse plaint maintenant de "Vous pouvez ne pas avoir la permission de collecter des statistiques." sauf si vous exécutez certaines commandes avec sudo, ce qui le rend inutilisable dans tous les scénarios où vous ne possédez pas complètement la machine cible.
Alamar
4

Une petite fonction shell qui peut être ajoutée avant les commandes pour mesurer leur temps:

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

Ensuite, utilisez-le dans votre script ou sur votre ligne de commande comme suit:

tm the_original_command with all its parameters
Evgeny
la source
Il serait intéressant d’ajouter des millisecondes, a-t-il essayé s=$(date +%s000), sans savoir si cela fonctionne.
loretoparisi
3

Voici une variante de la réponse d'Alex. Je ne me soucie que des minutes et des secondes, mais je voulais aussi que le formatage soit différent. Alors j'ai fait ça:

start=$(date +%s)
end=$(date +%s)
runtime=$(python -c "print '%u:%02u' % ((${end} - ${start})/60, (${end} - ${start})%60)")
Mpontillo
la source
1
Pourquoi le vote négatif? Est-ce que j'ai 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 [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
la source
7
En quoi est-ce vraiment différent des autres réponses déjà données qui utilisent dateavant et après le script et affichent la différence entre elles?
Eric Renouf
3

Personnellement, j'aime bien insérer tout mon code de script dans une fonction "principale" comme ceci:

main () {
 echo running ...
}

# stuff ...

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

Notez à quel point il est facile d’utiliser la timecommande dans ce scénario. Évidemment, vous ne mesurez pas l'heure précise, y compris la durée d'analyse du script, mais je la trouve suffisamment précise dans la plupart des situations.

LeZuse
la source
2

En utilisant uniquement bash, il est également possible de mesurer et de calculer la durée d'une partie du script shell (ou le temps écoulé pour l'ensemble du script):

start=$SECONDS

... # do time consuming stuff

end=$SECONDS

vous pouvez maintenant soit simplement imprimer la différence:

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

si vous n’avez besoin que d’une durée supplémentaire, faites:

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

Vous pouvez également stocker la durée dans une variable:

let diff=end-start
gauteh
la source
^ CECI est la réponse les gens. Ou plus simplement: $ SECONDS à la fin. C'est une variable Bash BUILT-IN. Toutes les autres réponses font juste un travail supplémentaire pour réinventer cette roue ...
Scott Prive
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
la source
1

La fonction de minutage basée sur SECONDS, limitée à la granularité de second niveau, n’utilise aucune commande externe:

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

Par exemple:

time_it sleep 5

donne

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