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

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 script. ¿Sería ¿Es posible mostrar una salida detallada como tiempo de procesador / tiempo de io, etc.?

Comentarios

Responder

Jus t use time cuando llame al script:

time yourscript.sh 

Comentarios

  • Esto genera tres veces: real, user y sys. Para conocer el significado de estos, consulte aquí .
  • y » real » es probablemente lo que la gente quiere saber – » Real es la hora del reloj de pared – tiempo desde el inicio hasta el final de la llamada »
  • ¿Hay alguna forma de capturar la salida estándar en un archivo? Por ejemplo, algo como time $( ... ) >> out.txt
  • También puede hacer esto con secuencias de comandos en la línea de comandos, por ejemplo: time (command1 && command2)
  • O podría, en su script, simplemente hacer: echo $ SECONDS asumiendo que es bash ….

Responder

Si time no es «una opción,

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

Comentarios

  • Tenga en cuenta que esto solo funciona si no ‘ t necesita precisión inferior a un segundo. Para algunos usos, esto podría ser aceptable , para otros no. Para una precisión ligeramente mejor (usted ‘ todavía está invocando date dos veces, por ejemplo, por lo que podría en mejor obtenga una precisión de milisegundos en la práctica, y probablemente menos), intente usar date +%s.%N. (%N son nanosegundos ya que el segundo .)
  • @ChrisH Oh. Es bueno señalarlo; la expansión aritmética de bash es solo de enteros. Veo dos obvi nuestras opciones; O deshaga el período en la cadena de formato de fecha (y trate el valor resultante como nanosegundos desde época), así que use date +%s%N, o use algo más capaz como bc para calcular el tiempo de ejecución real a partir de los dos valores, como sugiere jwchew. Aún así, creo que este enfoque es una forma subóptima de hacerlo; time es considerablemente mejor si está disponible, por las razones descritas anteriormente.
  • Simplemente instale bc y luego haga esto: runtime=$( echo "$end - $start" | bc -l )
  • Si su secuencia de comandos tarda varios minutos, utilice: echo "Duration: $((($(date +%s)-$start)/60)) minutes
  • Además de @ rubo77 comenta, si tu secuencia de comandos tarda varias horas, utiliza hours=$((runtime / 3600)); minutes=$(( (runtime % 3600) / 60 )); seconds=$(( (runtime % 3600) % 60 )); echo "Runtime: $hours:$minutes:$seconds (hh:mm:ss)"

Responder

Simplemente llame a times sin argumentos al salir de su secuencia de comandos.

Con ksh o zsh, también puedes usar time en su lugar. Con zsh, time también le dará la hora del reloj de pared además del usuario y sistema tiempo de CPU.

Para preservar el estado de salida de su secuencia de comandos, puede hacerlo:

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

O usted también puede agregar una trampa en EXIT:

trap times EXIT 

De esa manera, se llamará a las horas siempre que el shell salga y el estado de salida se conservará.

$ 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 los bash, ksh y zsh tienen una $SECONDS variable especial que se incrementa automáticamente cada segundo. Tanto en zsh como en ksh93, esa variable también se puede convertir en punto flotante (con typeset -F SECONDS ) para obtener más precisión. Esto es solo el tiempo del reloj de pared, no el tiempo de la CPU.

Comentarios

  • Esa variable $ SECONDS es muy útil, ¡gracias!
  • ¿Su enfoque elimina el efecto de los eventos temporales? – – Creo que su enfoque está cerca del time presentado anteriormente.- – Creo que el enfoque timeit presentado en el código MATLAB puede ser útil aquí.
  • Hola, @St é phane Chazelas. Encontré que times no ‘ da tiempo a la pared, ¿verdad? por ejemplo, bash -c 'trap times EXIT;sleep 2'
  • @Binarus, sí, esa característica viene de ksh. Al contrario de ksh93 / zsh, además de que bash no es compatible con el punto flotante, ‘ también se divide en que, después de establecer SEGUNDOS en 0, cambiará a 1 de 0 a 1 segundo más tarde ( ya que solo considera el resultado de time() que solo tiene una granularidad de segundo completa).
  • @Binarus, no eso ‘ s entre 0 y 1. Si establece SEGUNDOS en 0 a las 12: 00: 00.000, cambiará a 1 un segundo después. Pero si lo configura en 12: 00: 00.999, cambiará a 1 un milisegundo más tarde. Pero sí, estoy de acuerdo en que probablemente no ‘ no importe mucho en la práctica si no ‘ no tiene una precisión inferior a un segundo de todos modos.

