Hai bisogno di un ciclo per dormire per una frazione di secondo


13

Sulla mia macchina ho bisogno di eseguire un ciclo che itera 1 semplice comando che deve avere un ritardo espresso in frazioni di secondo.

Diciamo che ho bisogno di:

  • per salvare un file con una enumarazione crescente (file-0, file-1, file-2, ...) generare da qualcosa di banale per questo esempio come time > file-$x
  • Devo farlo ogni 1/70 di secondo (come esempio) perché vorrei esprimere il mio tempo con frazioni di secondo.

Come posso essere molto preciso e esprimere tutto con uno script bash?

La frazione può generare una quantità indeterminabile, devo essere preciso e quindi ho bisogno di almeno 4-5 decimali.

Risposte:


11

Per convertire da frazioni a decimali in bash, fai qualcosa di simile

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

Quindi, per fare un ciclo su quel valore,

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

La mia implementazione del sonno su Debian (GNU) sembra accettare valori decimali del sonno.

Sfortunatamente..

Con quel tipo di precisione (4-5 decimali), vorrai qualcosa come uno script perl o un programma compilato; l'overhead di chiamare qualsiasi programma all'interno del loop aggiungerà molto jitter. Chiamare il sonno stesso richiederà alcuni millisecondi.

Considera quanto segue, un millesimo di secondo di sonno, eseguito 1000 volte:

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

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

Il risultato atteso sarebbe 1/10 di secondo. Il sonno non ha nulla vicino alle tolleranze che desideri.

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

utilizzando perl's Time :: HiRes, 1000 * 1000 microsecondi:

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

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

ci avvicina molto di più a un secondo.


se non è possibile ottenere 4-5 decimali prenderei il miglior risultato possibile in alternativa, ma il mio punto è che devo esprimerlo in frazioni, non decimali o secondi.
user1717079,

Sarebbe assolutamente banale convertire una frazione in decimale in qualsiasi linguaggio di scripting, incluso bash. ad esempio echo "scale = 4; 5/10" | aC
Rob Bos,

ora ho come convertire l'espressione, il problema ora è che un semplice bash è davvero lento e non può semplicemente tenere il passo con questa frequenza ...
user1717079

Sì, se vuoi qualcosa di molto più preciso di circa 1/10 di secondo, probabilmente vorrai perl o python in modo da evitare che il programma chiami overhead all'interno del loop.
Rob Bos,

7

Forse puoi semplicemente correre

sleep 0.7

?

man 1 sleep

sulla mia archlinuxdistribuzione:

DESCRIZIONE Pausa per NUMBER secondi. SUFFIX può essere 's' per secondi (impostazione predefinita), 'm' per minuti, 'h' per ore o 'd' per giorni. A differenza della maggior parte delle implementazioni che richiedono che NUMBER sia un numero intero, qui NUMBER può essere un numero in virgola mobile arbitrario. Dati due o più argomenti, mettere in pausa per il tempo specificato dalla somma dei loro valori.


sleeppermette le frazioni? puoi offrire un esempio completo con un ciclo falso?
user1717079,

0.7 non è una frazione che esprime il mio problema ... il mio problema riguarda la granularità; pensa a 1/3 e cerca di essere preciso con il sonno.
user1717079,

6

Generare un processo e caricare un nuovo eseguibile in esso richiederà probabilmente alcuni millisecondi, quindi quel tipo di precisione non ha davvero senso. Si noti inoltre che il tempo della CPU su molti sistemi è allocato ai processi da sezioni fino a 10 ms.

Detto questo, alcune sleepimplementazioni richiedono un numero frazionario di secondi e sia zsh che ksh93 possono rendere $SECONDSfrazionaria la loro variabile speciale typeset -F SECONDS.

Esempio (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

Oops, è andato alla deriva. È possibile regolare il tempo di sonno in base a $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

Quei 2 millisecondi in più devono probabilmente essere spiegati per l'esecuzione dell'ultimo sleepe dei datecomandi.

Si noti inoltre che zsh ha un zselectbuilt-in con timeout espresso in centesimi di secondo. E ksh93 ha sleepincorporato (e accetta i punti mobili) e printfpuò stampare la data / ora.

$ 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

Se vuoi qualcosa di più preciso, probabilmente vorrai un sistema operativo in tempo reale o un sistema operativo con funzionalità in tempo reale e certamente non usare una shell.


sleep 1/70non consentito sulla mia macchina ...
user1717079

nemmeno vicino a ciò che voglio ottenere, come posso eseguire le cose più velocemente?
user1717079,

Scusate, per frazione, non intendevo espresso come n / m dove n e m sono numeri interi, ma come decimali con una parte frazionaria. Suppongo che potresti anche chiamarli numeri decimali in virgola mobile anche se non sono sicuro della corretta terminologia inglese
Stéphane Chazelas,

penso che
eviterò

Il risponditore ha ragione. In genere, shell e processi non sono adatti alla risoluzione in millisecondi. Per prestazioni affidabili, dovrai compilare un programma che chiama usleep (3) o simili - e potresti dover ricompilare il tuo kernel con una configurazione diversa; o almeno alimentare parametri diversi al programmatore di runtime del kernel. Questo non è banale da fare. Un'installazione Debian standard con un kernel standard ha capacità in tempo reale limitate. Tuttavia, potresti voler dare un'occhiata al bel comando (1); potrebbe aiutare.
thb,

3

Se la tua shell sleepnon accetta la frazione, usa perl.

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

sleep_fraction 0.01428

Se è necessario scoprire la frazione, utilizzare echo "scale=5; 1/70" | bc


0

In Alpine Linux (Busybox) è possibile eseguire il ciclo per microsecondi con usleep 10(equivalente a 0.00001di un secondo)

GNU sleep supporta le frazioni di secondo: sleep 0.00001

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.