¿Cómo obtener el tiempo de ejecución de un script de manera efectiva?

342

Me gustaría mostrar el tiempo de finalización de un script.

Lo que hago actualmente es

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

Esto solo muestra la hora de inicio y finalización del guión. ¿Sería posible mostrar una salida de grano fino como tiempo de procesador / tiempo io, etc.?

mtk
fuente
2
stackoverflow.com/questions/385408/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
Creo que la respuesta actual aceptada no es realmente suficiente debido a las fluctuaciones temporales.
Léo Léopold Hertz 준영
1
@CiroSantilli 烏坎 事件 2016 六四 事件 法轮功 Creo que su hilo propuesto aún no es suficiente para eliminar el efecto de los eventos temporales.
Léo Léopold Hertz 준영
Hay un comando que puede ejecutar y luego sincroniza todos los comandos de Unix automáticamente, ¿olvidó cómo habilitar?
powder366

Respuestas:

451

Solo use timecuando llame al script:

time yourscript.sh
Trudbert
fuente
61
Esto genera tres veces: real, usery sys. Para el significado de estos, ver aquí .
Garrett
27
y "real" es probablemente lo que la gente quiere saber: "Real es la hora del reloj de pared - hora desde el inicio hasta el final de la llamada"
Brad Parks,
3
¿Hay alguna manera de capturar el stdout en un archivo? Por ejemplo, algo así comotime $( ... ) >> out.txt
Jon
1
También puede hacer esto para secuencias de comandos en la línea de comandos, por ejemplo:time (command1 && command2)
meetar
1
O podría, en su guión, simplemente hacer: echo $ SEGUNDOS asumiendo que es bash ...
Scott Prive
171

Si timeno es una opción,

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

runtime=$((end-start))
Rob Bos
fuente
31
Tenga en cuenta que esto solo funciona si no necesita precisión de menos de un segundo. Para algunos usos que podrían ser aceptables, para otros no. Para obtener una precisión ligeramente mejor ( datepor ejemplo, todavía está invocando dos veces, por lo que en el mejor de los casos puede obtener una precisión de milisegundos en la práctica, y probablemente menos), intente usar date +%s.%N. ( %Nson nanosegundos desde todo el segundo.)
un CVn
Buen punto. Pensé en eso justo después de dejar el teclado, pero no regresé. ^^ Recuerde también, OP, que la "fecha" en sí misma agregará unos pocos milisegundos al tiempo de ejecución.
Rob Bos
2
@ChrisH Oh. Es bueno señalarlo; La expansión aritmética de bash es solo de enteros. Veo dos opciones obvias; deseche el período en la cadena de formato de fecha (y trate el valor resultante como nanosegundos desde la época), así que use date +%s%N, o use algo más capaz como bccalcular el tiempo de ejecución real a partir de los dos valores como sugiere jwchew. Aún así, siento que este enfoque es una forma subóptima de hacerlo; timees considerablemente mejor si está disponible, por las razones descritas anteriormente.
un CVn
10
Simplemente instale bcy luego haga esto:runtime=$( echo "$end - $start" | bc -l )
redolente
10
Si su script toma varios minutos, use:echo "Duration: $((($(date +%s)-$start)/60)) minutes
rubo77
75

Simplemente llame timessin argumentos al salir de su script.

Con ksho zsh, también puede usar timeen su lugar. Con zsh, timetambién le dará el tiempo de reloj de pared además del tiempo de CPU del usuario y del sistema .

Para preservar el estado de salida de su script, puede hacerlo:

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

O también puede agregar una trampa en EXIT:

trap times EXIT

De esa manera, se llamarán los tiempos cada vez que el shell salga y se conservará el estado de salida.

$ 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

También tenga en cuenta que todos bash, kshy zshtener una $SECONDSvariable especial que automáticamente se incrementa cada segundo. En ambos zshy ksh93, esa variable también se puede hacer punto flotante (con typeset -F SECONDS) para obtener más precisión. Este es solo el tiempo del reloj de pared, no el tiempo de la CPU.

Stéphane Chazelas
fuente
12
Esa variable $ SECONDS es muy útil, ¡gracias!
andybuckley
¿Su enfoque elimina el efecto de los eventos temporales? - - Creo que su enfoque es un timeenfoque cercano presentado anteriormente. - - Creo que el timeitenfoque presentado en el código MATLAB puede ser útil aquí.
Léo Léopold Hertz 준영
2
Hola, @ Stéphane Chazelas. Descubrí que timesno le da tiempo a la pared, ¿verdad? por ejemplo,bash -c 'trap times EXIT;sleep 2'
user15964
32

Llego un poco tarde al tren, pero quería publicar mi solución (para una precisión de menos de un segundo) en caso de que otros tropiecen con este hilo a través de la búsqueda. La salida está en formato de días, horas, minutos y finalmente segundos:

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

¡Espero que alguien lo encuentre útil!

