linux - Wird bei gettimeofday () eine Auflösung von Mikrosekunden garantiert?

Translate

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 implementiertQueryPerformanceCounterindem Sie die uSeconds seit dem Start des Prozesses angeben:

BOOL QueryPerformanceCounter(LARGE_INTEGER* performanceCount)
{
    gettimeofday(&currentTimeVal, NULL);
    performanceCount->QuadPart = (currentTimeVal.tv_sec - startTimeVal.tv_sec);
    performanceCount->QuadPart *= (1000 * 1000);
    performanceCount->QuadPart += (currentTimeVal.tv_usec - startTimeVal.tv_usec);

    return true;
}

Dies, gekoppelt mitQueryPerformanceFrequency()Die Angabe einer konstanten Frequenz von 1000000 funktioniert gutauf meiner Maschineund gibt mir eine 64-Bit-Variable, die enthältuSecondsseit dem Start des Programms.

Damitist das 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.

This question and all comments follow the "Attribution Required."

Alle Antworten

Translate

Könnte sein. Aber du hast größere Probleme.gettimeofday()kann zu falschen Timings führen, wenn auf Ihrem System Prozesse vorhanden sind, die den Timer ändern (z. B. ntpd). Unter einem "normalen" Linux glaube ich allerdings an die Auflösung vongettimeofday()ist 10us. Es kann vorwärts und rückwärts und folglich zeitlich springen, basierend auf den Prozessen, die auf Ihrem System ausgeführt werden. Dies macht effektiv die Antwort auf Ihre Frage nein.

Sie sollten untersuchenclock_gettime(CLOCK_MONOTONIC)für Zeitintervalle. Es leidet unter einigen weniger Problemen aufgrund von Dingen wie Mehrkernsystemen und externen Uhreinstellungen.

Schauen Sie auch in dieclock_getres()Funktion.

Quelle
Translate

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

11867927879484732
11867927879692217
it took this long to call printf: 207485

Hier ist dieIntel Entwicklerhandbuchdas gibt jede Menge Details.

#include <stdio.h>
#include <stdint.h>

inline uint64_t rdtsc() {
    uint32_t lo, hi;
    __asm__ __volatile__ (
      "xorl %%eax, %%eax\n"
      "cpuid\n"
      "rdtsc\n"
      : "=a" (lo), "=d" (hi)
      :
      : "%ebx", "%ecx");
    return (uint64_t)hi << 32 | lo;
}

main()
{
    unsigned long long x;
    unsigned long long y;
    x = rdtsc();
    printf("%lld\n",x);
    y = rdtsc();
    printf("%lld\n",y);
    printf("it took this long to call printf: %lld\n",y-x);
}
Quelle
Translate

@Bernard:

Ich muss zugeben, der größte Teil Ihres Beispiels ging mir direkt über den Kopf. Es kompiliert und scheint jedoch zu funktionieren. Ist dies sicher für SMP-Systeme oder SpeedStep?

Das ist eine gute Frage ... Ich denke, der Code ist in Ordnung. Aus praktischer Sicht verwenden wir es jeden Tag in meiner Firma und betreiben eine ziemlich große Auswahl an Boxen, alles von 2-8 Kernen. Natürlich YMMV usw., aber es scheint eine zuverlässige und zeitsparende Methode zu sein (da dadurch kein Kontext in den Systemraum gewechselt wird).

Im Allgemeinen funktioniert es wie folgt:

  • Deklarieren Sie den Codeblock als Assembler (und flüchtig, damit der Optimierer ihn in Ruhe lässt).
  • Führen Sie den CPUID-Befehl aus. Zusätzlich zum Abrufen einiger CPU-Informationen (mit denen wir nichts tun) wird der Ausführungspuffer der CPU synchronisiert, sodass die Timings nicht durch eine Ausführung außerhalb der Reihenfolge beeinflusst werden.
  • Führen Sie die Ausführung von rdtsc (read timestamp) aus. Dies ruft die Anzahl der Maschinenzyklen ab, die seit dem Zurücksetzen des Prozessors ausgeführt wurden. Dies ist ein 64-Bit-Wert, der bei den aktuellen CPU-Geschwindigkeiten etwa alle 194 Jahre auftritt. Interessanterweise stellen sie in der ursprünglichen Pentium-Referenz fest, dass sie sich etwa alle 5800 Jahre dreht.
  • In den letzten Zeilen werden die Werte aus den Registern in den Variablen hi und lo gespeichert und in den 64-Bit-Rückgabewert eingefügt.

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 constant_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
Translate

Wine verwendet tatsächlich gettimeofday (), um QueryPerformanceCounter () zu implementieren, und es ist bekannt, dass viele Windows-Spiele unter Linux und Mac funktionieren.

Startethttp://source.winehq.org/source/dlls/kernel32/cpu.c#L312

führt zuhttp://source.winehq.org/source/dlls/ntdll/time.c#L448

Quelle
Translate

Es heißt also explizit Mikrosekunden, aber die Auflösung der Systemuhr ist nicht spezifiziert. Ich nehme an, Auflösung bedeutet in diesem Zusammenhang, wie klein der Betrag ist, der jemals erhöht wird.

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 schlecht, da das Einstellen der Uhrzeit zu einem Zeitversatz führen und Ihre Berechnung beeinträchtigen kann.clock_gettime(CLOCK_MONOTONIC)ist was du willst undclock_getres()wird Ihnen die Präzision Ihrer Uhr sagen.

Quelle
Translate

Die tatsächliche Auflösung von gettimeofday () hängt von der Hardwarearchitektur ab. Intel-Prozessoren sowie SPARC-Maschinen bieten hochauflösende Timer, die Mikrosekunden messen. Andere Hardwarearchitekturen greifen auf den Timer des Systems zurück, der normalerweise auf 100 Hz eingestellt ist. In solchen Fällen ist die Zeitauflösung weniger genau.

Ich habe diese Antwort von erhaltenHochauflösende Zeitmessung und Timer, Teil I.

Quelle
Translate

Diese Antworterwähnt Probleme mit der eingestellten Uhr. Sowohl Ihre Probleme bei der Garantie von Tick-Einheiten als auch die Probleme mit der Zeitanpassung werden in C ++ 11 mit dem gelöst<chrono>Bibliothek.

Die Uhrstd::chrono::steady_clockwird garantiert nicht angepasst und wird darüber hinaus relativ zur Echtzeit mit einer konstanten Geschwindigkeit voranschreiten, sodass Technologien wie SpeedStep keine Auswirkungen darauf haben dürfen.

Sie können typsichere Einheiten erhalten, indem Sie in eine der konvertierenstd::chrono::durationSpezialisierungen wiestd::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
Translate

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
Translate

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 versuchenclock_gettime(CLOCK_REALTIME). Das posix-Handbuch gibt an, dass dies auf allen kompatiblen Systemen implementiert werden sollte. Es kann eine Nanosekundenzahl liefern, aber Sie werden es wahrscheinlich überprüfen wollenclock_getres(CLOCK_REALTIME)auf Ihrem System, um die tatsächliche Auflösung zu sehen.

Quelle