Jak dokładna jest metoda time.sleep () w Pythonie?

95

Mogę mu podać liczby zmiennoprzecinkowe, takie jak

time.sleep(0.5)

ale jak dokładne jest to? Jeśli to dam

time.sleep(0.05)

czy to naprawdę będzie spać około 50 ms?

Claudiu
źródło

Odpowiedzi:

79

Dokładność funkcji time.sleep zależy od dokładności uśpienia systemu operacyjnego. W przypadku systemów operacyjnych nie działających w czasie rzeczywistym, takich jak standardowy system Windows, najmniejszy odstęp czasu, w którym można spać, wynosi około 10-13 ms. Widziałem dokładne uśpienia w ciągu kilku milisekund od tego czasu, gdy powyżej minimum 10-13 ms.

Aktualizacja: Jak wspomniano w przytoczonych poniżej dokumentach, często spać w pętli, która zapewni powrót do snu, jeśli obudzi Cię wcześnie.

Powinienem również wspomnieć, że jeśli używasz Ubuntu, możesz wypróbować jądro pseudo czasu rzeczywistego (z zestawem poprawek RT_PREEMPT), instalując pakiet jądra rt (przynajmniej w Ubuntu 10.04 LTS).

EDYCJA: Korekcja jądra Linuksa nie działającego w czasie rzeczywistym ma minimalny interwał uśpienia znacznie bliższy 1 ms niż 10 ms, ale zmienia się w sposób niedeterministyczny.

Joseph Lisee
źródło
8
W rzeczywistości jądra Linuksa mają domyślnie wyższą częstotliwość taktowania od dłuższego czasu, więc „minimalny” czas uśpienia jest znacznie bliższy 1 ms niż 10 ms. Nie jest to gwarantowane - inna aktywność systemu może sprawić, że jądro nie będzie w stanie zaplanować twojego procesu tak szybko, jak chcesz, nawet bez rywalizacji o procesor. Myślę, że właśnie to jądra czasu rzeczywistego próbują naprawić. Ale jeśli naprawdę nie potrzebujesz zachowania w czasie rzeczywistym, po prostu użycie wysokiej częstotliwości taktowania (ustawienie HZ jądra) zapewni ci nie gwarantowany, ale wysoką rozdzielczość uśpienia w Linuksie bez użycia niczego specjalnego.
Glenn Maynard,
1
Tak, masz rację, próbowałem z Linuksem 2.6.24-24 i byłem w stanie osiągnąć częstotliwość aktualizacji prawie 1000 Hz. W tym czasie wykonywałem również kod na komputerach Mac i Windows, więc prawdopodobnie byłem zdezorientowany. Wiem, że Windows XP ma przynajmniej częstotliwość taktowania około 10 ms.
Joseph Lisee,
Na Windows 8 dostaję niecałe 2
ms
2
Również dokładność nie zależy tylko od systemu operacyjnego, ale także od tego, co system operacyjny robi w systemach Windows i Linux, jeśli są zajęci wykonywaniem czegoś ważniejszego sleep()z dokumentacji „czas zawieszenia może być dłuższy niż żądany przez dowolną kwotę z powodu harmonogramu inna aktywność w systemie ”.
markmnl
56

Ludzie mają rację co do różnic między systemami operacyjnymi i jądrem, ale nie widzę żadnej szczegółowości w Ubuntu, a widzę ziarnistość 1 ms w MS7. Sugerowanie innej implementacji time.sleep, a nie tylko innej częstotliwości taktowania. Bliższa inspekcja sugeruje przy okazji ziarnistość 1μs w Ubuntu, ale wynika to z funkcji time.time, której używam do pomiaru dokładności. Typowe zachowanie time.sleep w Linuksie i Windowsie w Pythonie

Wilberta
źródło
6
Interesujące jest to, że Linux zdecydował się zawsze spać nieco dłużej niż żądano, podczas gdy Microsoft wybrał podejście odwrotne.
jleahy
2
@jleahy - podejście linuxowe ma dla mnie sens: uśpienie jest tak naprawdę zwolnieniem priorytetu wykonania na pewien czas, po którym ponownie poddajesz się woli harmonogramu (który może zaplanować wykonanie od razu lub nie) .
podjechał
2
jak uzyskałeś wyniki? Czy możesz podać kod źródłowy? Wykres wygląda jak artefakt użycia różnych timerów do pomiaru czasu i snu (w zasadzie można nawet użyć dryftu między zegarami jako źródła losowości ).
jfs
2
@JF Sebastian - Funkcja, której użyłem, znajduje się w socsci.ru.nl/wilberth/computer/sleepAccuracy.html . Trzeci wykres pokazuje efekt podobny do tego, który widzisz, ale tylko 1 ‰.
Wilbert
1
@JF Sebastian Używam time.clock () w Windowsie
Wilbert
26

Z dokumentacji :

