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.