Windowsで経過時間をミリ秒単位で取得する最良の方法


8

2つのFILETIMEを使用して、それらをULONGLONGにキャストし、ULONGLONGを減算して、結果を10000で除算しようとしています。ビジュアルスタジオ2008エクスプレスエディション。これは私が使っているものです:

FILETIME filetime,filetime2;
GetSystemTimeAsFileTime(&filetime);
Sleep(100);
GetSystemTimeAsFileTime(&filetime2);
ULONGLONG time1,time2;
time1 = (((ULONGLONG) filetime.dwHighDateTime) << 32) + filetime.dwLowDateTime;
time2 = (((ULONGLONG) filetime2.dwHighDateTime) << 32) + filetime2.dwLowDateTime;
printf("ELAPSED TIME IN MS:%d",(int)((time2-time1)/10000));

回答:


11

QueryPerformanceCounterを使用します

long long milliseconds_now() {
    static LARGE_INTEGER s_frequency;
    static BOOL s_use_qpc = QueryPerformanceFrequency(&s_frequency);
    if (s_use_qpc) {
        LARGE_INTEGER now;
        QueryPerformanceCounter(&now);
        return (1000LL * now.QuadPart) / s_frequency.QuadPart;
    } else {
        return GetTickCount();
    }
}

// Somewhere else...
    // ...
    long long start = milliseconds_now();
    // ....
    long long elapsed = milliseconds_now() - start;

そしてそれは速く動くでしょうか?
XaitormanX 2012

フレーム中にダースを呼び出すのに十分な速さで、顕著な影響はありません。何回必要になると思いますか?

フレームの約4倍。しかし、そのコードをループして100回実行すると、実行に約10秒かかります。これは遅いのですが、なぜこれが起こっているのですか?
XaitormanX 2012

1
あなたが持っている Sleep(100)コードにあります。タイマーとは関係ありません。

2
それは1996年にp90で速く走った。今日は十分速く動くと思います。;)
Maximus Minimus 2012

7

一般に、timeGetTime()はゲームロジックのタイミングに最適です。GetTickCountの解像度はそれほど高くなく、QPCとRDTSCは、その目的に値するよりもはるかに問題が多くなります。

一方、プロファイリングの場合は、RDTSCまたはQPCのいずれかを使用する価値があります。マイクロソフトではQPCを推奨していますが、QPCよりもRDTSCを好みます。

私がwin32で使用する4つの一般的な時間関数:

GetTickCount()は、任意のゼロ(通常、システムの起動時間とは限りませんが)を基準とした現在の時刻をミリ秒単位で32ビット整数として返します(49日ごとにラップするため)。これは通常、時間を測定する最も速い方法です。残念ながら、それは低解像度です-通常、毎秒64回しか更新しません。一般的な噂に反して、timeBeginPeriod(1)を呼び出しても、テストしたシステムでは解像度が上がりません。ラッピングが心配な場合は、64ビットのバリアントもあります。

timeGetTime()は、任意のゼロを基準にした現在の時刻をミリ秒単位で32ビット値として返します(49日程度でラップされます)。GetTickCountほど高速ではありませんが、それでもかなり合理的です。最初はtimeBeginPeriod(1)を呼び出す必要があるかもしれませんが、それは1ミリ秒まで正確である場合があります。timeGetTimeを使用するには、winmm.libにリンクし、Mmsystem.hを含める必要があります。

QueryPerformanceCounter()は、現在の時刻を64ビット整数として任意の単位で返します。QueryPerformanceFrequency()を呼び出すことでユニットが何であるかを理解でき、ユニットは変更されません(再起動がない場合)。これの基礎となる実装は大きく異なり、各実装には独自の癖やバグがあり、一部のハードウェアで発生する可能性があるため、広く展開されているアプリケーションで使用するのは不愉快です。GetTickCountやtimeGetTimeほど高速ではないものもありますが、非常に低速な実装もあれば、妥当な実装もあります。通常、タイミング分解能は1マイクロ秒より優れていますが、必ずしも優れているとは限りません。

