Quão preciso é time.sleep () de python?


94

Posso dar números de ponto flutuante, como

time.sleep(0.5)

mas quão preciso é? Se eu der

time.sleep(0.05)

ele realmente dormirá cerca de 50 ms?

Respostas:


77

A precisão da função time.sleep depende da precisão do sono do seu sistema operacional. Para sistemas operacionais não em tempo real, como um Windows padrão, o menor intervalo pelo qual você pode dormir é de cerca de 10-13 ms. Tenho visto dormidas precisas em vários milissegundos desse tempo, quando acima do mínimo de 10-13 ms.

Atualização: Como mencionado nos documentos citados abaixo, é comum fazer o sleep in the loop que fará com que você volte a dormir caso acorde cedo.

Devo também mencionar que se você estiver executando o Ubuntu, pode experimentar um kernel pseudo-tempo real (com o conjunto de patches RT_PREEMPT) instalando o pacote do kernel rt (pelo menos no Ubuntu 10.04 LTS).

EDIT: Kernels de correção não em tempo real do Linux têm intervalo mínimo de hibernação muito mais próximo de 1 ms do que 10 ms, mas varia de maneira não determinística.


8
Na verdade, os kernels do Linux foram padronizados para uma taxa de tique mais alta por um bom tempo, então o sono "mínimo" é muito mais próximo de 1 ms do que 10 ms. Não é garantido - outra atividade do sistema pode tornar o kernel incapaz de agendar seu processo assim que você desejar, mesmo sem contenção de CPU. Isso é o que os kernels em tempo real estão tentando consertar, eu acho. Mas, a menos que você realmente precise de um comportamento em tempo real, simplesmente usar uma alta taxa de tique (configuração HZ do kernel) fará com que você adormeça não garantida, mas em alta resolução no Linux sem usar nada especial.
Glenn Maynard

1
Sim, você está certo, tentei com o Linux 2.6.24-24 e consegui obter taxas de atualização bem próximas de 1000 Hz. Na época em que eu estava fazendo isso, também estava executando o código no Mac e no Windows, então provavelmente fiquei confuso. Eu sei que o Windows XP tem pelo menos uma taxa de tique de cerca de 10 ms.
Joseph Lisee

No Windows 8, fico um pouco menos de 2ms
markmnl

2
Além disso, a precisão não depende apenas do sistema operacional, mas do que o sistema operacional está fazendo no Windows e no Linux, se eles estão ocupados fazendo algo mais importante sleep()dos documentos "o tempo de suspensão pode ser mais longo do que o solicitado por uma quantidade arbitrária por causa da programação de outra atividade no sistema ".
markmnl

55

As pessoas estão certas sobre as diferenças entre sistemas operacionais e kernels, mas não vejo nenhuma granularidade no Ubuntu e vejo uma granularidade de 1 ms no MS7. Sugerindo uma implementação diferente de time.sleep, não apenas uma taxa de tick diferente. A propósito, uma inspeção mais detalhada sugere uma granularidade de 1μs no Ubuntu, mas isso se deve à função time.time que uso para medir a precisão. Comportamento time.sleep típico do Linux e Windows em Python


6
É interessante como o Linux optou por sempre dormir um pouco mais do que o solicitado, enquanto a Microsoft escolheu a abordagem oposta.
jleahy

2
@jleahy - a abordagem do Linux faz sentido para mim: dormir é realmente uma liberação de prioridade de execução por um período de tempo após o qual você mais uma vez se submete à vontade do planejador (que pode ou não agendá-lo para execução imediata) .
underrun

2
como você conseguiu os resultados? Você poderia fornecer o código-fonte? O gráfico parece um artefato de usar diferentes temporizadores para medir o tempo e o sono (em princípio, você pode até usar o desvio entre os temporizadores como uma fonte de aleatoriedade ).
jfs

1
@JF Sebastian - A função que usei está em socsci.ru.nl/wilberth/computer/sleepAccuracy.html . O terceiro gráfico mostra um efeito semelhante ao que você vê, mas de apenas 1 ‰.
Wilbert

1
@JF Sebastian Eu uso time.clock () no Windows
Wilbert

26

Da documentação :

Por outro lado, a precisão de time()e sleep()é melhor do que seus equivalentes Unix: os tempos são expressos como números de ponto flutuante, time()retorna o tempo mais preciso disponível (usando Unix gettimeofday quando disponível) e sleep()aceitará um tempo com uma fração diferente de zero (Unix selecté usado para implementar isso, quando disponível).

E mais especificamente wrt sleep():

Suspenda a execução por um determinado número de segundos. O argumento pode ser um número de ponto flutuante para indicar um tempo de sono mais preciso. O tempo de suspensão real pode ser menor do que o solicitado porque qualquer sinal capturado encerrará a sleep()execução seguinte da rotina de captura desse sinal. Além disso, o tempo de suspensão pode ser maior do que o solicitado por um valor arbitrário devido ao agendamento de outra atividade no sistema.


1
Alguém pode explicar o "porque qualquer sinal capturado encerrará o sleep () após a execução da rotina de captura desse sinal"? A quais sinais ele está se referindo? Obrigado!
Diego Herranz,

1
Os sinais são como notificações que o SO gerencia ( en.wikipedia.org/wiki/Unix_signal ), isso significa que se o SO captou um sinal, o sleep () terminou após tratar esse sinal.
ArianJM