Responder

Llegué un poco tarde al tren, pero quería publicar mi solución (para una precisión inferior a un segundo ) en caso de que otros encuentren este hilo al buscar. 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) LC_NUMERIC=C printf "Total runtime: %d:%02d:%02d:%02.4f\n" $dd $dh $dm $ds 

Espero que alguien por ahí encuentra esto útil!

Comentarios

  • Supongo que no hay otra forma (solo bash) excepto usando ‘ bc ‘ para hacer los cálculos. Por cierto, muy buen script;)
  • Tenga cuidado con FreeBSD ‘ s date no admite precisión de menos de un segundo y solo agregará el literal «N» a la marca de tiempo.
  • Muy buen script, pero mi bash no ‘ t manejar la subsegunda parte. También aprendí que / 1 en bc elimina efectivamente eso, así que agregué @ / 1 @ al cálculo de $ ds, ¡y ahora muestra cosas muy bien!
  • bc admite módulo: dt2=$(echo "$dt%86400" | bc). Solo digo … Por cierto, prefiero la forma dt2=$(bc <<< "${dt}%86400") pero esa ‘ es completamente personal.
  • No ‘ No sé nada sobre bc todavía, pero la división por 86400 me hace desconfiar de la siguiente motivo: Hay días que no tienen 86400 segundos, por ejemplo, días en los que la hora cambia de DST a la hora normal y viceversa, o días con segundos bisiestos. Si esos días forman parte del período de tiempo que desea calcular, es decir, se encuentran entre res1 y res2 (incluidos estos valores), su secuencia de comandos probablemente fallará.

Respuesta

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" 

Comentarios

  • Esto muestra el tiempo transcurrido, pero no ‘ t mostrar » salida de grano fino como tiempo de procesador, tiempo de E / S » como se solicita en la pregunta.
  • Aquellos que busquen una respuesta a la pregunta tal como se planteó probablemente encontrarán útil esta solución. Su comentario estaría mejor dirigido a la pregunta de OP.

Respuesta

Personalmente, me gusta envolver todos mis código de secuencia de comandos en alguna función «principal» como esta:

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

Observe lo fácil que es usar el comando time en este escenario. Obviamente, no está 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.

Comentarios

  • Excelente solución, no ‘ no requiere herramientas adicionales y es autónomo.

Respuesta

#!/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 llama a Python, pero si puedes vivir con eso, entonces esta es una solución bastante agradable y concisa.

Comentarios

  • Tenga en cuenta que ejecutar ese comando python en una subcapa y leer su salida llevará varios millones de nanosegundos en la mayoría de los sistemas actuales. Lo mismo ocurre con date.
  • » Varios millones de nanosegundos » son varios milisegundos .La gente que mide el tiempo de los scripts de bash generalmente no está muy preocupada por eso (a menos que ejecuten varios miles de millones de scripts por megasegundo).
  • Tenga en cuenta también que, incluso si el 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 los » millones de nanosegundos » sobrecarga.
  • time main arriba es mucho más limpio …sí, podemos vivir con mucha degradación, pero ‘ es mucho mejor sin eso, ¡que ‘ sigan siendo ingenieros!
  • $(echo $end - $start | bc) hará lo mismo sin Python

Responder

Esta pregunta es bastante antigua, pero al tratar de encontrar mi forma favorita de hacerlo, este hilo salió muy 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 kernel en «tools / perf» y a menudo disponible para instalar como un paquete separado («perf» en CentOS y «linux-tools» en Debian / Ubuntu). La Wiki de perf del Kernal de Linux tiene mucha más información al respecto.

La ejecución de «perf stat» proporciona bastantes detalles, incluido el tiempo medio de ejecución. justo al final:

1.002248382 seconds time elapsed ( +- 0.01% ) 

Comentarios

  • ¿Qué es perf?
  • @B Layer – edité mi respuesta t o dar una breve descripción de ‘ perf ‘.
  • perf stat ahora 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 en los que no ‘ no posee completamente la máquina de destino.
  • ¡Increíble, gracias! Una solución mucho más detallada que time que sugieren otros. En particular, time no se aplica a la medición de soluciones de competencia de códigos (ya que no ‘ t tiempo de impresión en milisegundos o menos) , mientras que su solución lo hace.

Respuesta

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

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

Luego úselo en su secuencia de comandos, o en su línea de comando así:

tm the_original_command with all its parameters 

