?

Log in

No account? Create an account
nyaload

Журнал Пушыстого

Журнал Пушыстого

Previous Entry Share Flag Next Entry
Повышение точности Sleep в Windows
nyaload
_winnie
Sleep можно сделать точней. В данном случае timeBeginPeriod(1) довёл его точность с +- 10% при паузе на 100 мс до +- 10% при паузе на 10 мс. Осторожно, это недокументированное поведение timeBeginPeriod, и оно глобальное на все программы в винде (то есть, другое приложение может его нам испортить). Так же после timeBeginPeriod() начинает точней работать timeGetTime и GetTickCount(). Работает ли под Вистой - не проверял. Есть ли там такая же проблема с глобальным состоянием timeBeginPeriod - не знаю. Ох и устроили же разработчики Windows/процессоров кучу тайного знания по тому, как измерять небольшие кусочки времени в играх или как сделать что бы не грузить процессор на 100% при FPS, когда мы нарочно его ограничиваем числом 60-100.

Кстати, timeGetTime(после повышения точности) - это одно из средств избежать глюков QueryPerformanceCounter на многоядерных AMD-процессорах на непатченной специально винде.



#include <windows.h>
#include <stdio.h>

unsigned __int64 g_qpc_freq;
unsigned __int64 g_qpc_start;

void GetTimeInitQPC()
{
    LARGE_INTEGER freq;
    QueryPerformanceFrequency(&freq);
    g_qpc_freq = freq.QuadPart;


    LARGE_INTEGER counter;
    QueryPerformanceCounter(&counter);
    g_qpc_start = counter.QuadPart;
}

double GetTimeQPC()
{
    LARGE_INTEGER counter;
    QueryPerformanceCounter(&counter);
    return double(counter.QuadPart - g_qpc_start) / double(g_qpc_freq);
}

void TestSleep()
{
    for (int i = 0; i < 30; ++i)
    {
        DWORD nominal_sleep_time_ms = 10;
        double start_sleep = GetTimeQPC();
        Sleep(nominal_sleep_time_ms);
        double end_sleep = GetTimeQPC();

        double error = (nominal_sleep_time_ms * 0.001) / (end_sleep - start_sleep);
        if (error < 1)
            error = 1.0 / error;
        printf("%.3f\n", error);
    }
}

int main()
{
    GetTimeInitQPC();
    
    TestSleep();
    timeBeginPeriod(1);
    printf("after timeBeginPeriod(1):\n");
    TestSleep();
}
_Winnie C++ Colorizer




  • 1
а timeEndPeriod() кто вызывать будет? пушкин?

Хз. Как и открытые хендлы файлов или выделенная память освобождается при выходе из процесса, так и timePeriod восстанавливается. Но в отличие от памяти, время утекать не может, поэтому если устанавливаем на время работы всего приложения - то наверное можно timeEndPeriod забить. ИМХО.

> время утекать не может
ха! еще как может. у нас в коде не хватало вызова timeBeginPeriod(). так на некоторых тачках время вперед бежало прям на глазах.

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

Call this function immediately before using timer services, and call the timeEndPeriod function immediately after you are finished using the timer services.

You must match each call to timeBeginPeriod with a call to timeEndPeriod, specifying the same minimum resolution in both calls. An application can make multiple timeBeginPeriod calls as long as each call is matched with a call to timeEndPeriod.

>ха! еще как может. у нас в коде не хватало вызова timeBeginPeriod(). так на некоторых тачках время вперед бежало прям на глазах.
Наверное, timeBeginPeriod -> timeEndPeriod?..

То есть, при старте приложения один раз вызывался timeBeginPeriod, и из-за того что не вызывали в конце время начинало бежать вперёд? До или после окончания приложения? Если до, то как бы вызванный в конце timeEndPeriod не поможет ничему, что происходит до него. Если после, то это что-то странное, что только после, и что вообще влияет на поведение после выхода из процесса.

Или уже у вас тыщу раз вызывался timeBeginPeriod и после этого всё начинало странно себя вести? В таком случае это вряд ли из-за забытого вызова timeEndPeriod,

Если устанавливать на каждом timeBeginPeriod() сразу перед вызовом timeGetTime(), и сразу восстанавливать при помощи timeEndPeriod, то боюсь это убъёт все преимущества изменения точности и даст некорректные результаты, если делать это по сто раз в секунду.