23

Aqui está o meu acompanhamento à resposta de Wilbert: o mesmo para Mac OS X Yosemite, já que ainda não foi muito mencionado.Comportamento de suspensão do Mac OS X Yosemite

Parece que muitas vezes ele dorme cerca de 1,25 vezes o tempo que você solicitou e às vezes dorme entre 1 e 1,25 vezes o tempo que você solicitou. Quase nunca (~ duas vezes em 1000 amostras) dorme significativamente mais do que 1,25 vezes o tempo que você solicitar.

Além disso (não mostrado explicitamente), o relacionamento de 1,25 parece se manter muito bem até você ficar abaixo de cerca de 0,2 ms, após o qual começa a ficar um pouco confuso. Além disso, o tempo real parece se estabilizar em cerca de 5 ms a mais do que o solicitado depois que a quantidade de tempo solicitada ficar acima de 20 ms.

Novamente, parece ser uma implementação completamente diferente sleep()no OS X do que no Windows ou qualquer outro kernel do Linux que Wilbert estava usando.


Você poderia fazer upload do código-fonte do benchmark para github / bitbucket?
jfs

3
jfs

Eu acho que o sono em si é preciso, mas o agendamento do Mac OS X não é preciso o suficiente para fornecer CPU rápido o suficiente para que o despertar do sono seja atrasado. Se a hora exata para acordar for importante, parece que o sono deve ser definido para 0,75 vezes o realmente solicitado e verificar a hora após acordar e dormir repetidamente cada vez menos até a hora correta.
Mikko Rantalainen

16

Por que você não descobre:

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

Para o registro, eu obtenho cerca de 0,1ms de erro no meu HTPC e 2ms no meu laptop, ambas as máquinas Linux.


10
O teste empírico lhe dará uma visão muito limitada. Existem muitos kernels, sistemas operacionais e configurações de kernel que afetam isso. Kernels Linux mais antigos têm como padrão uma taxa de tique mais baixa, o que resulta em uma granularidade maior. Na implementação do Unix, um sinal externo durante o sono irá cancelá-lo a qualquer momento, e outras implementações podem ter interrupções semelhantes.
Glenn Maynard

6
Bem, é claro que a observação empírica não é transferível. Além de sistemas operacionais e kernels, existem muitos problemas transitórios que afetam isso. Se forem necessárias garantias rígidas em tempo real, todo o projeto do sistema, desde o hardware até o momento, deve ser levado em consideração. Acabei de achar os resultados relevantes considerando as afirmações de que 10ms é a precisão mínima. Não estou em casa no mundo do Windows, mas a maioria das distros Linux já rodam kernels tickless há algum tempo. Com os multicores agora predominantes, é muito provável que seja agendado muito perto do tempo limite.
Formigas Aasma

4

Uma pequena correção, várias pessoas mencionam que o sono pode ser interrompido mais cedo por um sinal. Nos documentos 3.6 , diz:

Alterado na versão 3.5: A função agora dorme pelo menos segundos, mesmo se o sono for interrompido por um sinal, exceto se o manipulador de sinal levantar uma exceção (consulte PEP 475 para a justificativa).


3

Você realmente não pode garantir nada sobre o sono (), exceto que ele fará pelo menos um esforço para dormir, desde que você diga (sinais podem matar seu sono antes que o tempo acabe, e muitas outras coisas podem fazê-lo funcionar longo).

Com certeza, o mínimo que você pode obter em um sistema operacional de desktop padrão será em torno de 16 ms (granularidade do cronômetro mais tempo para alternar o contexto), mas as chances são de que a% de desvio do argumento fornecido será significativo quando você tentar dormir por 10 segundos de milissegundos.

Sinais, outros threads que prendem o GIL, agendamento de kernel divertido, aumento da velocidade do processador, etc. podem causar estragos na duração do seu thread / processo realmente adormecido.


3
A documentação diz o contrário:> O tempo real de suspensão pode ser menor do que o solicitado porque qualquer sinal capturado encerrará o sleep () após a execução da rotina de captura daquele sinal.
Glenn Maynard

Ah, ponto justo, consertei a postagem, embora dormir mais () seja muito mais provável do que dormir mais.
Nick Bastin

1
Dois anos e meio depois ... a documentação ainda está. No Windows, os sinais não encerram a suspensão (). Testado em Python 3.2, WinXP SP3.
Dave

Sim, mas sinaliza que o sono preventivo é incomum, por exemplo, KILL, a documentação também diz: "Além disso, o tempo de suspensão pode ser mais longo do que o solicitado por um valor arbitrário por causa do agendamento de outra atividade no sistema." o que é mais típico.
markmnl

1
Singnals e Windows são simplesmente bobos. No Windows, o Python time.sleep () espera um ConsoleEvent para capturar coisas como Ctrl-C.
schlenk

1

se precisar de mais precisão ou menos horas de sono, considere fazer o seu próprio:

import time

def sleep(duration, get_now=time.perf_counter):
    now = get_now()
    end = now + duration
    while now < end:
        now = get_now()


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

Não use uma variável para passar o argumento de sleep (), você deve inserir o cálculo diretamente em sleep ()


E o retorno do meu terminal

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 ───────────────────────

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.