¿Cómo medir el tiempo de ejecución promedio de un script?

23

Tengo dos scripts que cada uno calcula el factorial de un número. Me gustaría saber cuál es más rápido. El timecomando me da milisegundos y el resultado es diferente de vez en cuando:

piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.089s
user    0m0.052s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.091s
user    0m0.048s
sys 0m0.036s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.088s
user    0m0.048s
sys 0m0.040s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.088s
user    0m0.048s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.087s
user    0m0.064s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.089s
user    0m0.068s
sys 0m0.016s
piousbox@piousbox-laptop:~/projects/trash$ 

¿Cómo tomo el tiempo promedio para ejecutar el script? Podría analizar y promediar la salida de un 100 timepero imagino que hay una mejor solución.

Victor Piousbox
fuente
1
stackoverflow.com/questions/8215482/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Respuestas:

4

No, tu idea de promediar es correcta.

La ejecución de la secuencia de comandos depende de muchos factores y, sin embargo, debe dividirse entre el tiempo de configuración (carga del intérprete en la memoria, configuración y posiblemente compilación de código a código de bytes o código de máquina) y el tiempo de ejecución real.

Para centrarse mejor en el tiempo de ejecución interno, realice el bucle en el script en sí (es decir, en lugar de calcular un factorial, lo calcula 100 veces dentro de una ejecución del script. El script se configurará una vez y la rutina interna ejecutará 100 veces).

Para centrarse en el tiempo total, ejecuta el script cien veces y promedia los resultados. Idealmente, debe separar esas ejecuciones lo suficiente como para que el sistema regrese en un "estado de referencia" (o un estado no relacionado con el script) cada vez. Por ejemplo, el propio intérprete se almacenará en la memoria caché, de modo que la primera ejecución del guión será considerablemente más lenta que las posteriores.

Para obtener una mejor visión del algoritmo, creo que la mejor manera es algo como esto (en una máquina inactiva):

  • envolver el algoritmo en una sola función.
  • en la aplicación de control:
    • llamar a la función una vez
    • obtenga el tiempo del sistema ("reloj de pared") y agregue 10 (o un N razonable) segundos
    • ingrese al ciclo y comience a contar iteraciones
    • después de cada llamada a la función, incremente el contador
    • Si la hora del sistema está por debajo del tiempo guardado, realice otro ciclo
    • obtener N exacto, posiblemente punto flotante, de la hora actual del reloj de pared
    • muestra el contador dividido por N: ese es el número de iteraciones / segundo.

La aplicación solo se ejecuta una vez, toda la configuración y cebado se realiza mediante la primera iteración no programada, por lo que esto debería minimizar los gastos generales (excepto tal vez por la llamada de tiempo).

Si la función recibe una entrada, haría bien en proporcionarle una secuencia aleatoria de entradas utilizando un PRNG sembrado con un valor fijo, para asegurarse de que ambas versiones de la función que se está probando reciban los mismos valores. Esto evita que una función tenga un rendimiento aparentemente mejor debido a los "números de la suerte" (p. Ej., Recuerdo una variación del algoritmo de Hillsort que funcionó considerablemente mejor si el número de elementos que se ordenarían tenía la forma 2 k -1 con pequeñas k s).

LSerni
fuente
Bien gracias. Noté que las llamadas posteriores se acortaban. Ejecuté el bucle dentro de los scripts ahora, y descubrí que un algoritmo es definitivamente más rápido que el otro.
Victor Piousbox
39

Puede ejecutar iteraciones del programa en un bucle; y divide el tiempo total entre el número de iteraciones:

time for i in {1..10}; do sleep 1; done
real    0m10.052s
user    0m0.005s
sys 0m0.018s
Kent
fuente
2
Súper simple, me encanta. Tampoco lo vi {1..10}antes y estoy desconcertado de que funcione, no puedo encontrarlo en el manual de bash. Lo único triste es que no conoce la difusión de sus resultados (tiempo mínimo y máximo).
w00t
@ w00t:man -P 'less +/Brace\ Expansion' bash
usuario2683246
Gracias @ user2683246! Entonces también lo encontré en gnu.org/software/bash/manual/bash.html#Brace-Expansion : un buen uso de menos por cierto. Ahora también tengo curiosidad sobre cuándo apareció esto en bash ...
w00t
1
Ajá, versión 3, 10 años después de que empecé a usar bash :) tldp.org/LDP/abs/html/bashver3.html
w00t
2
Si esto no funciona para los Googlers que llegan, puede ser porque no estás ejecutando bash. Intenta correr /bin/bashantes de esto.
Cory Klein
14

existe una herramienta llamada multitime que hace exactamente esto: ejecutar un comando varias veces, medir cuánto tiempo se tarda (tiempo real / usuario / sistema con tiempo medio, mínimo / máximo y mediana calculados automáticamente)

Por ejemplo, para medir un script similar 100 veces:

multitime -q -n 100 "fact1.sh"
===> multitime results
1: -q fact1.sh
            Mean        Std.Dev.    Min         Median      Max
real        0.122       0.032       0.086       0.116       0.171       
user        0.148       0.044       0.096       0.137       0.223       
sys         0.023       0.019       0.000       0.014       0.061 
Cyril Chaboisseau
fuente
12

Esto es antiguo, pero apareció en Google cuando estaba buscando un comando que utilicé anteriormente pero que no pude encontrar. De todos modos, mi forma preferida de hacer esto es:

perf stat -r 10 -B sleep 1

Esto proporciona bastantes detalles, incluido el tiempo de ejecución promedio al final:

1.002248382 seconds time elapsed                   ( +-  0.01% )
Zaahid
fuente
1

Hiperfina es otra opción.

Uso de la muestra:

hyperfine --warmup 3 'ruby fac1.rb'
Luís Bianchin
fuente
1
La hiperfina es mucho mejor que las otras alternativas que es ridícula. Detección de carreras necesarias, calentamiento, salida hermosa, informes de rebajas, escritos en óxido, etc.
Klas Mellbourn