天天看點

Windows下多媒體定時器對waitabletimer/sleep的精度影響

有篇寫的不錯遊戲主循環架構的文章,https://blog.csdn.net/u014038143/article/details/78192076,提到了如何在避免死等的情況下實作高精度定時,另外一個程式員基于寫了個測試程式https://blog.csdn.net/Gammy_Ye/article/details/90272904。這個程式每個循環都有輸出,沒法測試總體的偏差,注釋掉其中的cout輸出,統計一下6000次循環,每次等待0.01秒的總耗時。編譯運作

  • 總時間為60秒,期望在60秒左右完成,如果系統壓力不大,實際上在92秒左右(觀察Windows CPU,此期間CPU頻率會降低到2Ghz以下)
  • 打開視訊播放器,基本在62秒左右完成

是以看起來還是有其它因素影響這個精度。搜尋Windows API,看到多次提到mmtimer提供高精度計時,是以修改了一下代碼,核心是通過timeBeginPeriod設定系統計時精度,如果設定為1或者2毫秒,則測試程式總是可以在62秒左右完成,輸出如下

Total excu time for Loop 6000 times by 10ms=62684ms
Timer interval is 0.01
Use timer times is 6000
Total deviation is 26394715|2.63947
Average deviation is 0.000439912      

其實把setwaitabletimer換成sleep(),完成時間也差不多,是以看起來要得到高精度計時,必須通過timeBeginPeriod來設定系統timer精度,按微軟文檔,在Win10 2004前,這個設定甚至是全局的,即任何一個程式設定過,是以程式都會使用最小精度值,2004後則隻影響同樣調用過此函數的程序。

代碼如下:

#include <windows.h>
#include <iostream>

using namespace std;

#pragma comment(lib,"winmm.lib")

int main()
{

#define TARGET_RESOLUTION 1         // 1-millisecond target resolution

    TIMECAPS tc;
    UINT     wTimerRes;

    if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR)
    {
        // Error; application can\'t continue.
    }

    wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax);
    wTimerRes = 2;
    timeBeginPeriod(wTimerRes);


    HANDLE hTimer = CreateWaitableTimer(nullptr, true, nullptr);
    if (hTimer == nullptr)
    {
        return 1;
    }

    LARGE_INTEGER DueTime;
    DueTime.QuadPart = -10000000LL / 100; //定時的間隔為0.01秒
    int LoopTimes = 6000; //做6000次定時
    LARGE_INTEGER Counter_1, Counter_2, Counter_3, Counter_Dif, Frequency;
    Counter_Dif.QuadPart = 0;

    if (QueryPerformanceFrequency(&Frequency) == false)
    {
        return 1;
    }

    QueryPerformanceCounter(&Counter_1);

    for (int I = 0; I < LoopTimes; ++I)
    {
        SetWaitableTimer(hTimer, &DueTime, 0, nullptr, nullptr, 0);
        if (QueryPerformanceCounter(&Counter_2) == false)
        {
            return 1;
        }
        
        WaitForSingleObject(hTimer, INFINITE);
        //Sleep(10);

        if (QueryPerformanceCounter(&Counter_3) == false)
        {
            return 1;
        }
        LARGE_INTEGER Delta;
        Delta.QuadPart = abs(Counter_3.QuadPart - Counter_2.QuadPart + DueTime.QuadPart);//本次定時的誤內插補點(機關是硬體計數)
        Counter_Dif.QuadPart += Delta.QuadPart;

        //cout << Counter_3.QuadPart - Counter_2.QuadPart << "|" << Delta.QuadPart << "|"
        //    << (long double)Delta.QuadPart / Frequency.QuadPart << endl; //本次定時的誤差時間(機關為秒)
    }

    timeEndPeriod(wTimerRes);


    cout << "Total excu time for Loop 6000 times by 10ms=" << (Counter_3.QuadPart - Counter_1.QuadPart)*1000 / Frequency.QuadPart << "ms" << endl;

    cout << "Timer interval is " << (long double)-DueTime.QuadPart / 10000000 << endl;
    cout << "Use timer times is " << LoopTimes << endl;
    cout << "Total deviation is " << Counter_Dif.QuadPart << "|" << (long double)Counter_Dif.QuadPart / Frequency.QuadPart << endl;
    cout << "Average deviation is " << (long double)Counter_Dif.QuadPart / Frequency.QuadPart / LoopTimes << endl;//平均誤差時間
    system("pause");
}      
Windows下多媒體定時器對waitabletimer/sleep的精度影響