Насколько точным является python time.sleep()?

Я могу дать ему числа с плавающей запятой, такие как

time.sleep(0.5)

но насколько это точно? Если я дам

time.sleep(0.05)

это действительно будет спать около 50 мс?

13 ответов

Решение

Точность функции time.sleep зависит от точности сна вашей базовой ОС. Для нереальных ОС, таких как обычная Windows, наименьший интервал, в течение которого вы можете спать, составляет около 10-13 мс. Я видел точные сны в течение нескольких миллисекунд того времени, когда они превышали минимум 10-13 мс.

Обновление: Как упомянуто в документах, цитируемых ниже, это обычно делает сон в цикле, который обязательно вернется ко сну, если он разбудит вас рано.

Я также должен отметить, что если вы работаете в Ubuntu, вы можете попробовать псевдо-ядро реального времени (с набором патчей RT_PREEMPT), установив пакет ядра rt (по крайней мере, в Ubuntu 10.04 LTS).

РЕДАКТИРОВАТЬ: Исправление ядра Linux не в реальном времени имеют минимальный интервал ожидания гораздо ближе к 1 мс, чем 10 мс, но он изменяется недетерминированным образом.

Люди совершенно правы относительно различий между операционными системами и ядрами, но я не вижу никакой детализации в Ubuntu, и я вижу гранулярность в 1 мс в MS7. Предлагая другую реализацию time.sleep, а не просто другую частоту тиков. Между прочим, более детальная проверка предполагает гранулярность в 1 мкс в Ubuntu, но это связано с функцией time.time, которую я использую для измерения точности.Linux и Windows типичное поведение time.sleep в Python

Вот мое продолжение ответа Уилберта: то же самое для Mac OS X Yosemite, так как он еще не упоминался.Спящее поведение Mac OS X Yosemite

Похоже, большую часть времени он спит примерно в 1,25 раза больше времени, которое вы запрашиваете, а иногда спит от 1 до 1,25 раза времени, которое вы запрашиваете. Он почти никогда (примерно вдвое из 1000 образцов) не спит более, чем в 1,25 раза больше времени, которое вы запрашиваете.

Также (не показано явно) отношение 1.25, кажется, держится довольно хорошо, пока вы не опускаетесь ниже примерно 0,2 мс, после чего оно начинает становиться немного размытым. Кроме того, кажется, что фактическое время устанавливается примерно на 5 мс дольше, чем вы запрашиваете, после того, как запрошенное время превышает 20 мс.

Опять же, похоже, это совершенно другая реализация sleep() в OS X, чем в Windows или какой-либо Linux Kernal Уилберт использовал.

Из документации:

С другой стороны, точность time() а также sleep() лучше, чем их Unix-эквиваленты: времена выражаются числами с плавающей запятой, time() возвращает максимально точное время (используя Unix gettimeofday где доступно), и sleep() примет время с ненулевой дробью (Unix select используется для реализации этого, где это возможно).

И, в частности, относительно WRT sleep():

Приостановить выполнение на указанное количество секунд. Аргумент может быть числом с плавающей запятой, чтобы указать более точное время сна. Фактическое время приостановки может быть меньше запрошенного, потому что любой пойманный сигнал прервет sleep() после выполнения процедуры отлова этого сигнала. Кроме того, время приостановки может быть дольше, чем запрошено на произвольную величину из-за планирования других действий в системе.

Если вам нужна более высокая точность или меньшее время сна, подумайте о создании своего собственного:

import time

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

Почему бы вам не узнать:

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

Для справки, я получаю около 0,1 мс ошибки на моем HTPC и 2 мс на моем ноутбуке, обе машины Linux.

В предстоящем выпуске Python (3.11) этот метод был сильно переработан. Теперь одинаковую точность можно ожидать как на платформе Windows, так и на платформе Unix, и по умолчанию всегда используется самая высокая точность. Вот соответствующая часть новой документации :