>Если устанавливать на каждом timeBeginPeriod()
Если устанавливать на каждом кадре timeBeginPeriod()

если я правильно помню, именно timeBeginPeriod.

хм.
у меня не восстанавливался.
винда была 2000

Ну, могу ещё добавить что Sleep это не единственный вариант. Можно к примеру так сделать:

timer_event = CreateEvent(0,0,0,0);
timer_id = timeSetEvent(fps_timeout, 0, (LPTIMECALLBACK)timer_event, 0, TIME_PERIODIC|TIME_CALLBACK_EVENT_PULSE);

...

for (;;)
  {
    ...
    WaitForSingleObject(timer_event,1000);
    ...
  }


К тому же есть ещё одна замечательная функция - SwitchToThread


у WaitForSingleObject накладные расходы неслабые. не говоря о том, что это не целевое использование.

Хм, нецелевое для чего, для ожидания синхронизации n раз секунду? ИМХО, самое что ни наесть целевое.

Кстати насчёт накладных расходов, в чём же они существенно больше ухода Sleep'а в ядро? Вобщем-то KeDelayExecution и KeSigleThreadWait достаточно слабо различаются. Формально у слипа причина ожидания своя =) Для планировщика же вообще никакой разницы нет, он работает только с готовыми для исполнения потоками, а в состояние готовности поток переводится по списку ожидания на мониторе блокировки. Соответственно в состояние 'готов' поток в слипе переводит внутренний таймер, а поток ждущий монитор переводится освобождением монитора. Вот и вся разница.


слона-то я и не приметил. в ночи показалось, что WaitForSingleObject() ждет невзведенный timer_event и отваливается по таймауту.

Вот статья в тему: http://www.dtf.ru/articles/read.php?id=39888

Quote: "Если прочитать описание функции timeBeginPeriod() в MSDN, то ни за что не догадаться, что на самом деле она изменяет quantum."

Вобщем, самое оно.

(Deleted comment)
Ммм?.. Глюки мат-платы или кванты времени потоков никуда не делись, какую оболочку не выбери.
Ну и boost - это такая небольшая платформа, так что никуда ты не ушёл, точнее ушёл на другую :)

(Deleted comment)
> твязка от аппаратно-осевой части через интерфейс это гут
Зависит =) Очень сильно зависит. =)) Причём как бенифиты собственно наличия отвязки так и сама линия раздела, а она может быть проведена очень поразному.

(Deleted comment)
Ну дык, кто же спорит =) Только она не может быть проведена одинаково хорошо для всех возможных задач. Где-то получиться лучше, где-то хуже. Вопрос, для чего заточен бюст? =)

Дык, тут вроде про игры приложения реального времени писали

(Deleted comment)
Бюст однако и мешает =) Ты знаешь, к примеру, сколько он на каждую конструкцию делает аллокаций памяти? А они синхронные? А сколько на это _точно_ уйдёт времени? А в каком компиляторе? И в какой версии? И на какой _платформе_ (процессор, память, переферия)? Это не для спора, а так, просто мысля в слух.

Но если честно, то неравномерность в 1с тоже ведь РТ, вопрос лишь в определении критических временных ограничений. =)))

(Deleted comment)
Калибровку неравномерности? Это как?

Или ты будешь менять алгоритмы бюста и пользуемого им STL для гаранитрованного времени отклика? Они же написаны оптимально-для-лучшего/среднего, а РТ алгоритмы пишутся гарантированно для худшего. Отсюда и более низкий перформанс РТ систем.

А можно поподробнее о "глюков QueryPerformanceCounter на многоядерных AMD-процессорах на непатченной специально винде"?

http://developer.amd.com/assets/TSC_Dual-Core_Utility.pdf (в конце - рекомендация установить патч, если QPC не работает).

http://users.livejournal.com/_winnie/84036.html и ссылка оттуда на http://channel9.msdn.com/ShowPost.aspx?PostID=156175 и

И ещё - http://www.rsdn.ru/Forum/?mid=618808 , но вроде это глюк материнской платы, многоядерных процессоров от AMD тогда ещё не было в продаже для домашних компов.

Спасибо за информацию. Как сложно оказывается узнать точное время )

  • 1