Z drugiej strony, precyzja time()i sleep()jest lepiej niż ich odpowiedniki Unix: czasy są wyrażone w liczbach zmiennoprzecinkowych, time()zwraca najbardziej dokładny czas dostępne (za pomocą Unix gettimeofday , gdzie jest dostępny) i sleep()zaakceptuje razem z frakcją niezerową (Unix selectjest używany do wdrożenia tego, jeśli jest to możliwe).

A dokładniej wrt sleep():

Wstrzymaj wykonywanie na podaną liczbę sekund. Argumentem może być liczba zmiennoprzecinkowa wskazująca dokładniejszy czas uśpienia. Rzeczywisty czas zawieszenia może być krótszy niż żądany, ponieważ każdy przechwycony sygnał zakończy sleep()kolejne wykonanie procedury przechwytywania tego sygnału. Ponadto czas zawieszenia może być dłuższy niż żądany przez dowolną kwotę ze względu na harmonogram innych działań w systemie.

Stephan202
źródło
1
Czy ktoś może wyjaśnić, że „ponieważ każdy przechwycony sygnał zakończy uśpienie () po wykonaniu procedury przechwytywania tego sygnału”? Do jakich sygnałów to się odnosi? Dzięki!
Diego Herranz,
1
Sygnały są jak powiadomienia, którymi zarządza system operacyjny ( en.wikipedia.org/wiki/Unix_signal ), oznacza to, że jeśli system operacyjny złapie sygnał, uśpienie () jest zakończone po przetworzeniu tego sygnału.
ArianJM
24

Oto moja kontynuacja odpowiedzi Wilberta: to samo dla Mac OS X Yosemite, ponieważ nie zostało jeszcze dużo wspominane.Zachowanie systemu Mac OS X Yosemite podczas snu

Wygląda na to, że przez większość czasu przesypia około 1,25 razy dłużej niż prosisz, a czasami przesypia od 1 do 1,25 razy dłużej niż prosisz. Prawie nigdy (~ dwa razy na 1000 próbek) śpi znacznie dłużej niż 1,25 razy dłużej niż żądasz.

Również (nie pokazane bezpośrednio) zależność 1,25 wydaje się utrzymywać całkiem dobrze, dopóki nie spadnie poniżej około 0,2 ms, po czym zaczyna się trochę rozmywać. Ponadto wydaje się, że rzeczywisty czas ustala się o około 5 ms dłużej niż żądasz, gdy żądany czas przekroczy 20 ms.

Ponownie wydaje się, że jest to zupełnie inna implementacja sleep()w systemie OS X niż w systemie Windows lub jakimkolwiek jądrze Linuksa, którego używał Wilbert.

Tim Supinie
źródło
Czy mógłbyś przesłać kod źródłowy testu porównawczego na github / bitbucket?
jfs
3
Wypróbowałem to na moim komputerze. Wynik jest podobny do odpowiedzi @ Wilberta .
jfs
Sądzę, że sam sen jest dokładny, ale harmonogram Mac OS X nie jest wystarczająco dokładny, aby zapewnić wystarczająco szybki procesor, aby wybudzenie ze snu było opóźnione. Jeśli ważny jest dokładny czas budzenia, wydaje się, że sen powinien być ustawiony na 0,75 razy większy niż faktycznie wymagany i sprawdzać czas po przebudzeniu i wielokrotnie spać coraz rzadziej, aż do właściwego czasu.
Mikko Rantalainen
16

Dlaczego się nie dowiesz:

from datetime import datetime
import time

def check_sleep(amount):
    start = datetime.now()
    time.sleep(amount)
    end = datetime.now()
    delta = end-start
    return delta.seconds + delta.microseconds/1000000.

error = sum(abs(check_sleep(0.050)-0.050) for i in xrange(100))*10
print "Average error is %0.2fms" % error

Dla przypomnienia, pojawia się błąd około 0,1 ms na moim HTPC i 2 ms na moim laptopie, na obu komputerach z systemem Linux.

Ants Aasma
źródło
10
Testy empiryczne dają bardzo wąski obraz. Istnieje wiele jąder, systemów operacyjnych i konfiguracji jądra, które mają na to wpływ. Starsze jądra Linuksa domyślnie mają niższą częstotliwość taktowania, co powoduje większą szczegółowość. W implementacji Unix, sygnał zewnętrzny podczas uśpienia anuluje go w dowolnym momencie, a inne implementacje mogą mieć podobne zakłócenia.
Glenn Maynard,
6
Cóż, oczywiście obserwacji empirycznej nie można przenieść. Oprócz systemów operacyjnych i jądra istnieje wiele przejściowych problemów, które mają na to wpływ. Jeśli wymagane są twarde gwarancje w czasie rzeczywistym, należy wziąć pod uwagę cały projekt systemu, od sprzętu w górę. Właśnie uznałem wyniki za istotne, biorąc pod uwagę stwierdzenia, że ​​10 ms to minimalna dokładność. Nie ma mnie w domu w świecie Windowsa, ale większość dystrybucji Linuksa od jakiegoś czasu używa jądra bez kleszczy. W przypadku rozpowszechnienia wielu rdzeni jest prawdopodobne, że zostanie zaplanowany bardzo blisko limitu czasu.
Ants Aasma,
4