Comentarios

  • Valdría la pena agregar milisegundos, probé ilke s=$(date +%s000), no estoy seguro si funciona.
  • @loretoparisi durante milisegundos, puede intentar date +%s%N. Consulta serverfault.com/questions/151109/…
  • Esto es genial, excepto que es más seguro. use "$@"

Respuesta

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

Comentarios

  • ¿Cómo es esto realmente diferente de las otras respuestas ya dadas que usan date antes y después el script y generar la diferencia entre ellos?

Responder

  1. Solo use time [any command] . Por ejemplo: time sleep 1 dormirá durante un tiempo real (es decir, según el tiempo de un cronómetro) de ~ 1.000 a ~ 1.020 segundos, como se muestra aquí:

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

    Qué cosa tan hermosa. Puede poner cualquier comando después de él, y genera el resultado en una forma agradable y legible por humanos. Realmente me gusta usarlo para cronometrar compilaciones. Ej .:

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

    … o para operaciones git que potencialmente pueden ser largo, para que pueda desarrollar expectativas mentales realistas:

      # 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. Para necesidades de temporización más personalizadas donde es posible que deba manipular la salida o convertirla a otras formas, en bash , use la variable interna $SECONDS. Aquí «una demostración, que incluye la conversión de estos segundos a otras unidades, como minutos de punto flotante:

    Tenga en cuenta que dt_min se redondea de 0.01666666666... (1 segundo = esa cantidad de minutos) a 0.017 en este caso, ya que «estoy usando la función printf para redondear. La sleep 1; parte de abajo es donde llamarías a tu script para que se ejecute y cronometra, pero estoy durmiendo solo 1 segundo por el bien de esta demostración.

    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"  

    Resultado:

      dt_sec = 1; dt_min = 0.017  

Relacionado:

  1. Leer más acerca de bc y printf en mi respuesta aquí: https://stackoverflow.com/questions/12722095/how-do-i-use-floating-point-division-in-bash/58479867#58479867
  2. Ya no recuerdo dónde aprendí por primera vez sobre el comando time, pero puede que haya sido de La respuesta de @Trudbert aquí mismo .

Responder

Usando solo bash también es posible medir y calcular la duración de 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:

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

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

let diff=end-start 

Comentarios

  • ^ ESTA es la respuesta, amigos. O más simplemente: $ SECONDS al final. Es ‘ una variable Bash INTEGRADA. Todas las otras respuestas solo están haciendo un trabajo adicional para reinventar esta rueda …
  • Esto es solo una repetición de la respuesta anterior de Marks unix.stackexchange .com / a / 340156/155362 .

Respuesta

Función de temporización basada en SECONDS, limitado solo a la 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 

Respuesta

¿Usar bash time incorporado?

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. 

Ejemplo:

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

Resultado:

The command took 0.108s 

Respuesta

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

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

Comentarios

  • ¿Por qué el voto negativo? ¿Tengo un error?
  • Usar python para ese cálculo también me parece un error, lo siento. Por qué la gente no puede ‘ simplemente llamar a time es algo que me supera.

Responder

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

Answer

La solución aceptada usando time escribe en stderr.
La solución que usa times escribe en stdout.
Las soluciones que utilizan $SECONDS carecen de precisión inferior a un segundo.
Las otras soluciones implican llamar a programas externos como date o perf que no es eficiente.
Si está de acuerdo con alguno de estos, úselo.

Pero si necesita una solución eficiente para obtener los tiempos con precisión de milisegundos y los necesita en variables tales que el la salida original permanece inalterada puede combinar la sustitución del proceso con algunas redirecciones alrededor de time que es mucho más rápido que llamar programas externos y permite redirecciones alrededor del script de contenedor de tiempo como en el comando / script 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 

Seleccione una de las siguientes variantes analizando la salida de time apropiado para sus necesidades:
Variante más corta donde stdout de $Cmd está escrito en stderr y nada para stdout:

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

Variante más larga que mantiene stdout originales y stderr separados uno del otro:

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

La variante más complicada incluye cerrar los descriptores de archivos adicionales de modo que se llame a $Cmd como si no tuviera este contenedor de tiempo y lvm comandos como vgs no se quejan de descriptores de archivos filtrados:

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

Usted incluso puede simular una adición de punto flotante en bash sin llamar a bc que sería mucho más 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 

Posibles resultados con los dos ejemplos de $Cmd en 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 

O:

stdout stderr CPU 0.008 s, Elapsed 0.109 s 

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *