Der beste Weg, um die verstrichene Zeit in Millisekunden in Fenstern zu ermitteln

8

Ich versuche es mit zwei FILETIMEs zu machen, sie in ULONGLONGs umzuwandeln, die ULONGLONGs zu subtrahieren und das Ergebnis durch 10000 zu dividieren. Aber es ist ziemlich langsam und ich möchte wissen, ob es einen besseren Weg gibt, dies zu tun. Ich verwende c ++ mit Visual Studio 2008 Express Edition. Das benutze ich:

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));
XaitormanX
quelle

Antworten:

11

Verwenden Sie 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;

quelle
Und würde das schnell gehen?
XaitormanX
Schnell genug, um ein Dutzend Mal während eines Frames ohne spürbare Auswirkungen aufzurufen. Wie oft erwarten Sie, dass Sie es brauchen?
etwa 4 mal pro Frame. Aber wenn ich diesen Code 100 Mal in eine Schleife stecke, dauert die Ausführung ungefähr 10 Sekunden, was langsam ist. Warum passiert das?
XaitormanX
1
Sie haben eine Sleep(100)in Ihrem Code. Es hat nichts mit den Timern zu tun.
2
Es lief schnell auf einem p90 im Jahr 1996; Ich denke, es wird heute schnell genug laufen. ;)
Maximus Minimus
7

Im Allgemeinen eignet sich timeGetTime () am besten für die Timing-Spielelogik - GetTickCount ist nicht hoch genug aufgelöst, und QPC & RDTSC sind viel schwieriger, als sie für diesen Zweck wert sind.

Für die Profilerstellung hingegen kann sich entweder RDTSC oder QPC durchaus lohnen. Ich bevorzuge RDTSC gegenüber QPC, obwohl Microsoft QPC empfiehlt.

Die vier gängigen Zeitfunktionen, die ich unter win32 verwende:

GetTickCount () gibt die aktuelle Zeit in Millisekunden relativ zu einer beliebigen Null (normalerweise, wenn auch nicht immer, die Systemstartzeit) als 32-Bit-Ganzzahl zurück (wird also etwa alle 49 Tage umgebrochen). Dies ist normalerweise die schnellste Methode, um eine Zeit zu messen. Leider ist die Auflösung niedrig - normalerweise wird sie nur 64 Mal pro Sekunde aktualisiert. Im Gegensatz zu populären Gerüchten wird das Aufrufen von timeBeginPeriod (1) die Auflösung auf keinem System erhöhen, auf dem ich das getestet habe. Es gibt auch eine 64-Bit-Variante, wenn Sie sich Sorgen über das Umschließen machen.

timeGetTime () gibt die aktuelle Zeit in Millisekunden relativ zu einer beliebigen Null als 32-Bit-Wert zurück (wird also nach etwa 49 Tagen umgebrochen). Es ist nicht so schnell wie GetTickCount, aber immer noch ziemlich vernünftig. Es kann auf eine Millisekunde genau sein, obwohl dazu möglicherweise zuerst timeBeginPeriod (1) aufgerufen werden muss. Die Verwendung von timeGetTime erfordert eine Verknüpfung mit winmm.lib und einschließlich Mmsystem.h.

QueryPerformanceCounter () gibt die aktuelle Zeit in beliebigen Einheiten als 64-Bit-Ganzzahl zurück. Sie können herausfinden, um welche Einheiten es sich handelt, indem Sie QueryPerformanceFrequency () aufrufen. Die Einheiten ändern sich nicht (ohne Neustart). Die zugrunde liegende Implementierung ist sehr unterschiedlich, und jede Implementierung weist ihre eigenen Macken und Fehler auf, die auf einigen Hardwarekomponenten auftreten können, was die Verwendung in weit verbreiteten Anwendungen unangenehm macht. Einige Implementierungen sind sehr langsam, andere sind sinnvoll, obwohl keine so schnell ist wie GetTickCount oder sogar timeGetTime. Normalerweise ist die Timing-Auflösung besser als 1 Mikrosekunde, wenn auch nicht unbedingt viel besser.

RDTSC ist ein Opcode in x86 / x64-Assemblersprache, der die aktuelle Zeit in Einheiten von CPU-Zyklen zurückgibt (dh als 64-Bit-Ganzzahl. Auf einigen Compilern kann als intrinsische (__rdstc) zugegriffen werden, auf anderen benötigen Sie Inline-ASM. Die Geschwindigkeit variiert ein wenig zwischen den CPUs, ist aber normalerweise ziemlich vernünftig - normalerweise deutlich schneller als QueryPerformanceCounter, aber nicht so schnell wie GetTickCount. Leider hat es einige Macken - nicht so viele wie QueryPerformanceCounter, aber immer noch viel mehr als die Funktionen mit niedrigerer Auflösungszeit. Auf Multicore-CPUs kann es manchmal rückwärts laufen, wenn Sie aufgrund einer unvollständigen Synchronisation zwischen den Kernen die Kerne wechseln, die Einheiten schwer herauszufinden sind und sich die Einheiten während des Timings ändern können, da die Energieverwaltung die Rate des CPU-Takts ändert.

Beachten Sie, dass jede dieser vier Methoden relativ zu den anderen drei Methoden abweichen kann - es gibt keinen eindeutigen maßgeblichen Zeitfluss.

user3535668
quelle
3

Um Ihre Frage zu beantworten: Es gibt keine "beste" Lösung, um die Zeit zu messen. Jede Methode hat ihre eigenen Vor- und Nachteile. Es hängt alles von Ihren Bedürfnissen ab.

Hier sind einige Fragen, die Sie sich stellen sollten, bevor Sie eine Lösung auswählen:

  • Timer-Auflösung . Benötigen Sie wirklich einen präzisen Timer (<1 ms) oder können Sie mit etwas weniger Präzisem leben (z. B. 10 ms Präzision)?

  • CPU-Kosten . Einige Methoden erfordern mehr CPU als andere. Im Allgemeinen benötigen präzisere Timer mehr CPU. Das Aufrufen einiger Methoden kann auch Konsequenzen für die anderen Anwendungen haben (z. B. das Aufrufen timeBeginPeriod(1)von win32 belastet das Betriebssystem und verkürzt die Akkulaufzeit des Laptops).

  • funktioniert mit SMP . Einige Methoden verwenden bestimmte CPU-Daten (z. B. Anzahl der verstrichenen Zyklen) und können in Multiprozessor-Umgebungen Probleme haben.

  • funktioniert mit CPU mit Stromsparfunktion.Einige CPUs können die Frequenz im Laufe der Zeit ändern, was bei einigen Timing-Methoden zu Problemen führen kann.

Für das Beispiel, das Sie geben, würde ich eine Kombination von verwenden QueryPerformanceCounter() / QueryPerformanceFrequency().

Bitte beachten Sie auch, dass in dem von Ihnen angegebenen Beispiel sleep(100)tatsächlich mehr als 100 ms gewartet werden kann. Selbst mit einem sehr präzisen Timer können andere Ergebnisse als 100 ms zurückgegeben werden.

Tigrou
quelle
Es stimmt zwar, dass einige Timing-Methoden Probleme mit SMP haben, dies sind jedoch immer Fehler in der HAL, im BIOS oder in anderen treiberbezogenen Problemen, die auf dieser Ebene behoben werden müssen . Es ist selten, dass Sie das Problem lösen können, indem Sie zu einer anderen Timing-Methode wechseln. Ich habe zum Beispiel gesehen, wie QPC wegen schlechter Treiber rückwärts lief, aber GetTickCount hätte es auch getan - der einzige Grund, warum dies nicht der Fall war, war, dass der Sprung ~ 50 µs betrug, sodass er es überhaupt nicht bemerkte. Bestenfalls kann Ihre App einen maxAnruf mit dem zuvor zurückgegebenen Wert tätigen.
QPC und gleichwertige Funktionen auf anderen Betriebssystemen wie POSIX clock_gettimesind so spezifiziert, dass sie auch auf SMP-Systemen nicht rückwärts laufen und unabhängig von der aktuellen CPU-Frequenz sind. Es ist daher ein schlechter Rat, die Leute von ihnen weg und hin zu schlechteren Timing-Funktionen zu warnen, da alle anderen Timing-Funktionen wahrscheinlich die gleichen Probleme haben würden, wenn Sie sie jemals dazu bringen könnten, so genau zu arbeiten, wie Sie es von QPC wollten. Also ja, es gibt eine beste Lösung: QPC, clock_gettime und mach_absolute_time. Für das Timing der Spielschleife gibt es keinen Grund, etwas anderes zu verwenden, wenn diese verfügbar sind.
0

Ich habe gerade einige Benchmarks durchgeführt, und es gibt so gut wie keinen guten Grund mehr, timeGetTime zu verwenden. QueryPerformanceCounter ist etwas schneller und viel präziser.

Dann gibt es GetTickCount, das fast augenblicklich ist, da es nur einen Wert zurückliest, den das Betriebssystem während der Prozessplanung ändert. Der Wert bleibt jeweils über 15 ms lang gleich und wird nur für ein sehr grobes Timing empfohlen.

Dwedit
quelle
-1

Schauen Sie sich die Funktion GetTickCount () an. Der Rückgabewert ist die Anzahl der Millisekunden, die seit dem Start des Systems vergangen sind.

unsigned int Last = 0;
unsigned int Now = 0;
Last = GetTickCount();
//...Run your functions..
Now = GetTickCount();
printf("Elapsed Time in Milliseconds: %i",(Now-Last));
Bandrewk
quelle
6
-1. GetTickCount () hat eine schlechte Auflösung.