jwchew
fuente
1
Supongo que no hay otra forma (solo bash) excepto usar 'bc' para hacer los cálculos. Por cierto, muy buen guión;)
tvl
1
Tenga en cuenta que FreeBSD dateno admite precisión por debajo de un segundo y solo agregará literalmente " N" a la marca de tiempo.
Anton Samsonov
1
Muy buen guión, pero mi bash no maneja la segunda parte. También aprendí que / 1 en bc efectivamente elimina eso, así que agregué @ / 1 @ al cálculo de $ ds, ¡y ahora muestra cosas muy bonitas!
Daniel
1
bc apoya módulo: dt2=$(echo "$dt%86400" | bc). Solo digo ... Por cierto, prefiero la forma, dt2=$(bc <<< "${dt}%86400")pero eso es completamente personal.
Dani_l
16

Mi método para bash:

# Reset BASH time counter
SECONDS=0
    # 
    # do stuff
    # 
ELAPSED="Elapsed: $(($SECONDS / 3600))hrs $((($SECONDS / 60) % 60))min $(($SECONDS % 60))sec"
marca
fuente
Esto muestra el tiempo transcurrido, pero no muestra "salida de grano fino como el tiempo del procesador, el tiempo de E / S" como se solicita en la pregunta.
roaima
3
Es probable que quienes buscan una respuesta a la pregunta planteada encuentren útil esta solución. Su comentario estaría mejor dirigido a la pregunta de los OP.
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"

Sí, esto se llama Python, pero si puedes vivir con eso, entonces esta es una solución bastante agradable y concisa.

Alex
fuente
55
Tenga en cuenta que ejecutar ese pythoncomando en una subshell y leer su salida requerirá varios millones de nanosegundos en la mayoría de los sistemas actuales. Lo mismo para correr date.
Stéphane Chazelas
77
"Varios millones de nanosegundos" son varios milisegundos. Las personas que cronometran los scripts de bash generalmente no están muy preocupados por eso (a menos que ejecuten varios miles de millones de scripts por cada segundo)
Zilk
2
Observe también que, incluso si el cálculo se realiza dentro de un proceso de Python, los valores se recopilaron por completo antes de que se invocara Python. El tiempo de ejecución del script se medirá correctamente, sin la sobrecarga de "millones de nanosegundos".
Victor Schröder
5

Esta pregunta es bastante antigua, pero al tratar de encontrar mi forma favorita de hacerlo, este hilo surgió alto ... y me sorprende que nadie lo haya mencionado:

perf stat -r 10 -B sleep 1

'perf' es una herramienta de análisis de rendimiento incluida en el núcleo bajo 'tools / perf' y a menudo disponible para instalar como un paquete separado ('perf' en CentOS y 'linux-tools' en Debian / Ubuntu). El Linux Kernal perf Wiki tiene mucha más información al respecto.

Ejecutar 'perf stat' proporciona bastantes detalles, incluido el tiempo de ejecución promedio justo al final:

1.002248382 seconds time elapsed                   ( +-  0.01% )
Zaahid
fuente
2
¿Qué es perf?
Capa B
@B Layer: edité mi respuesta para dar una breve descripción de 'perf'.
Zaahid
1
perf statahora se queja de "Es posible que no tenga permiso para recopilar estadísticas". a menos que ejecute algunos comandos con sudo, lo que lo hace inútil en todos los escenarios donde no posee completamente la máquina de destino.
alamar
4

Una pequeña función de shell que se puede agregar antes de los comandos para medir su tiempo:

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

Luego, úselo en su script o en su línea de comando de la siguiente manera:

tm the_original_command with all its parameters
Evgeny
fuente
Valdría la pena agregar milisegundos, probé ilke s=$(date +%s000), no estoy seguro de si funciona.
loretoparisi
3

Aquí hay una variación de la respuesta de Alex. Solo me importan los minutos y los segundos, pero también quería que tuviera un formato diferente. Entonces hice esto:

start=$(date +%s)
end=$(date +%s)
runtime=$(python -c "print '%u:%02u' % ((${end} - ${start})/60, (${end} - ${start})%60)")
mpontillo
fuente
1
¿Por qué el voto negativo? ¿Tengo un error?
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
fuente
77
¿Cómo es esto realmente diferente de las otras respuestas ya dadas que usan dateantes y después del guión y generan la diferencia entre ellas?
Eric Renouf
2

Usando solo bash también es posible medir y calcular la duración del tiempo para una parte del script de shell (o el tiempo transcurrido para todo el script):

start=$SECONDS

... # do time consuming stuff

end=$SECONDS

ahora puede imprimir la diferencia:

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

si solo necesita una duración incremental, haga lo siguiente:

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

También puede almacenar la duración en una variable:

let diff=end-start
Gauteh
fuente
^ ESTA es la respuesta amigos. O más simplemente: $ SEGUNDOS al final. Es una variable Bash incorporada. Todas las otras respuestas solo están haciendo un trabajo extra para reinventar esta rueda ...
Scott Prive
2

Personalmente, me gusta envolver todo mi código de script en alguna función "principal" de esta manera:

main () {
 echo running ...
}

# stuff ...

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

Observe lo fácil que es usar el timecomando en este escenario. Obviamente no estás midiendo el tiempo preciso, incluido el tiempo de análisis del script, pero lo encuentro lo suficientemente preciso en la mayoría de las situaciones.

LeZuse
fuente
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
fuente
1

La función de temporización basada en SECONDS, limitada solo a granularidad de segundo nivel, no utiliza ningún comando externo:

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

Por ejemplo:

time_it sleep 5

da

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