¿Alternativa de referencia de tiempo universal no bash? [cerrado]

10

Para comparar los tiempos de ejecución de los scripts entre diferentes shells, algunas respuestas de SE sugieren usar bashel comando incorporado de la siguiente time manera:

time bash -c 'foo.sh'
time dash -c 'foo.sh'

... etc , para cada shell para probar. Tales puntos de referencia no logran eliminar el tiempo tomado para cada shell para cargar e inicializar . Por ejemplo, suponga que los dos comandos anteriores se almacenaron en un dispositivo lento con la velocidad de lectura de un disquete anterior , (124KB / s), dash(un ejecutable de ~ 150K ) se cargaría aproximadamente 7 veces más rápido que bash( ~ 1M ), el shell el tiempo de carga sesgaría los timenúmeros: los tiempos de precarga de esos proyectiles son irrelevantes para medir los tiempos de ejecución foo.shdebajo de cada proyectil después de cargar los proyectiles.

Cuál es la mejor util portátil y en general a una duración de tiempo de la escritura que se pueden ejecutar desde dentro de cada capa? Entonces, el código anterior se vería así:

bash -c 'general_timer_util foo.sh'
dash -c 'general_timer_util foo.sh'

NB: no hay comandos integrados de shell time, ya que ninguno es portátil o general.


Mejor aún si la utilidad también es capaz de comparar el tiempo que tardan los comandos internos y las canalizaciones de un shell, sin que el usuario tenga que envolverlos primero en un script. La sintaxis artificial como esta ayudaría:

general_timer_util "while read x ; do echo x ; done < foo"

Algunos proyectiles timepueden manejar esto. Por ejemplo bash -c "time while false ; do : ; done"funciona. Para ver qué funciona (y qué no) en su sistema, intente:

tail +2 /etc/shells | 
while read s ; do 
    echo $s ; $s -c "time while false ; do : ; done" ; echo ----
done
agc
fuente
66
¿Solo usar /usr/bin/time?
Kusalananda
1
No entiendo cómo cualquier no integrado podría "eliminar el tiempo que tarda cada shell en cargarse e inicializarse" y ejecutar un script independiente mientras es "portátil y general".
Michael Homer
1
Esa no es una respuesta a la pregunta, es un aviso para que aclare lo que quiere.
Michael Homer
1
He publicado mi mejor intento, pero creo que la pregunta aún no se especifica exactamente sobre lo que realmente está tratando de lograr.
Michael Homer
2
¿Qué quiere decir con "portátil o general"? Los componentes integrados de shell son tan portátiles (funcionan en tantos sistemas) y más generales (funcionan en más circunstancias, ya que pueden cronometrar algo más que la ejecución de un archivo) como el comando externo. ¿Que problema estas tratando de resolver?
Gilles 'SO- deja de ser malvado'

Respuestas:

10

Debe tener en cuenta que timePOSIX lo especifica , y AFAICT es la única opción que POSIX menciona ( -p) es compatible con varios shells:

$ bash -c 'time -p echo'

real 0.00
user 0.00
sys 0.00
$ dash -c 'time -p echo'

real 0.01
user 0.00
sys 0.00
$ busybox sh -c 'time -p echo'

real 0.00
user 0.00
sys 0.00
$ ksh -c 'time -p echo'       

real 0.00
user 0.00
sys 0.00
muru
fuente
1
El problema es que para poder comparar tiempos, el resultado debe ser cronometrado por la misma implementación de time. Es comparable a dejar que los velocistas midan su propio tiempo en 100 m, individualmente, en lugar de hacerlo con un reloj al mismo tiempo. Obviamente, esto es una trampa, pero aún así ...
Kusalananda
@Kusalananda Pensé que el problema era que OP pensaba timeque no era portátil; Parece ser portátil. (Sin embargo, estoy de acuerdo con su punto de comparabilidad)
muru
@muru, en mi sistema dash -c 'time -p while false ; do : ; done'devuelve "tiempo: no se puede ejecutar mientras: No existe dicho archivo o directorio <cr> Comando salido con errores de estado no cero 127" .
agc
1
@agc POSIX también dice: "El término utilidad se usa, en lugar de comando, para resaltar el hecho de que los comandos compuestos de shell, canalizaciones, funciones integradas especiales, etc. no se pueden usar directamente. Sin embargo, la utilidad incluye programas de aplicación de usuario y scripts de shell, no solo las utilidades estándar ". (ver sección JUSTIFICACIÓN)
muru
1
@agc manpages.ubuntu.com/manpages/wily/en/man1/time.1posix.html , sección JUSTIFICACIÓN
muru
7

Uso el comando GNU date , que admite un temporizador de alta resolución:

START=$(date +%s.%N)
# do something #######################

"$@" &> /dev/null

#######################################
END=$(date +%s.%N)
DIFF=$( echo "scale=3; (${END} - ${START})*1000/1" | bc )
echo "${DIFF}"

Y luego llamo al guión así:

/usr/local/bin/timing dig +short unix.stackexchange.com
141.835

La unidad de salida está en milisegundos.

Rabin
fuente
1
Suponiendo que el tiempo (tiempo de época) no cambia en el medio. No puedo pensar en un caso en la práctica en el que pueda causar un problema, pero aún vale la pena mencionarlo.
phk
1
Debe agregar que esto requiere GNU dateespecíficamente.
Kusalananda
@phk por favor explique?
Rabin el
1
@Rabin Digamos que sus problemas con el cliente NTP y actualizan y cambian su reloj entre donde STARTy ENDestá configurado, entonces esto obviamente afectaría su resultado. No tengo idea de cuán preciso lo necesita y si importa en su caso, pero como dije, algo a tener en cuenta. (Historia divertida: Sé un software en el que es exactamente lo que condujo a un resultado inesperadamente negativos - se utiliza para cálculos de rendimiento - que luego se rompieron algunas cosas.)
PHK
1
Además, ¿no algunos clientes NTP disminuyen la velocidad y aceleran el reloj, en lugar de hacer "saltos" en la hora del sistema? Si tiene un cliente NTP como ese, e hizo algunos horarios ayer por la noche, probablemente estén sesgados por el cliente NTP "anticipando" el segundo salto. (¿O el reloj del sistema simplemente corre a 61 en ese caso?)
Jörg W Mittag el
6