В Windows, если secs равно нулю, поток уступает оставшуюся часть своего кванта времени любому другому потоку, готовому к выполнению. Если других потоков, готовых к выполнению, нет, функция немедленно возвращается, и поток продолжает выполнение. В Windows 8.1 и более поздних версиях используется таймер высокого разрешения, обеспечивающий разрешение 100 наносекунд. Если secs равно нулю, используется Sleep(0).

Unix-реализация:

  • Используйте clock_nanosleep(), если доступно (разрешение: 1 наносекунда);
  • Или используйте nanosleep(), если доступно (разрешение: 1 наносекунда);
  • Или используйте select() (разрешение: 1 микросекунда).

Так что просто звоню time.sleepбудет работать на большинстве платформ, начиная с Python 3.11 , и это отличная новость! Было бы неплохо сделать кросс-платформенный тест этой новой реализации, аналогичный тому, что сделал @wilbert.

Небольшая поправка, некоторые люди отмечают, что сон может быть прекращен рано по сигналу. В 3.6 документах сказано:

Изменено в версии 3.5: теперь функция спит не менее секунды, даже если спящий режим прерывается сигналом, за исключением случаев, когда обработчик сигнала вызывает исключение (объяснение см. В PEP 475).

Высокоточный вариант time.sleep().

Логика: time.sleep() имеет низкую точность < 5 мс, поэтому с учетом временного окна (например, 1 секунда) оставшееся время разбивается на Оставшееся время / 2, сокращая время сна вдвое на каждой итерации. Когда оно достигает < 20 мс, он переходит к циклу while (нагружает процессор), а затем прерывается, когда остается < 0 мс.

      import time
def high_precision_sleep(duration):
    start_time = time.perf_counter()
    while True:
        elapsed_time = time.perf_counter() - start_time
        remaining_time = duration - elapsed_time
        if remaining_time <= 0:
            break
        if remaining_time > 0.02:  # Sleep for 5ms if remaining time is greater
            time.sleep(max(remaining_time/2, 0.0001))  # Sleep for the remaining time or minimum sleep interval
        else:
            pass

Тест на время:

      script_start_time = time.perf_counter()
time.sleep(1)
time_now = time.perf_counter()
elapsed_time = (time_now - script_start_time) * 1000
print("[%.6f] time.sleep" % elapsed_time)

script_start_time = time.perf_counter()
high_precision_sleep(1)
time_now = time.perf_counter()
elapsed_time = (time_now - script_start_time) * 1000
print("[%.6f] high_precision_sleep" % elapsed_time)

Полученные результаты:

      [1007.893800] time.sleep
[1000.004200] high_precision_sleep

Вы не можете ничего гарантировать в отношении sleep(), за исключением того, что он, по крайней мере, приложит максимум усилий, чтобы спать, пока вы говорите об этом (сигналы могут убить ваш сон до истечения времени, и намного больше вещей может заставить его работать) долго).

Конечно, минимальный уровень, который вы можете получить в стандартной операционной системе настольного компьютера, составит около 16 мс (детализация таймера плюс время для переключения контекста), но есть вероятность, что отклонение в% от предоставленного аргумента будет значительным, когда вы пытаетесь спать в течение 10 секунд миллисекунд.

Сигналы, другие потоки, удерживающие GIL, удовольствие от планирования ядра, пошаговое изменение скорости процессора и т. Д. - все это может повлиять на время, в течение которого ваш поток / процесс фактически спит.

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)

В Windows 7 / Python 3.8 вернулся 1000 для меня, даже если я установил значение сна на 0.0005

так что идеальный 1 мс

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

Не используйте переменную для передачи аргумента sleep (), вы должны вставить вычисление непосредственно в sleep ()


И возвращение моего терминала

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

Протестировал это недавно на Python 3.7 на Windows 10. Точность была около 1 мс.

Другие вопросы по тегам