Как повысить точность сна / паузы в Python? - PullRequest
0 голосов
/ 04 марта 2019

Я провел эксперимент, чтобы сравнить точность хронирования сна / паузы в Python и C ++

Краткое содержание эксперимента:

В цикле из 1000000 итераций по 1 микросекунде в каждой итерации.

Ожидаемая продолжительность: 1,000000 секунд (для 100% точной программы)

В питоне:

import pause
import datetime

start = time.time()
dt = datetime.datetime.now()
for i in range(1000000):
    dt += datetime.timedelta(microseconds=1)
    pause.until(dt)
end = time.time()
print(end - start)

Ожидаемый: 1,000000 секунд, Фактический (приблизительный): 2.603796

В C ++:

#include <iostream>
#include <chrono>
#include <thread>

using namespace std;

using usec = std::chrono::microseconds;
using datetime = chrono::_V2::steady_clock::time_point;
using clk = chrono::_V2::steady_clock;

int main()
{
    datetime dt;
    usec timedelta = static_cast<usec>(1);

    dt = clk::now();

    const auto start = dt;

    for(int i=0; i < 1000000; ++i) {
        dt += timedelta;
        this_thread::sleep_until(dt);
    }

    const auto end = clk::now();

    chrono::duration<double> elapsed_seconds = end - start;

    cout << elapsed_seconds.count();

    return 0;
}

Ожидаемый: 1,000000 с, Фактический (приблизительный): 1.000040

Очевидно, что C ++ гораздо точнее, но я разрабатываю проект на python и мне нужно повысить точность.Есть идеи?

PS Это нормально, если вы предложите другую библиотеку / технику Python, если она более точна:)

Ответы [ 4 ]

0 голосов
/ 04 марта 2019

Проблема не только в том, что таймер сна python является неточным, но и в том, что каждая часть цикла требует некоторого времени.

Ваш исходный код имеет время выполнения ~ 1.9528656005859375 в моей системе.

Если я только запускаю эту часть вашего кода без сна:

for i in range(100000):
   dt += datetime.timedelta(microseconds=1)

Тогда требуемое время для этого цикла уже ~ 0,45999741554260254.

Если я только запускаю

for i in range(1000000):
   pause.milliseconds(0)

Тогда время выполнения кода ~ 0,5583224296569824.

Использование всегда одной и той же даты:

dt = datetime.datetime.now()
for i in range(1000000):
    pause.until(dt)

Результат выполнения ~ 1,326077938079834

Если вы сделаете то же самое с отметкой времени:

dt = datetime.datetime.now()
ts = dt.timestamp()
for i in range(1000000):
    pause.until(ts)

Тогда время выполнения изменится на ~ 0.36722803115844727

И если вы увеличите отметку времени на одну микросекунду:

dt = datetime.datetime.now()
ts = dt.timestamp()
for i in range(1000000):
    ts += 0.000001
    pause.until(ts)

Тогда вы получите время выполнения ~ 0,9536933898925781

То, что оно меньше 1, связано с неточностями с плавающей запятой, добавив print(ts-dt.timestamp()) после того, как цикл покажет ~ 0,95367431640625, так что продолжительность самой паузыправильно, бно ts += 0.000001 накапливает ошибку.

Вы получите лучший результат, если посчитаете количество итераций и добавите iterationCount/1000000 к времени начала:

dt = datetime.datetime.now()
ts = dt.timestamp()
for i in range(1000000):
    pause.until(ts+i/1000000)

И этоприведет к ~ 1.000023365020752

Так что в моем случае pause сам по себе уже позволит точность менее 1 микросекунды.Проблема на самом деле в части datetime, которая требуется как для datetime.timedelta, так и для sleep_until.

. Поэтому, если вы хотите иметь точность в микросекундах, вам нужно искать библиотеку времени, которая работает лучше, чемdatetime.

0 голосов
/ 04 марта 2019

Библиотека пауз говорит, что

Точность должна быть в пределах 0,001 секунды, однако это будет зависеть от того, насколько> точен ваш системный сон и других факторов производительности.

Если вы умножите 0,001 на 1000000, вы получите большую накопленную ошибку.

Пара вопросов:

Зачем вам спать?

Какова минимальная требуемая точность?

Насколько согласованы выполняемые вами операции?Если эти вызовы функций различаются более чем на 0,001, то накопленная ошибка будет больше из-за выполняемых вами операций, чем может быть отнесено к паузах / снам.

0 голосов
/ 04 марта 2019

Спящий поток по своей сути недетерминирован - вы не можете говорить о «точности» на самом деле для спящего потока в целом - возможно, только в контексте конкретной системы и платформы - слишком много факторовкоторые, возможно, могут играть роль, например, сколько ядер процессора и т. д.

Чтобы проиллюстрировать эту мысль, мысленный эксперимент :

Предположим, вы сделали много потоков (не менее 1000) и по расписанию их запускать в одно и то же время.Какую «точность» вы тогда ожидаете?

0 голосов
/ 04 марта 2019
import pause
import datetime
import time

start = time.time()
dt = datetime.datetime.now()

for i in range(1000000):
    dt += datetime.timedelta(microseconds=1)
    pause.until(1) 
end = time.time()
print(end - start)

ВЫХОД:

1.0014092922210693
...