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