RDTSCは、x86 / x64アセンブリ言語のオペコードであり、CPUサイクルの単位で現在の時刻を返します(つまり、64ビット整数として。コンパイラによっては、組み込み(__rdstc)としてアクセスできます。他のコンパイラでは、インラインasmが必要です。その速度はCPUによって多少異なりますが、通常はかなり妥当です。通常、QueryPerformanceCounterより大幅に高速ですが、GetTickCountほど高速ではありません。残念なことに、QueryPerformanceCounterほど多くはありませんが、低解像度の時間関数よりもはるかに多くの癖があります。コア間の同期が不完全なためにコアを切り替えると、マルチコアCPUで逆方向に実行されることがあり、ユニットを理解するのが難しく、電源管理がCPUクロックのレートを変更するため、タイミングの途中でユニットが変更される場合があります。

これらの4つの方法はそれぞれ、他の3つの方法に比べてドリフトする可能性があることに注意してください。明確な信頼できる時間の流れはありません。


3

あなたの質問に答えるために:時間を測定するための「最良の」解決策はありません。すべての方法には、独自の長所/短所があります。それはすべてあなたのニーズに依存します。

ここでは、1つのソリューションを選択する前に、自問すべきいくつかの質問を示します。

  • タイマーの解像度。本当に正確なタイマー(<1ms)が必要ですか、それともそれほど正確ではないもの(例:10ms精度)が必要ですか?

  • CPUコスト。一部のメソッドは、他のメソッドよりも多くのCPUを必要とします。一般に、より正確なタイマーにはより多くのCPUが必要です。また、いくつかのメソッドを呼び出すと、他のアプリケーションに影響を与える可能性があります(例:timeBeginPeriod(1)win32を呼び出すと、OSにストレスがかかり、ラップトップのバッテリー寿命が短くなります)。

  • SMPで動作します。一部のメソッドは特定のCPUデータ(例:経過サイクル数)を使用し、マルチプロセッサ環境で問題が発生する可能性があります。

  • 省電力機能のあるCPUで動作します。一部のCPUは、時間の経過とともに周波数を変更する機能を備えているため、一部のタイミング方式に問題を引き起こす可能性があります。

あなたが与える例では、私はの組み合わせを使用します QueryPerformanceCounter() / QueryPerformanceFrequency()ます。

また、指定した例では、sleep(100)実際には100ms以上待機する可能性があることに注意してください。したがって、非常に正確なタイマーを使用しても、100ms以外の結果が返される可能性があります。


SMPで一部のタイミング方式に問題があることは事実ですが、これらは常にHAL、BIOS、またはその他のドライバー関連の問題であり、そのレベルで修正する必要あります。別のタイミング方法に切り替えることで問題を解決できることはまれです。たとえば、ドライバーが悪いためにQPCが逆方向に実行されるのを見ましたが、GetTickCountもそうでした。これが実行されなかった唯一の理由は、ジャンプが〜50µsecであったため、まったく気付かなかったためです。せいぜい、アプリmaxは以前に返された値で呼び出しを実行できます。

現状では、QPCおよびPOSIX clock_gettimeなどの他のOSの同等の機能はすべて、SMPシステムでも逆方向に実行されないように指定されており、現在のCPU周波数とは無関係です。したがって、QPCから最初の望みどおりに機能させることができれば、他のタイミング関数でも同じ問題が発生する可能性があるため、他のタイミング関数では同じ問題が発生する可能性が高いため、人々からそれらを遠ざけてタイミング関数を悪化させることはお勧めできません。そうそう、そこにある QPC、にclock_gettime、およびmach_absolute_time:最善の解決策は。ゲームのループタイミングについては、それが利用可能であれば他のものを使用する理由はありません。

0

私はいくつかのベンチマークを実行しただけで、timeGetTimeを使用する正当な理由はほとんどありません。QueryPerformanceCounterはわずかに高速で、はるかに正確です。

次に、GetTickCountがあります。これはほぼ瞬時に行われます。これは、プロセスのスケジューリング中にOSが変更する値を読み取るだけだからです。値は一度に15ミリ秒以上同じままであり、非常に粗いタイミング以外には推奨されません。


-1

GetTickCount()関数を見てください。その戻り値は、システムが起動してから経過したミリ秒数です。

unsigned int Last = 0;
unsigned int Now = 0;
Last = GetTickCount();
//...Run your functions..
Now = GetTickCount();
printf("Elapsed Time in Milliseconds: %i",(Now-Last));

6
-1。GetTickCount()の解像度は低くなっています。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.