La timeutilidad generalmente está integrada en el shell, como se habrá dado cuenta, lo que la hace inútil como un temporizador "neutral".

Sin embargo, la utilidad generalmente también está disponible como una utilidad externa /usr/bin/time, que bien puede usarse para realizar los experimentos de temporización que usted propone.

$ bash -c '/usr/bin/time foo.sh'
Kusalananda
fuente
¿Cómo "elimina el tiempo que tarda cada shell en cargarse e inicializarse"?
Michael Homer
1
Si foo.shes ejecutable y tiene un shebang, esto siempre lo ejecuta con el mismo shell, y cuenta el tiempo de inicio de ese shell, por lo que esto no es lo que OP quiere. Si foo.shfalta uno de esos, entonces esto no funciona en absoluto.
Kevin
@ Kevin Muy cierto. timeParece que solo tomé en cuenta "sin shell incorporado ". Es posible que el tiempo de inicio del shell deba medirse por separado.
Kusalananda
1
No conozco ningún shell que tenga un timecomando incorporado. Sin embargo, muchos shells incluyen bashuna timepalabra clave que se puede usar para cronometrar tuberías. Para deshabilitar esa palabra clave para que timese use el comando (en el sistema de archivos), puede citarla como "time" foo.sh. Ver también unix.stackexchange.com/search?q=user%3A22565+time+keyword
Stéphane Chazelas
6

Aquí hay una solución que:

  1. eliminar [s] el tiempo que tarda cada shell en cargarse e inicializarse

  2. se puede ejecutar desde dentro de cada cáscara

  3. Usos

    no hay comandos integrados de shell time, ya que ninguno es portátil o general

  4. Funciona en todos los shells compatibles con POSIX.
  5. Funciona en todos los sistemas compatibles con POSIX y XSI con un compilador de C , o donde puede compilar un ejecutable de C por adelantado.
  6. Utiliza la misma implementación de temporización en todos los shells.

Hay dos partes: un programa C corto que finaliza gettimeofday, que está en desuso pero aún más portátil que clock_gettime, y un script de shell corto que usa ese programa para obtener un reloj de precisión de microsegundos que lee ambos lados de la fuente de un script. El programa C es la única forma portátil y mínima para obtener una precisión de menos de un segundo en una marca de tiempo.

Aquí está el programa C epoch.c:

#include <sys/time.h>
#include <stdio.h>
int main(int argc, char **argv) {
    struct timeval time;
    gettimeofday(&time, NULL);
    printf("%li.%06i", time.tv_sec, time.tv_usec);
}

Y el script de shell timer:

#!/bin/echo Run this in the shell you want to test

START=$(./epoch)
. "$1"
END=$(./epoch)
echo "$END - $START" | bc

Esta es la norma lenguaje de comandos shell y bcy debería funcionar como un guión bajo cualquier shell compatible con POSIX.

Puedes usar esto como:

$ bash timer ./test.sh
.002052
$ dash timer ./test.sh
.000895
$ zsh timer ./test.sh
.000662

No mide el tiempo del sistema o del usuario, solo el tiempo transcurrido del reloj de pared no monótono. Si el reloj del sistema cambia durante la ejecución del script, esto dará resultados incorrectos. Si el sistema está bajo carga, el resultado será poco confiable. No creo que nada mejor pueda ser portátil entre proyectiles.

Un script de temporizador modificado podría usarse evalen su lugar para ejecutar comandos fuera de un script.

Michael Homer
fuente
Casualmente, justo antes de leer esto, (y la última línea sobre eval), estaba modificando el guión en la respuesta de Rabin para incluirlo eval "$@", de modo que pudiera ejecutar construcciones de shell sobre la marcha.
agc
4

Solución revisada varias veces usando /proc/uptimey dc/ bc/ awken partes grandes gracias a la entrada de agc :

#!/bin/sh

read -r before _ < /proc/uptime

sleep 2s # do something...

read -r after _ < /proc/uptime

duration=$(dc -e "${after} ${before} - n")
# Alternative using bc:
#   duration=$(echo "${after} - ${before}" | bc)
# Alternative using awk:
#   duration=$(echo "${after} ${before}" | awk '{print $1 - $2}')

echo "It took $duration seconds."

Asume obviamente que /proc/uptimeexiste y tiene una cierta forma.

phk
fuente
3
Esto lo haría portátil entre shells, pero no portátil entre implementaciones de Unix, ya que algunos simplemente carecen del /procsistema de archivos. Si esto es una preocupación o no, no lo sé.
Kusalananda
1
Para enfatizar, esta Q sugiere velocidades de disquete o peor. En cuyo caso, la sobrecarga de carga awkpuede ser significativa. Tal vez b=$(cat /proc/uptime)antes, luego a=$(cat /proc/uptime)después, luego analice $ a y $ b y reste.
agc
@agc Buena entrada, gracias, agregué algunas soluciones alternativas en consecuencia.
phk
1
No pensar en ello antes, pero si las órdenes internas como readson mejores que cat, esto sería más limpia (y un poco más rápido): read before dummyvar < /proc/uptime ;sleep 2s;read after dummyvar < /proc/uptime; duration=$(dc -e "${after} ${before} - n");echo "It took $duration seconds."
agc