Как получить точный тик таймера 1 мс под WinXP

Я пытаюсь вызывать функцию каждые 1 мс. Проблема в том, что мне нравится делать это с Windows. Поэтому я попробовал мультимедийный API.

Мультимедийный API

Источник

idTimer = timeSetEvent( 
     1, 
     0,
     TimerProc, 
     0, 
     TIME_PERIODIC|TIME_CALLBACK_FUNCTION ); 

Мой результат состоял в том, что большую часть времени 1 мс была в порядке, но иногда я получаю двойной период. Посмотрите на небольшой выступ на мультимедийной гистограмме в 1,95 мс http://www.freeimagehosting.net/uploads/8b78f2fa6d.png

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

API очереди по таймеру

Моя следующая попытка была с использованием API таймеров очереди с

hTimerQueue = CreateTimerQueue();
if(hTimerQueue == NULL)
{
printf("Error creating queue: 0x%x\n", GetLastError());
}

BOOL res = CreateTimerQueueTimer(
&hTimer, 
hTimerQueue, 
TimerProc, 
NULL, 
0, 
1,  // 1ms
    WT_EXECUTEDEFAULT);

Но и результат оказался не таким, как ожидалось. Теперь я получаю большую часть времени за 2 мс. http://www.freeimagehosting.net/uploads/2a46259a15.png

измерение

Для измерения времени я использовал метод QueryPerformanceCounter и QueryPerformanceFrequency.

Вопрос

Итак, теперь мой вопрос, если кто-то сталкивался с подобными проблемами под Windows и, возможно, даже нашел решение?

Благодарю.

3 ответа

Решение

Не переходя на ОС реального времени, вы не можете ожидать, что ваша функция будет вызываться каждые 1 мс.

В Windows, которая НЕ является ОС реального времени (для Linux она похожа), программа, которая многократно считывает текущее время с точностью до микросекунды и сохраняет последовательные различия в гистограмме, имеет непустую ячейку в течение>10 мс! Это означает, что иногда у вас будет 2 мс, но вы также можете получить больше между вашими вызовами.

Вызов NtQueryTimerResolution() вернет значение для A ctualResolution. В вашем случае реальное разрешение почти наверняка составляет 0,9765625 мс. Это именно то, что вы показываете в первом сюжете. Точность второго порядка 1,95 мс Sleep(1) = 1,9531 мс = 2 х 0,9765625 мс

Я полагаю, что период прерывания составляет около 1 мс (0,9765625).

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

Скажем, A ctualResolution установлено на 0,9765625, сердцебиение прерывания системы будет выполняться с периодами 0,9765625 мс или 1024 Гц и вызовом Sleep производится с желаемой задержкой 1 мс. Необходимо рассмотреть два сценария:

  1. Вызов был сделан < 1 мс (ΔT) перед следующим прерыванием. Следующее прерывание не подтвердит, что желаемый период времени истек. Только следующее прерывание вызовет возврат вызова. Результирующая задержка сна составит ΔT + 0,9765625 мс.
  2. Вызов был сделан> = 1 мс (ΔT) перед следующим прерыванием. Следующее прерывание заставит звонок вернуться. Результирующая задержка сна будет ΔT.

Таким образом, результат во многом зависит от того, когда был сделан вызов, и поэтому вы можете наблюдать события 0,98 мс, а также события 1,95 мс.

Изменить: используя CreateTimerQueueTimer увеличит наблюдаемую задержку до 1,95, поскольку тик таймера (период прерывания) равен 0,9765625 мс. При первом появлении прерывания запрошенная длительность в 1 мс не истекла, поэтому TimerProc сработает только после второго прерывания (2 x 0,9765625 мс = 1,953125 мс> 1 мс). Следовательно, график queueTimer показывает пик в 1,953125 мс.

Примечание. Такое поведение сильно зависит от используемого оборудования.

Более подробную информацию можно найти в Windows Timestamp Project.

Вы можете попробовать запустить timeBeginPeriod(1) при запуске программы и timeEndPeriod(1) прежде чем выйти. Это, вероятно, может повысить точность таймера.

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