Potrzebujesz pętli do spania na ułamek sekundy


14

Na mojej maszynie muszę wykonać cykl, który iteruje 1 proste polecenie, które musi mieć opóźnienie wyrażone w ułamkach sekundy.

Powiedzmy, że potrzebuję:

  • aby zapisać plik z rosnącą enumaracją (plik-0, plik-1, plik-2, ...) wygeneruj przez coś trywialnego dla tego przykładu, na przykład time > file-$x
  • Muszę to robić co 1/70 sekundy (jako przykład), ponieważ chciałbym wyrazić swój czas ułamkami sekundy.

Jak mogę być bardzo precyzyjny i wszystko wyrażać za pomocą skryptu bash?

Ułamek może generować nieokreśloną ilość, muszę być precyzyjny, więc potrzebuję co najmniej 4-5 miejsc po przecinku.

Odpowiedzi:


12

Aby przekonwertować bash na ułamki zwykłe, zrób coś podobnego

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

Następnie, aby wykonać pętlę na tej wartości,

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

Moja implementacja snu w Debianie (GNU) wydaje się akceptować dziesiętne wartości snu.

Niestety..

Z taką precyzją (4-5 miejsc po przecinku) będziesz potrzebować czegoś takiego jak skrypt perla lub skompilowany program; obciążenie związane z wywoływaniem dowolnego programu w pętli spowoduje wiele zakłóceń. Samo wywołanie snu zajmie kilka milisekund.

Rozważ następujące, 1/10 000 sekundy drugiego snu, wykonane 1000 razy:

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

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

Oczekiwany wynik to 1/10 sekundy. Sen nie ma bliskich tolerancji.

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

używając Perla Czas :: HiRes, 1000 * 1000 mikrosekund:

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

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

daje nam znacznie bliżej sekundy.


jeśli nie jest możliwe osiągnięcie 4-5 miejsc po przecinku, wybrałbym najlepszy możliwy wynik jako alternatywę, ale moim celem jest to, że muszę wyrazić to w ułamkach, a nie po przecinku lub sekundach.
user1717079,

Przekształcenie ułamka dziesiętnego w dowolny język skryptowy, w tym bash, byłoby całkowicie trywialne. np. echo „skala = 4; 5/10” | bc
Rob Bos

teraz mam sposób na konwersję wyrażenia, problem polega na tym, że proste uderzenie jest naprawdę powolne i nie może po prostu nadążyć za tą częstotliwością
user1717079,

Tak, jeśli chcesz czegoś znacznie dokładniejszego niż około 1/10 sekundy, prawdopodobnie będziesz chciał Perla lub Pythona, aby uniknąć wywoływania przez program narzutu w pętli.
Rob Bos

7

Może możesz po prostu uciec

sleep 0.7

?

man 1 sleep

na mojej archlinuxdystrybucji:

OPIS Pauza na NUMBER sekundy. SUFFIX może być „s” dla sekund (domyślnie), „m” dla minut, „h” dla godzin lub „d” dla dni. W przeciwieństwie do większości implementacji, które wymagają, aby NUMBER był liczbą całkowitą, tutaj NUMBER może być dowolną liczbą zmiennoprzecinkową. Biorąc pod uwagę dwa lub więcej argumentów, zatrzymaj się na czas określony przez sumę ich wartości.


sleepdopuszcza ułamki? czy możesz podać pełny przykład z fałszywą pętlą?
user1717079,

0.7 to nie ułamek, który wyraża mój problem ... mój problem dotyczy ziarnistości; pomyśl o 1/3 i staraj się być precyzyjny podczas snu.
user1717079,

6

Spawnowanie procesu i ładowanie nowego pliku wykonywalnego może potrwać kilka milisekund, więc taka precyzja nie ma sensu. Należy również pamiętać, że czas procesora w wielu systemach jest alokowany do procesów w odstępach do 10 ms.

To powiedziawszy, niektóre sleepimplementacje zajmują ułamkowe liczby sekund, a zarówno zsh, jak i ksh93 mogą tworzyć swoją $SECONDSspecjalną zmienną ułamkową typeset -F SECONDS.

Przykład (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

Ups, dryfował. Możesz dostosować czas snu na podstawie $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

Te 2 dodatkowe milisekundy należy prawdopodobnie uwzględnić przy uruchamianiu ostatniego polecenia sleepi datepoleceń.

Zauważ też, że zsh ma zselectwbudowany limit czasu wyrażony w setnych sekundach. A ksh93 ma sleepwbudowane (i akceptuje zmiennoprzecinkowe) i printfmoże drukować datę / godzinę.

$ 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

Jeśli chcesz czegoś bardziej precyzyjnego, prawdopodobnie będziesz chciał systemu operacyjnego w czasie rzeczywistym lub systemu operacyjnego z funkcjami w czasie rzeczywistym i na pewno nie użyje powłoki.


sleep 1/70niedozwolone na mojej maszynie ...
user1717079,

nawet nie blisko tego, co chcę osiągnąć, w jaki sposób mogę wykonywać rzeczy szybciej?
user1717079,

Przepraszam, przez ułamek nie miałem na myśli wyrażonego jako n / m, gdzie n i m są liczbami całkowitymi, ale jako ułamki dziesiętne z częścią ułamkową. Podejrzewam, że można również nazwać je dziesiętnymi liczbami zmiennoprzecinkowymi, chociaż nie jestem pewien co do właściwej angielskiej terminologii
Stéphane Chazelas,

myślę, że
uniknę

Odpowiadający ma rację. Powłoki i procesy zwykle nie są dobrze dostosowane do rozdzielczości milisekundowej. Aby uzyskać niezawodną wydajność, musisz skompilować program, który wywołuje usleep (3) lub podobny - i może być konieczne ponowne skompilowanie jądra z inną konfiguracją; lub przynajmniej podaj różne parametry do harmonogramu uruchomieniowego twojego jądra. To nie jest łatwe. Standardowa instalacja Debiana ze standardowym jądrem ma ograniczone możliwości w czasie rzeczywistym. Nadal możesz chcieć sprawdzić polecenie nice (1); To może pomóc.
thb

3

Jeśli twoja skorupa sleepnie przyjmuje frakcji, użyj perla.

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

sleep_fraction 0.01428

Jeśli chcesz znaleźć frakcję, użyj echo "scale=5; 1/70" | bc


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.