Таймеры Win32 API - PullRequest
       31

Таймеры Win32 API

2 голосов
/ 16 февраля 2010

Я использовал системный таймер (функция clock (), см. Time.h) для определения времени некоторых последовательных и USB-коммуникаций. Все, что мне было нужно, это точность около 1 мс. Первое, что я заметил, это то, что отдельные времена могут быть (плюс или минус) 10 мс. Сроки ряда меньших событий привели к менее точным срокам по мере прохождения событий. Совокупные сроки были немного лучше. После небольшого рута в MSDN и т. Д. Я наткнулся на таймер в мультимедийной библиотеке Windows (timeGetTime (), см. MMSystem.h). Это было намного лучше с приличной точностью до уровня 1 мс.

Затем возникла странность, после первоначальной безупречной работы в течение нескольких дней (прекрасные журналы с полезными таймингами) все стало грушевидным, так как этот API также начал показывать эту странную гранулярность (вместо набора небольших сообщений от 3 до 2 мс, 3 мс, 2 мс , 3 мс и т. Д. Получилось как 0 мс, 0 мс, 0 мс, 0 мс, 15 мс и т. Д. Перезагрузка ПК восстановила неточность, но через некоторое неопределенное время (примерно через час) аномалия вернулась.

Кто-нибудь получил какие-либо идеи или предложения о том, как получить такой уровень точности синхронизации в Windows XP (32-разрядная версия Pro, с использованием Visual Studio 2008).

Мой маленький класс времени:

class TMMTimer
{
public:
    TMMTimer( unsigned long msec);
    TMMTimer();

    void Clear() { is_set = false; }
    void Set( unsigned long msec=0);

    bool Expired();
    unsigned long Elapsed();

private:
    unsigned long when;
    int roll_over;
    bool is_set;
};



/** Main constructor.
 */
TMMTimer::TMMTimer()
{
    is_set = false;
}




/** Main constructor.
 */
TMMTimer::TMMTimer( unsigned long msec)
{
    Set( msec);
}




/** Set the timer.
 *
 * @note   This sets the timer to some point in the future.
 *         Because the timer can wrap around the function sets a
 *         rollover flag for this condition which is checked by the
 *         Expired member function.
 */
void TMMTimer::Set( unsigned long msec /*=0*/)
{
    unsigned long now = timeGetTime();       // System millisecond counter.

    when = now + msec;

    if (when < now)
        roll_over = 1;
    else
        roll_over = 0;

    is_set = true;
}




/** Check if timer expired.
 *
 * @return  Returns true if expired, else false.
 *
 * @note    Also returns true if timer was never set. Note that this
 *          function can handle the situation when the system timer
 *          rolls over (approx every 47.9 days).
 */
bool TMMTimer::Expired()
{
    if (!is_set)
        return true;

    unsigned long now = timeGetTime();       // System millisecond counter.

    if (now > when)
    {
        if (!roll_over)
        {
            is_set = false;
            return true;
        }
    }
    else
    {
        if (roll_over)
            roll_over = false;
    }

    return false;
}




/** Returns time elapsed since timer expired.
 *
 * @return  Time in milliseconds, 0 if timer was never set.
 */
unsigned long TMMTimer::Elapsed()
{
    if (!is_set)
        return 0;

    return timeGetTime()-when;
}

Ответы [ 3 ]

9 голосов
/ 16 февраля 2010

Вы звонили timeBeginPeriod(1);, чтобы установить разрешение мультимедиа на 1 миллисекунду? Разрешение мультимедийного таймера является системно-глобальным, поэтому, если вы не установили его самостоятельно, скорее всего, вы начали после того, как что-то еще вызвало его, затем, когда это что-то еще вызвало timeEndPeriod(), разрешение вернулось к системному значению по умолчанию ( что обычно составляет 10 мс, если память служит).

Другие посоветовали использовать QueryPerformanceCounter(). Это имеет гораздо более высокое разрешение, но вы все равно должны быть осторожны. В зависимости от используемого ядра он может / будет использовать функцию x86 RDTSC, которая является 64-битным счетчиком циклов команд. Хорошо это или плохо, но на процессоре, частота которого варьируется (который запускался на ноутбуках, но теперь распространен почти везде), соотношение между количеством часов и временем на стенке меняется вместе с тактовой частотой. Если память служит, если вы заставляете Windows устанавливать с предположением, что имеется несколько физических процессоров (не только несколько ядер), вы получите ядро, в котором QueryPerformanceCounter() будет вместо этого считывать тактовую частоту 1,024 МГц материнской платы. Это уменьшает разрешение по сравнению с тактовой частотой процессора, но, по крайней мере, скорость постоянна (и если вам нужно только разрешение в 1 мс, оно должно быть более чем достаточно).

4 голосов
/ 16 февраля 2010

Если вы хотите синхронизацию с высоким разрешением в Windows, вам следует рассмотреть возможность использования QueryPerformanceFrequency и QueryPerformanceCounter .

Они обеспечат наиболее точную синхронизацию, доступную в Windows, и должны быть намного более «стабильными» с течением времени. QPF дает вам количество импульсов в секунду, а QPC дает вам текущий счет. Вы можете использовать это для выполнения таймингов с очень высоким разрешением в большинстве систем (доли мс).

1 голос
/ 16 февраля 2010

Проверьте таймеры высокого разрешения из Win32 API.

http://msdn.microsoft.com/en-us/library/ms644904(VS.85).aspx

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

...