Ich portiere ein Spiel, das ursprünglich für die Win32-API geschrieben wurde, nach Linux (nun, ich portiere den OS X-Port des Win32-Ports nach Linux).
Ich habe implementiert, QueryPerformanceCounter
indem ich die uSeconds seit dem Start des Prozesses angegeben habe:
BOOL QueryPerformanceCounter(LARGE_INTEGER* performanceCount)
{
gettimeofday(¤tTimeVal, NULL);
performanceCount->QuadPart = (currentTimeVal.tv_sec - startTimeVal.tv_sec);
performanceCount->QuadPart *= (1000 * 1000);
performanceCount->QuadPart += (currentTimeVal.tv_usec - startTimeVal.tv_usec);
return true;
}
Dies, zusammen mit der QueryPerformanceFrequency()
Angabe einer konstanten Frequenz von 1000000 als Frequenz, funktioniert auf meinem Computer gut und gibt mir eine 64-Bit-Variable, die uSeconds
seit dem Start des Programms enthält .
Ist das also tragbar? Ich möchte nicht herausfinden, dass es anders funktioniert, wenn der Kernel auf eine bestimmte Weise kompiliert wurde oder so etwas. Ich bin damit einverstanden, dass es nicht auf etwas anderes als Linux portierbar ist.
Hohe Auflösung, geringes Overhead-Timing für Intel-Prozessoren
Wenn Sie mit Intel-Hardware arbeiten, lesen Sie hier den Echtzeit-Befehlszähler der CPU. Hier erfahren Sie, wie viele CPU-Zyklen seit dem Start des Prozessors ausgeführt wurden. Dies ist wahrscheinlich der feinkörnigste Zähler, den Sie zur Leistungsmessung erhalten können.
Beachten Sie, dass dies die Anzahl der CPU-Zyklen ist. Unter Linux können Sie die CPU-Geschwindigkeit von / proc / cpuinfo abrufen und teilen, um die Anzahl der Sekunden zu ermitteln. Dies in ein Doppel umzuwandeln ist sehr praktisch.
Wenn ich das auf meiner Box laufen lasse, bekomme ich
Hier ist das Intel-Entwicklerhandbuch, das jede Menge Details enthält.
quelle
CPUID
nach der erstenRDTSC
Anweisung und vor der Ausführung des zu vergleichenden Codes nicht erneut verwendet werden? Was kann sonst verhindern, dass der Benchmark-Code vor / parallel zum ersten ausgeführtRDTSC
und folglich imRDTSC
Delta unterrepräsentiert wird ?@Bernard:
Das ist eine gute Frage ... Ich denke, der Code ist in Ordnung. Aus praktischer Sicht verwenden wir es jeden Tag in meiner Firma und laufen auf einer ziemlich großen Auswahl an Boxen, alles von 2-8 Kernen. Natürlich YMMV usw., aber es scheint eine zuverlässige und kostengünstige Timing-Methode zu sein (da dadurch kein Kontext in den Systemraum gewechselt wird).
Im Allgemeinen funktioniert es wie folgt:
Besondere Hinweise:
Eine Ausführung außerhalb der Reihenfolge kann zu falschen Ergebnissen führen. Daher führen wir die Anweisung "cpuid" aus, die nicht nur einige Informationen zur CPU liefert, sondern auch die Ausführung einer Anweisung außerhalb der Reihenfolge synchronisiert.
Die meisten Betriebssysteme synchronisieren die Zähler auf den CPUs, wenn sie gestartet werden, sodass die Antwort innerhalb weniger Nanosekunden gut ist.
Der Kommentar zum Ruhezustand ist wahrscheinlich wahr, aber in der Praxis interessieren Sie sich wahrscheinlich nicht für das Timing über die Grenzen des Ruhezustands hinweg.
in Bezug auf Speedstep: Neuere Intel-CPUs kompensieren die Geschwindigkeitsänderungen und geben eine angepasste Anzahl zurück. Ich habe einige der Boxen in unserem Netzwerk schnell gescannt und nur eine Box gefunden, die sie nicht hatte: einen Pentium 3 mit einem alten Datenbankserver. (Dies sind Linux-Boxen, also habe ich Folgendes überprüft: grep Konstante_tsc / proc / cpuinfo)
Ich bin mir bei den AMD-CPUs nicht sicher, wir sind in erster Linie ein Intel-Shop, obwohl ich weiß, dass einige unserer Low-Level-Systemgurus eine AMD-Evaluierung durchgeführt haben.
Ich hoffe, dies befriedigt Ihre Neugier, es ist ein interessanter und (IMHO) wenig erforschter Bereich der Programmierung. Weißt du, als Jeff und Joel darüber sprachen, ob ein Programmierer C kennen sollte oder nicht? Ich schrie sie an: "Hey, vergiss das hochrangige C-Zeug ... Assembler ist das, was du lernen solltest, wenn du wissen willst, was der Computer tut!"
quelle
Sie könnten an Linux-FAQ für interessiert sein
clock_gettime(CLOCK_REALTIME)
quelle
Wine verwendet tatsächlich gettimeofday (), um QueryPerformanceCounter () zu implementieren, und es ist bekannt, dass viele Windows-Spiele unter Linux und Mac funktionieren.
Startet http://source.winehq.org/source/dlls/kernel32/cpu.c#L312
führt zu http://source.winehq.org/source/dlls/ntdll/time.c#L448
quelle
Die Datenstruktur hat Mikrosekunden als Maßeinheit, aber das bedeutet nicht, dass die Uhr oder das Betriebssystem tatsächlich in der Lage sind, diese fein zu messen.
Wie andere Leute vorgeschlagen haben,
gettimeofday()
ist es schlecht, weil das Einstellen der Zeit zu einem Zeitversatz führen und Ihre Berechnung beeinträchtigen kann.clock_gettime(CLOCK_MONOTONIC)
ist, was Sie wollen, undclock_getres()
wird Ihnen die Präzision Ihrer Uhr sagen.quelle
Diese Antwort erhielt ich von High Resolution Time Measurement and Timers, Teil I.
quelle
Diese Antwort erwähnt Probleme mit der Einstellung der Uhr. Sowohl Ihre Probleme bei der Garantie von Tick-Einheiten als auch die Probleme mit der Zeitanpassung werden in C ++ 11 mit der
<chrono>
Bibliothek gelöst .Die Uhr
std::chrono::steady_clock
wird garantiert nicht angepasst und bewegt sich außerdem relativ zur Echtzeit mit einer konstanten Geschwindigkeit, sodass Technologien wie SpeedStep sie nicht beeinflussen dürfen.Sie können typsichere Einheiten erhalten, indem Sie in eine der
std::chrono::duration
Spezialisierungen konvertieren , zstd::chrono::microseconds
. Bei diesem Typ gibt es keine Mehrdeutigkeit hinsichtlich der vom Tick-Wert verwendeten Einheiten. Beachten Sie jedoch, dass die Uhr nicht unbedingt diese Auflösung hat. Sie können eine Dauer in Attosekunden umwandeln, ohne eine so genaue Uhr zu haben.quelle
Aus meiner Erfahrung und aus dem, was ich im Internet gelesen habe, lautet die Antwort "Nein", dies ist nicht garantiert. Dies hängt von der CPU-Geschwindigkeit, dem Betriebssystem, der Linux-Version usw. ab.
quelle
Das Lesen des RDTSC ist in SMP-Systemen nicht zuverlässig, da jede CPU ihren eigenen Zähler verwaltet und nicht garantiert wird, dass jeder Zähler in Bezug auf eine andere CPU synchronisiert wird.
Ich könnte vorschlagen, es zu versuchen
clock_gettime(CLOCK_REALTIME)
. Das posix-Handbuch gibt an, dass dies auf allen kompatiblen Systemen implementiert werden sollte. Es kann eine Nanosekundenzahl liefern, aber Sie sollten wahrscheinlichclock_getres(CLOCK_REALTIME)
Ihr System überprüfen , um die tatsächliche Auflösung zu ermitteln.quelle
clock_getres(CLOCK_REALTIME)
wird nicht die wirkliche Auflösung geben. Es gibt immer "1 ns" (eine Nanosekunde) zurück, wenn Zeitgeber verfügbar sind. Überprüfen Sie dieinclude/linux/hrtimer.h
Datei aufdefine HIGH_RES_NSEC 1
(mehr unter stackoverflow.com/a/23044075/196561 )