Necesita un ciclo para dormir por una fracción de segundo

13

En mi máquina, necesito ejecutar un ciclo que repita 1 comando simple que debe tener un retraso expresado en fracciones de segundo.

Digamos que necesito:

  • para guardar un archivo con una enumeración creciente (archivo-0, archivo-1, archivo-2, ...) generado por algo trivial para este ejemplo como time > file-$x
  • Necesito hacer esto cada 1/70 de segundo (como ejemplo) porque me gustaría expresar mi tiempo con fracciones de segundo.

¿Cómo puedo ser realmente preciso y tener todo expresado con un script bash?

La fracción puede generar una cantidad indeterminable, necesito ser preciso y por lo tanto necesito al menos 4-5 decimales.

usuario1717079
fuente

Respuestas:

11

Para convertir de fracciones a decimales en bash, haga algo como

myvar=$(echo "scale=4; 5/10" | bc)

Luego, para hacer un bucle sobre ese valor,

for i in $(seq 1 1000); do sleep $myvar; done

Mi implementación de suspensión en Debian (GNU) parece aceptar valores de suspensión decimales.

Desafortunadamente..

Con ese tipo de precisión (4-5 decimales), querrá algo como un script perl o un programa compilado; la sobrecarga de llamar a cualquier programa dentro del bucle va a agregar mucha inquietud. Llamar al sueño en sí llevará unos pocos milisegundos.

Considere lo siguiente, 1 / 10,000ths de un segundo sueño, hecho 1000 veces:

time for i in $(seq 1 1000); do sleep 0.0001; done

real    0m2.099s
user    0m3.080s
sys     0m1.464s

El resultado esperado sería 1/10 de segundo. Dormir no tiene las tolerancias que deseas.

/programming/896904/how-do-i-sleep-for-a-millisecond-in-perl

usando Perl's Time :: HiRes, 1000 * 1000 microsegundos:

my $i=0;
for($i=0;$i<=1000;$i++) {
        usleep(1000);
}

real    0m1.133s
user    0m0.024s
sys     0m0.012s

nos da mucho más cerca de un segundo.

Rob Bos
fuente
si no es posible lograr 4-5 decimales, tomaría el mejor resultado posible como alternativa, pero mi punto es que necesito expresar esto en fracciones, no en decimales o segundos.
user1717079
Sería completamente trivial convertir una fracción a un decimal en cualquier lenguaje de script, incluido bash. por ejemplo, echo "escala = 4; 5/10" | aC
Rob Bos
Ahora tengo cómo convertir la expresión, el problema ahora es que un simple golpe es muy lento y no puede simplemente mantenerse al día con esta frecuencia ...
user1717079
Sí, si desea algo mucho más preciso que aproximadamente 1/10 de segundo, probablemente querrá perl o python para evitar que el programa llame a la sobrecarga dentro del bucle.
Rob Bos
7

Quizás puedas simplemente correr

sleep 0.7

?

man 1 sleep

en mi archlinuxdistro:

DESCRIPCIÓN Pausa durante NUMBER segundos. SUFFIX puede ser 's' por segundos (el valor predeterminado), 'm' por minutos, 'h' por horas o 'd' por días. A diferencia de la mayoría de las implementaciones que requieren que NUMBER sea un número entero, aquí NUMBER puede ser un número arbitrario de coma flotante. Dados dos o más argumentos, haga una pausa durante el tiempo especificado por la suma de sus valores.

Gilles Quenot
fuente
sleeppermite fracciones? ¿Puedes ofrecer un ejemplo completo con un bucle falso?
usuario1717079
0.7 no es una fracción que expresa mi problema ... mi problema es sobre granularidad; piense en 1/3 e intente ser preciso con el sueño.
user1717079
6

Es probable que generar un proceso y cargar un nuevo ejecutable en él tarde unos pocos milisegundos, por lo que ese tipo de precisión realmente no tiene sentido. También tenga en cuenta que el tiempo de CPU en muchos sistemas se asigna a los procesos por segmentos de hasta 10 ms.

Dicho esto, algunas sleepimplementaciones toman números fraccionales de segundos, y tanto zsh como ksh93 pueden hacer que su $SECONDSvariable especial sea fraccional con typeset -F SECONDS.

Ejemplo (zsh):

$ typeset -F SECONDS=0; for ((i=1; i<=70; i++)); do sleep $((1./70)); date +%s.%N; done | { head -n3;echo ..;tail -n3; }; echo $SECONDS
1350076317.374870501
1350076317.391034397
1350076317.407278461
..
1350076318.464585550
1350076318.480887660
1350076318.497133050
1.1393780000

Vaya, se fue a la deriva. Puede ajustar el tiempo de sueño según $SECONDS:

$ typeset -F SECONDS=0; for ((i=1; i<=70; i++)); do sleep $((i/70. - SECONDS)); date +%s.%N; done | { head -n3;echo ...;tail -n3; }; echo $SECONDS
1350076420.262775654
1350076420.277012997
1350076420.291302750
../..
1350076421.219682227
1350076421.234134663
1350076421.248255685
1.0020580000

Esos 2 milisegundos adicionales probablemente se deban a ejecutar el último sleepy los datecomandos.

También tenga en cuenta que zsh tiene un valor zselectincorporado con tiempo de espera expresado en centésimas de segundo. Y ksh93 se ha sleepincorporado (y acepta puntos flotantes) y printfpuede imprimir fechas / horas.

$ typeset -F SECONDS=0; for ((i=1; i<=70; i++)); do ((i<4 || i>67)) && printf '%(%S.%N)T\n' now; sleep $((i/70.-SECONDS)); done; echo $SECONDS
20.823349000
20.837510000
20.851663000
21.780099000
21.794254000
21.808405000
0.9992358685

Si desea algo más preciso, es probable que desee un sistema operativo en tiempo real o un sistema operativo con capacidades en tiempo real y ciertamente no usar un shell.

Stéphane Chazelas
fuente
sleep 1/70no permitido en mi máquina ...
user1717079
ni siquiera cerca de lo que quiero lograr, ¿cómo puedo ejecutar las cosas más rápido?
user1717079
Lo siento, por fraccionario, no quise expresar como n / m donde n y m son enteros, sino como decimales con una parte fraccionaria. Supongo que también podría llamarlos números decimales de coma flotante, aunque no estoy seguro de la terminología adecuada en inglés
Stéphane Chazelas
Creo que voy a evitar el del shell ...
user1717079
El que responde tiene razón. Los shells y los procesos generalmente no son adecuados para una resolución de milisegundos. Para un rendimiento confiable, necesitará compilar un programa que llame a usleep (3) o similar, y es posible que también necesite recompilar su kernel con una configuración diferente; o al menos alimente diferentes parámetros al programador de tiempo de ejecución de su núcleo. Esto no es trivial de hacer. Una instalación estándar de Debian con un núcleo estándar tiene capacidades limitadas en tiempo real. Aún así, es posible que desee comprobar el comando nice (1); podría ayudar.
thb
3

Si su caparazón sleepno acepta fracción, use perl.

sleep_fraction() {
  /usr/bin/perl -e "select(undef, undef, undef, $1)"
}

sleep_fraction 0.01428

Si necesita averiguar la fracción, use echo "scale=5; 1/70" | bc

lolesque
fuente
0

En Alpine Linux (Busybox) puede recorrer microsegundos con usleep 10(equivalente a 0.00001un segundo)

GNU sleep admite fracciones de segundo: sleep 0.00001

Stuart Cardall
fuente