Ist gettimeofday () garantiert in Mikrosekunden Auflösung?

  • Ich portiere ein Spiel, das ursprünglich für die Win32-API geschrieben wurde, nach Linux (also den OS X-Port des Win32-Ports nach Linux).

    Ich habe QueryPerformanceCounter implementiert, indem ich die uSeconds seit dem Start des Prozesses angegeben habe:

     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;
    }
     

    In Kombination mit QueryPerformanceFrequency(), das eine konstante 1000000 als Frequenz angibt, funktioniert es gut auf meinem Rechner und gibt mir eine 64-Bit-Variable, die seit dem Programmstart uSeconds enthält.

    Ist das also tragbar? Ich möchte nicht herausfinden, dass es anders funktioniert, wenn der Kernel auf eine bestimmte Art oder Weise kompiliert wurde. Ich bin damit einverstanden, dass es nicht zu etwas anderem als Linux portierbar ist.

    31 May 2018
    Rann LifshitzCato Johnston
9 answers
  • Vielleicht. Aber du hast größere Probleme. gettimeofday() kann zu fehlerhaften Timings führen, wenn in Ihrem System Prozesse vorhanden sind, die den Timer ändern (dh ntpd). Auf einem "normalen" Linux glaube ich jedoch, dass die Auflösung von gettimeofday() 10us beträgt. Je nach den Prozessen, die auf Ihrem System ablaufen, kann es vorwärts und rückwärts sowie zeitlich springen. Damit erhalten Sie effektiv die Antwort auf Ihre Frage Nr.

    Sie sollten sich clock_gettime(CLOCK_MONOTONIC) für Timing-Intervalle anschauen. Es gibt einige weniger Probleme, die auf Multi-Core-Systeme und externe Clock-Einstellungen zurückzuführen sind.

    Schauen Sie sich auch die Funktion clock_getres() an.

    14 October 2012
    Paul FlemingChris
  • Hohe Auflösung, geringer Overhead-Zeitpunkt für Intel-Prozessoren

    Wenn Sie Intel-Hardware verwenden, lesen Sie hier die CPU-CPU -Zeitanweisungszähler. Sie erhalten die Anzahl der seit dem Start des Prozessors ausgeführten CPU-Zyklen. Dies ist wahrscheinlich der feinste Zähler, den Sie für die 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 die Anzahl der Sekunden aufteilen. Das Konvertieren in ein Double ist sehr praktisch.

    Wenn ich das auf meiner Box ausführte, bekomme ich

     11867927879484732
    11867927879692217
    it took this long to call printf: 207485
     

    Hier ist das Handbuch für Intel-Entwickler Tonnen von 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);
    }
     
    23 August 2010
    Evan Teran
  • @Bernard:

    Ich muss zugeben, das meiste Ihres Beispiels ging mir direkt über den Kopf. Es kompiliert und scheint zu funktionieren. Ist dies für SMP-Systeme oder SpeedStep sicher?

    Das ist eine gute Frage ... Ich denke, der Code ist in Ordnung. Aus praktischer Sicht, wir verwenden es jeden Tag in meiner Firma, und wir verwenden eine ganze Reihe von Boxen, alles von 2 bis 8 Kernen. Natürlich YMMV usw., aber es scheint ein zuverlässiger und Overhead (weil der Kontext nicht in den Systemraum wechselt) Methode des Timings.

    Im Allgemeinen ist dies:

    • deklarieren Sie den Codeblock als Assembler (und flüchtig, sodass der Optimierer ihn in Ruhe lassen kann).
    • führt das aus CPUID-Anweisung Zusätzlich zum Abrufen einiger CPU-Informationen (mit denen wir nichts tun) synchronisiert er den Ausführungspuffer der CPU , sodass die Timings nicht durch die Ausführung außerhalb der Reihenfolge beeinflusst werden.
    • führt die Ausführung von rdtsc (Lesezeitstempel) aus. Dadurch wird die Anzahl der seit dem Zurücksetzen des Prozessors ausgeführten Maschinenzyklen abgerufen. Dies ist ein 64-Bit-Wert . Bei aktuellen CPU-Geschwindigkeiten wird dies etwa alle 194 Jahre ausgeführt. Interessanterweise wird in der ursprünglichen Pentium-Referenz angegeben, dass sie alle 5800 Jahre oder so umschlägt .
    • Die letzten Zeilen speichern die Werte aus den Registern in die Variablen hi und lo und setzen diese Werte in den 64-Bit-Rückgabewert.

    Spezifische Hinweise:

    • Die Ausführung außerhalb der Reihenfolge kann zu fehlerhaften Ergebnissen führen Wir führen die "cpuid" -Anweisung aus, die neben einigen Informationen über die CPU auch die Ausführung von Out-of-Order-Anweisungen synchronisiert.

    • Die meisten Betriebssysteme synchronisieren die Zähler auf den CPUs, wenn sie gestartet werden. Daher ist die Antwort innerhalb weniger Nanosekunden.

    • Der Ruhezustand Kommentar ist wahrscheinlich wahr, aber in der Praxis wahrscheinlich nicht

    15 June 2011
    Tony Delroy
  • Möglicherweise interessieren Sie sich für Linux-FAQ für clock_gettime(CLOCK_REALTIME)

    08 July 2009
    CesarB
  • Wine verwendet eigentlich 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

    04 August 2008
    Vincent Robert
  • So heißt es Mikrosekunden explizit, sagt aber, dass die Auflösung der Systemuhr nicht spezifiziert ist. Ich nehme an, Auflösung bedeutet in diesem Zusammenhang, wie der kleinste Betrag, den sie jemals inkrementiert wird, ist?

    Die Datenstruktur wird als Mikrosekunden als Maßeinheit definiert , aber das bedeutet nicht, dass die Uhr oder das Betriebssystem tatsächlich in der Lage sind, das genau zu messen.

    Wie andere Leute vorgeschlagen haben, ist gettimeofday() schlecht, weil die Uhrzeit eingestellt werden kann Verursachen Sie Zeitversatz und werfen Sie Ihre Berechnung aus clock_gettime(CLOCK_MONOTONIC) ist das, was Sie wollen, und clock_getres() sagt Ihnen die Genauigkeit Ihrer Uhr.

    19 December 2013
    vitaut
  • 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 Zeitgeber 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 Zeitmessung und Zeitmessung mit hoher Auflösung, Teil I

    12 September 2017
    Vineet JainCodingWithoutComments
  • 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-Variante usw. ab.

    01 August 2008
    CodingWithoutComments
  • Das Lesen des RDTSC ist in SMP-Systemen nicht zuverlässig, da jede CPU ihren eigenen Zähler unterhält und jeder Zähler nicht durch Synchronisation in Bezug auf eine andere CPU garantiert wird.

    Ich könnte vorschlagen, clock_gettime(CLOCK_REALTIME) zu versuchen. Das posix-Handbuch gibt an, dass dies auf allen kompatiblen Systemen implementiert werden sollte. Es kann eine Anzahl von Nanosekunden liefern, aber Sie sollten wahrscheinlich clock_getres(CLOCK_REALTIME) auf Ihrem System überprüfen, um die tatsächliche Auflösung zu ermitteln.

    08 July 2009
    CesarB