web-dev-qa-db-ja.com

QueryPerformanceCounterの使用方法

私は最近、Timerクラスでミリ秒からマイクロ秒に変更する必要があると判断しました。いくつかの調査の後、QueryPerformanceCounterがおそらく最も安全な方法であると判断しました。 (Boost::Posixでの警告は、Win32 APIで動作しない可能性があるということで、私は少し先送りされました)。しかし、私はそれを実装する方法が本当によくわかりません。

私がやっていることは、使用しているGetTicks() esque関数を呼び出して、それをTimerのstartingTicks変数に割り当てることです。次に、経過した時間を見つけるために、関数の戻り値をstartingTicksから減算し、タイマーをリセットするときに関数を再度呼び出して、startingTicksを割り当てます。残念ながら、私が見たコードから、それはQueryPerformanceCounter()を呼び出すほど単純ではなく、引数として何を渡すべきかわかりません。

92
Anonymous
#include <windows.h>

double PCFreq = 0.0;
__int64 CounterStart = 0;

void StartCounter()
{
    LARGE_INTEGER li;
    if(!QueryPerformanceFrequency(&li))
    cout << "QueryPerformanceFrequency failed!\n";

    PCFreq = double(li.QuadPart)/1000.0;

    QueryPerformanceCounter(&li);
    CounterStart = li.QuadPart;
}
double GetCounter()
{
    LARGE_INTEGER li;
    QueryPerformanceCounter(&li);
    return double(li.QuadPart-CounterStart)/PCFreq;
}

int main()
{
    StartCounter();
    Sleep(1000);
    cout << GetCounter() <<"\n";
    return 0;
}

このプログラムは1000に近い数値を出力する必要があります(windows sleepはそれほど正確ではありませんが、999のようになります)。

StartCounter()関数は、CounterStart変数にパフォーマンスカウンターのティック数を記録します。 GetCounter()関数は、StartCounter()が最後にdoubleとして呼び出されてからのミリ秒数を返します。したがって、GetCounter()が0.001を返す場合、StartCounter()が呼び出されてから約1マイクロ秒になります。

タイマーに代わりに秒を使用させたい場合は、変更します

PCFreq = double(li.QuadPart)/1000.0;

PCFreq = double(li.QuadPart);

または、マイクロ秒が必要な場合は、

PCFreq = double(li.QuadPart)/1000000.0;

しかし、実際には、doubleを返すため、利便性が重要です。

153
Ramónster

私はこれらの定義を使用します:

/** Use to init the clock */
#define TIMER_INIT \
    LARGE_INTEGER frequency; \
    LARGE_INTEGER t1,t2; \
    double elapsedTime; \
    QueryPerformanceFrequency(&frequency);


/** Use to start the performance timer */
#define TIMER_START QueryPerformanceCounter(&t1);

/** Use to stop the performance timer and output the result to the standard stream. Less verbose than \c TIMER_STOP_VERBOSE */
#define TIMER_STOP \
    QueryPerformanceCounter(&t2); \
    elapsedTime=(float)(t2.QuadPart-t1.QuadPart)/frequency.QuadPart; \
    std::wcout<<elapsedTime<<L" sec"<<endl;

使用法(再定義を防ぐための括弧):

TIMER_INIT

{
   TIMER_START
   Sleep(1000);
   TIMER_STOP
}

{
   TIMER_START
   Sleep(1234);
   TIMER_STOP
}

使用例からの出力:

1.00003 sec
1.23407 sec
17
user152949

あなたがWindowsにいると仮定すると(そうであればあなたの質問にタグを付ける必要があります!)、 このMSDNページ で、必要なシステムコールをラップするシンプルで便利なHRTimer C++クラスのソースを見つけることができます必要なものに非常に近いものを実行します(特にGetTicks()メソッドを追加すると、特にexactly必要なものを追加できます)。

Windows以外のプラットフォームでは、QueryPerformanceCounter関数がないため、ソリューションは直接移植できません。ただし、上記のHRTimerなどのクラスでラップすると、現在のプラットフォームが実際に提供できるものを使用するようにクラスの実装を変更するのが簡単になります(おそらくBoost経由など)。

2
Alex Martelli

時間を取得する際のNDISドライバーの例を使用して、この質問を拡張します。ご存じのように、KQueryQueryTime(NdisGetCurrentSystemTimeに似ています)のミリ秒以上の解像度は低く、タイムスタンプが必要なネットワークパケットや他のIRPなどのプロセスがあります。

例は簡単です:

LONG_INTEGER data, frequency;
LONGLONG diff;
data = KeQueryPerformanceCounter((LARGE_INTEGER *)&frequency)
diff = data.QuadPart / (Frequency.QuadPart/$divisor)

除数は、必要な解像度に応じて10 ^ 3または10 ^ 6です。

1
kagali-san