Mała korekta, kilka osób wspomina, że ​​sen można zakończyć wcześnie sygnałem. W dokumentach 3.6 mówi:

Zmieniono w wersji 3.5: Funkcja zasypia teraz co najmniej sekund, nawet jeśli uśpienie jest przerywane przez sygnał, chyba że program obsługi sygnału zgłosi wyjątek ( uzasadnienie w PEP 475 ).

user405
źródło
3

Naprawdę nie możesz zagwarantować niczego związanego ze snem (), z wyjątkiem tego, że przynajmniej postara się zasnąć tak długo, jak to powiedziałeś (sygnały mogą zabić twój sen, zanim upłynie czas, a wiele innych rzeczy może sprawić, że będzie działał długie).

Na pewno minimum, które możesz uzyskać w standardowym systemie operacyjnym dla komputerów stacjonarnych, będzie wynosić około 16 ms (szczegółowość timera plus czas do przełączenia kontekstu), ale są szanse, że odchylenie% od podanego argumentu będzie znaczące, gdy próbujesz spać przez 10 milisekund.

Sygnały, inne wątki przechowujące GIL, zabawa z planowaniem jądra, stopniowanie szybkości procesora itp. Mogą siać spustoszenie w czasie, w którym wątek / proces faktycznie śpi.

Nick Bastin
źródło
3
Dokumentacja mówi inaczej:> Rzeczywisty czas zawieszenia może być krótszy niż żądany, ponieważ każdy przechwycony sygnał zakończy sleep () po wykonaniu procedury przechwytywania tego sygnału.
Glenn Maynard,
Ach słusznie, naprawiłem słupek, chociaż dłuższe śpi () jest znacznie bardziej prawdopodobne niż krótsze.
Nick Bastin,
1
Dwa i pół roku później ... dokumentacja wciąż leży. W systemie Windows sygnały nie przerywają funkcji sleep (). Przetestowano w Pythonie 3.2, WinXP SP3.
Dave
Tak, ale sygnały wyprzedzające sen są nietypowe, np. KILL, w dokumentacji jest również napisane: „Ponadto czas zawieszenia może być dłuższy niż żądany przez dowolną kwotę ze względu na harmonogram innej aktywności w systemie”. co jest bardziej typowe.
markmnl
1
Singnals i Windows są po prostu głupie. W Windows Python time.sleep () czeka na zdarzenie ConsoleEvent, aby przechwycić rzeczy takie jak Ctrl-C.
schlenk
1

jeśli potrzebujesz większej precyzji lub krótszych czasów snu, rozważ stworzenie własnego:

import time

def sleep(duration, get_now=time.perf_counter):
    now = get_now()
    end = now + duration
    while now < end:
        now = get_now()
Lars
źródło
0

Testowałem to niedawno w Pythonie 3.7 w systemie Windows 10. Precyzja wynosiła około 1 ms.

Aleksandar Kiridžić
źródło
3
Jak to przetestowałeś?
Agustin Barrachina
0
def start(self):
    sec_arg = 10.0
    cptr = 0
    time_start = time.time()
    time_init = time.time()
    while True:
        cptr += 1
        time_start = time.time()
        time.sleep(((time_init + (sec_arg * cptr)) - time_start ))

        # AND YOUR CODE .......
        t00 = threading.Thread(name='thread_request', target=self.send_request, args=([]))
        t00.start()

Nie używaj zmiennej do przekazywania argumentu sleep (), musisz wstawić obliczenie bezpośrednio do funkcji sleep ()


I zwrot mojego terminala

1 ───── 17:20: 16,891 ───────────────────

2 ───── 17:20: 18,891 ───────────────────

3 ───── 17:20: 20,891 ───────────────────

4 ───── 17:20: 22,891 ───────────────────

5 ───── 17:20: 24,891 ───────────────────

....

689 ─── 17:43: 12,891 ────────────────────

690 ─── 17:43: 14,890 ────────────────────

691 ─── 17:43: 16,891 ────────────────────

692 ─── 17:43: 18,890 ────────────────────

693 ─── 17:43: 20,891 ────────────────────

...

727 ─── 17:44: 28,891 ────────────────────

728 ─── 17:44: 30,891 ────────────────────

729 ─── 17: 44: 32,891 ────────────────────

730 ─── 17:44: 34,890 ────────────────────

731 ─── 17: 44: 36,891 ────────────────────

Forrest
źródło
0
def test():
    then = time.time()  # get time at the moment
    x = 0
    while time.time() <= then+1:  # stop looping after 1 second
        x += 1
        time.sleep(0.001)  # sleep for 1 ms
    print(x)

W 1000systemie Windows 7 / Python 3.8 zwrócił mi się, nawet jeśli ustawię wartość uśpienia na0.0005

więc idealny 1 ms

Walter
źródło