Timer-Funktion zur Bereitstellung der Zeit in Nanosekunden mit C ++

101

Ich möchte die Zeit berechnen, die eine API benötigt hat, um einen Wert zurückzugeben. Die für eine solche Aktion benötigte Zeit liegt im Bereich von Nanosekunden. Da die API eine C ++ - Klasse / Funktion ist, verwende ich die Datei timer.h, um dasselbe zu berechnen:

  #include <ctime>
  #include <cstdio>

  using namespace std;

  int main(int argc, char** argv) {

      clock_t start;
      double diff;
      start = clock();
      diff = ( std::clock() - start ) / (double)CLOCKS_PER_SEC;
      cout<<"printf: "<< diff <<'\n';

      return 0;
  }

Der obige Code gibt die Zeit in Sekunden an. Wie bekomme ich das gleiche in Nanosekunden und präziser?

gagneet
quelle
Der obige Code berechnet in Sekunden, ich möchte die Antwort in Nano-Sekunden erhalten ...
Gagneet
Sie müssen die Plattform zur Frage (und vorzugsweise auch zum Titel) hinzufügen, um eine gute Antwort zu erhalten.
Patrick Johnmeyer
Zusätzlich zum Erhalten der Zeit muss man Probleme mit dem Mikrobenchmarking nachschlagen (was äußerst komplex ist) - nur eine Ausführung durchzuführen und die Zeit am Anfang und am Ende zu erhalten, ist unwahrscheinlich, um eine ausreichende Genauigkeit zu erzielen.
Blaisorblade
@Blaisorblade: Zumal ich in einigen meiner Tests festgestellt habe, dass das bei weitem clock()nicht so schnell ist, wie ich dachte.
Mooing Duck

Antworten:

83

Was andere über das wiederholte Ausführen der Funktion in einer Schleife geschrieben haben, ist korrekt.

Für Linux (und BSD) möchten Sie clock_gettime () verwenden .

#include <sys/time.h>

int main()
{
   timespec ts;
   // clock_gettime(CLOCK_MONOTONIC, &ts); // Works on FreeBSD
   clock_gettime(CLOCK_REALTIME, &ts); // Works on Linux
}

Für Fenster möchten Sie den QueryPerformanceCounter verwenden . Und hier ist mehr über QPC

Anscheinend ist bei einigen Chipsätzen ein Problem mit QPC bekannt. Sie sollten daher sicherstellen, dass Sie diesen Chipsatz nicht haben. Zusätzlich können einige Dual-Core-AMDs ebenfalls ein Problem verursachen . Siehe den zweiten Beitrag von sebbbi, in dem er sagt:

QueryPerformanceCounter () und QueryPerformanceFrequency () bieten eine etwas bessere Auflösung, haben jedoch unterschiedliche Probleme. In Windows XP geben beispielsweise alle AMD Athlon X2-Dual-Core-CPUs den PC eines der Kerne "zufällig" zurück (der PC springt manchmal etwas zurück), es sei denn, Sie installieren speziell das AMD Dual-Core-Treiberpaket, um das Problem zu beheben. Wir haben keine anderen Dual + Core-CPUs mit ähnlichen Problemen bemerkt (p4 dual, p4 ht, core2 dual, core2 quad, phänom quad).

EDIT 2013/07/16:

Es sieht so aus, als ob es unter bestimmten Umständen einige Kontroversen über die Wirksamkeit von QPC gibt, wie unter http://msdn.microsoft.com/en-us/library/windows/desktop/ee417693(v=vs.85).aspx angegeben

... Während QueryPerformanceCounter und QueryPerformanceFrequency normalerweise für mehrere Prozessoren angepasst werden, können Fehler im BIOS oder in den Treibern dazu führen, dass diese Routinen unterschiedliche Werte zurückgeben, wenn der Thread von einem Prozessor zu einem anderen wechselt ...

Diese StackOverflow-Antwort https://stackoverflow.com/a/4588605/34329 besagt jedoch, dass QPC nach Win XP Service Pack 2 auf jedem MS-Betriebssystem einwandfrei funktionieren sollte.

Dieser Artikel zeigt, dass Windows 7 feststellen kann, ob die Prozessoren eine invariante TSC haben, und auf einen externen Timer zurückgreift, wenn dies nicht der Fall ist. http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html Die Prozessorübergreifende Synchronisierung ist immer noch ein Problem.

Andere gute Lektüre im Zusammenhang mit Timern:

Weitere Details finden Sie in den Kommentaren.

trauern
quelle
1
Ich habe TSC-Taktversatz auf einem älteren Dual-Xeon-PC gesehen, aber bei weitem nicht so schlimm wie auf einem Athlon X2 mit aktivierter C1-Taktrampe. Wenn der C1-Takt hochfährt, verlangsamt das Ausführen eines HLT-Befehls den Takt, wodurch die TSC auf Leerlaufkernen langsamer inkrementiert als auf aktiven Kernen.
bk1e
6
CLOCK_MONOTONIC funktioniert mit den verfügbaren Linux-Versionen.
Bernard
1
@Bernard - Das muss neu hinzugefügt werden, seit ich das das letzte Mal angeschaut habe. Danke für die Warnung.
Trauer
3
In der Tat müssen Sie verwenden CLOCK_MONOTONIC_RAW, wenn es verfügbar ist, um Hardware-Zeit nicht von NTP angepasst zu bekommen.
Wie hier erläutert, wird bei der korrekten Implementierung von QPC der TSC-Zähler nicht verwendet, zumindest wenn bekannt ist, dass er unzuverlässig ist: stackoverflow.com/q/510462/53974
Blaisorblade
69

Diese neue Antwort verwendet die Funktion von C ++ 11 <chrono>. Während es andere Antworten gibt, die die Verwendung zeigen <chrono>, zeigt keine von ihnen die Verwendung <chrono>mit der RDTSCin mehreren der anderen Antworten hier erwähnten Funktion. Also dachte ich , ich würde zeigen , wie die Verwendung RDTSCmit <chrono>. Ich werde zusätzlich zeigen , wie Sie den Testcode auf der Uhr templatize können , so dass Sie schnell umschalten können zwischen RDTSCund Ihrem System eingebaute Uhr Einrichtungen (was wahrscheinlich basierend auf wird clock(), clock_gettime()und / oder QueryPerformanceCounter.

Beachten Sie, dass die RDTSCAnweisung x86-spezifisch ist. QueryPerformanceCounterist nur Windows. Und clock_gettime()ist nur POSIX. Im Folgenden stelle ich zwei neue Uhren vor: std::chrono::high_resolution_clockund std::chrono::system_clock, die, wenn Sie C ++ 11 annehmen können, jetzt plattformübergreifend sind.

Hier erfahren Sie zunächst, wie Sie aus der Intel- rdtscAssembly-Anweisung eine C ++ 11-kompatible Uhr erstellen . Ich werde es nennen x::clock:

#include <chrono>

namespace x
{

struct clock
{
    typedef unsigned long long                 rep;
    typedef std::ratio<1, 2'800'000'000>       period; // My machine is 2.8 GHz
    typedef std::chrono::duration<rep, period> duration;
    typedef std::chrono::time_point<clock>     time_point;
    static const bool is_steady =              true;

    static time_point now() noexcept
    {
        unsigned lo, hi;
        asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
        return time_point(duration(static_cast<rep>(hi) << 32 | lo));
    }
};

}  // x

Dieser Takt zählt lediglich die CPU-Zyklen und speichert sie in einer vorzeichenlosen 64-Bit-Ganzzahl. Möglicherweise müssen Sie die Assembler-Syntax für Ihren Compiler anpassen. Oder Ihr Compiler bietet eine Eigenart an, die Sie stattdessen verwenden können (z now() {return __rdtsc();}. B. ).

Um eine Uhr zu erstellen, müssen Sie ihr die Darstellung (Speichertyp) geben. Sie müssen auch die Taktperiode angeben, die eine Kompilierungszeitkonstante sein muss, obwohl Ihr Gerät die Taktrate in verschiedenen Leistungsmodi ändern kann. Und von diesen aus können Sie die "native" Zeitdauer und den Zeitpunkt Ihrer Uhr anhand dieser Grundlagen leicht definieren.

Wenn Sie nur die Anzahl der Tick-Ticks ausgeben möchten, spielt es keine Rolle, welche Anzahl Sie für die Clock-Periode angeben. Diese Konstante kommt nur ins Spiel, wenn Sie die Anzahl der Tick-Ticks in eine Echtzeiteinheit wie Nanosekunden umwandeln möchten. Und in diesem Fall ist die Umrechnung in Nanosekunden (Millisekunden, was auch immer) umso genauer, je genauer Sie die Taktrate angeben können.

Unten finden Sie einen Beispielcode, der die Verwendung zeigt x::clock. Eigentlich habe ich den Code auf der Uhr als Vorlage verwendet, um zu zeigen, wie Sie viele verschiedene Uhren mit genau derselben Syntax verwenden können. Dieser spezielle Test zeigt, wie hoch der Schleifenaufwand ist, wenn Sie das ausführen, was Sie unter einer Schleife messen möchten:

#include <iostream>

template <class clock>
void
test_empty_loop()
{
    // Define real time units
    typedef std::chrono::duration<unsigned long long, std::pico> picoseconds;
    // or:
    // typedef std::chrono::nanoseconds nanoseconds;
    // Define double-based unit of clock tick
    typedef std::chrono::duration<double, typename clock::period> Cycle;
    using std::chrono::duration_cast;
    const int N = 100000000;
    // Do it
    auto t0 = clock::now();
    for (int j = 0; j < N; ++j)
        asm volatile("");
    auto t1 = clock::now();
    // Get the clock ticks per iteration
    auto ticks_per_iter = Cycle(t1-t0)/N;
    std::cout << ticks_per_iter.count() << " clock ticks per iteration\n";
    // Convert to real time units
    std::cout << duration_cast<picoseconds>(ticks_per_iter).count()
              << "ps per iteration\n";
}

Als erstes erstellt dieser Code eine "Echtzeit" -Einheit, in der die Ergebnisse angezeigt werden. Ich habe Pikosekunden ausgewählt, aber Sie können beliebige Einheiten auswählen, entweder ganzzahlig oder Gleitkomma. Als Beispiel gibt es eine vorgefertigte std::chrono::nanosecondsEinheit, die ich hätte verwenden können.

Als weiteres Beispiel möchte ich die durchschnittliche Anzahl von Taktzyklen pro Iteration als Gleitkomma ausdrucken, also erstelle ich eine andere Dauer, basierend auf double, die die gleichen Einheiten hat wie der Tick der Uhr ( Cycleim Code aufgerufen ).

Die Schleife wird mit Aufrufen clock::now()auf beiden Seiten zeitgesteuert . Wenn Sie den von dieser Funktion zurückgegebenen Typ benennen möchten, ist dies:

typename clock::time_point t0 = clock::now();

(wie im x::clockBeispiel deutlich gezeigt , und gilt auch für die vom System gelieferten Uhren).

Um eine Dauer in Form von Gleitkomma-Takt-Ticks zu erhalten, subtrahiert man lediglich die beiden Zeitpunkte, und um den Wert pro Iteration zu erhalten, dividieren Sie diese Dauer durch die Anzahl der Iterationen.

Sie können die Anzahl in beliebiger Dauer mithilfe der count()Elementfunktion abrufen. Dies gibt die interne Darstellung zurück. Schließlich std::chrono::duration_castkonvertiere ich die Dauer Cyclein die Dauer picosecondsund drucke sie aus.

Die Verwendung dieses Codes ist einfach:

int main()
{
    std::cout << "\nUsing rdtsc:\n";
    test_empty_loop<x::clock>();

    std::cout << "\nUsing std::chrono::high_resolution_clock:\n";
    test_empty_loop<std::chrono::high_resolution_clock>();

    std::cout << "\nUsing std::chrono::system_clock:\n";
    test_empty_loop<std::chrono::system_clock>();
}

Oben übe ich den Test mit unseren hausgemachten x::clockUhren aus und vergleiche diese Ergebnisse mit zwei der vom System gelieferten Uhren: std::chrono::high_resolution_clockund std::chrono::system_clock. Für mich druckt dies aus:

Using rdtsc:
1.72632 clock ticks per iteration
616ps per iteration

Using std::chrono::high_resolution_clock:
0.620105 clock ticks per iteration
620ps per iteration

Using std::chrono::system_clock:
0.00062457 clock ticks per iteration
624ps per iteration

Dies zeigt, dass jede dieser Uhren eine andere Tick-Periode hat, da die Ticks pro Iteration für jede Uhr sehr unterschiedlich sind. Bei der Umrechnung in eine bekannte Zeiteinheit (z. B. Pikosekunden) erhalte ich jedoch für jede Uhr ungefähr das gleiche Ergebnis (Ihr Kilometerstand kann variieren).

Beachten Sie, dass mein Code völlig frei von "magischen Konvertierungskonstanten" ist. In der Tat gibt es im gesamten Beispiel nur zwei magische Zahlen:

  1. Die Taktrate meiner Maschine, um zu definieren x::clock.
  2. Die Anzahl der zu testenden Iterationen. Wenn durch Ändern dieser Anzahl Ihre Ergebnisse stark variieren, sollten Sie wahrscheinlich die Anzahl der Iterationen erhöhen oder Ihren Computer während des Testens von konkurrierenden Prozessen entleeren.
Howard Hinnant
quelle
5
Mit "RDTSC ist nur Intel" beziehen Sie sich wirklich auf die x86-Architektur und -Derivate, nicht wahr? AMD-, Cyrix- und Transmeta x86-Chips verfügen über diese Anweisung , Intel RISC- und ARM-Prozessoren nicht.
Ben Voigt
1
@ BenVoigt: +1 Ja, deine Korrektur ist ganz richtig, danke.
Howard Hinnant
1
Wie wirkt sich die CPU-Drosselung darauf aus? Ändert sich die Taktrate nicht basierend auf der CPU-Last?
Tejas Kale
@TejasKale: Dies wird in der Antwort in den beiden aufeinander folgenden Absätzen beschrieben, beginnend mit "Um eine Uhr zu bauen, du ...". Normalerweise misst der Timing-Code keine Arbeit, die einen Thread blockiert (aber es kann). Und so wird Ihre CPU normalerweise nicht drosseln. Wenn Sie jedoch Code messen, der Schlaf, Mutex-Sperre, bedingungsvariable Wartezeit usw. umfasst, weist die rdtscUhr wahrscheinlich ungenaue Konvertierungen in andere Einheiten auf. Es ist eine gute Idee, Ihre Messungen so einzurichten, dass Sie die Uhren leicht ändern und vergleichen können (wie in dieser Antwort gezeigt).
Howard Hinnant
27

Mit dieser Genauigkeit ist es besser, im CPU-Tick zu argumentieren, als im Systemaufruf wie clock () . Und vergessen Sie nicht, dass es so gut wie unmöglich ist, eine Nanosekundengenauigkeit zu haben, wenn mehr als eine Nanosekunde benötigt wird, um einen Befehl auszuführen.

Trotzdem ist so etwas ein Anfang:

Hier ist der tatsächliche Code zum Abrufen der Anzahl der 80 x 86 CPU-Takt-Ticks, die seit dem letzten Start der CPU übergeben wurden. Es funktioniert auf Pentium und höher (386/486 wird nicht unterstützt). Dieser Code ist eigentlich MS Visual C ++ -spezifisch, kann aber wahrscheinlich sehr einfach auf alles andere portiert werden, solange er Inline-Assembly unterstützt.

inline __int64 GetCpuClocks()
{

    // Counter
    struct { int32 low, high; } counter;

    // Use RDTSC instruction to get clocks count
    __asm push EAX
    __asm push EDX
    __asm __emit 0fh __asm __emit 031h // RDTSC
    __asm mov counter.low, EAX
    __asm mov counter.high, EDX
    __asm pop EDX
    __asm pop EAX

    // Return result
    return *(__int64 *)(&counter);

}

Diese Funktion hat auch den Vorteil, dass sie extrem schnell ist - die Ausführung dauert normalerweise nicht mehr als 50 CPU-Zyklen.

Verwenden der Timing-Zahlen :
Wenn Sie die Taktzahlen in die tatsächlich verstrichene Zeit umwandeln müssen, teilen Sie die Ergebnisse durch die Taktrate Ihres Chips. Denken Sie daran, dass der "Nenn" -GHz-Wert wahrscheinlich geringfügig von der tatsächlichen Geschwindigkeit Ihres Chips abweicht. Um die tatsächliche Geschwindigkeit Ihres Chips zu überprüfen, können Sie mehrere sehr gute Dienstprogramme oder den Win32-Aufruf QueryPerformanceFrequency () verwenden.

VonC
quelle
danke für die information, das ist nützlich. Ich habe nicht an die CPU-Zyklen gedacht, um die Zeit zu berechnen. Ich denke, das ist ein sehr guter Punkt, den man beachten sollte :-)
Gagneet
4
Die Verwendung von QueryPerformanceFrequency () zum Umwandeln von TSC-Zählungen in verstrichene Zeit funktioniert möglicherweise nicht. QueryPerformanceCounter () verwendet den HPET (High Precision Event Timer) unter Vista, sofern verfügbar. Es verwendet den ACPI-Energieverwaltungs-Timer, wenn der Benutzer / USEPMTIMER zur boot.ini hinzufügt.
bk1e
23

Um dies richtig zu machen, können Sie eine von zwei Möglichkeiten verwenden, entweder mit RDTSCoder mit clock_gettime(). Die Sekunde ist ungefähr zweimal schneller und hat den Vorteil, die richtige absolute Zeit anzugeben. Beachten Sie, dass RDTSCSie es wie angegeben verwenden müssen, um richtig zu funktionieren (andere Kommentare auf dieser Seite weisen Fehler auf und können auf bestimmten Prozessoren zu falschen Timing-Werten führen).

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

und für clock_gettime: (Ich habe die Auflösung von Mikrosekunden willkürlich gewählt)

#include <time.h>
#include <sys/timeb.h>
// needs -lrt (real-time lib)
// 1970-01-01 epoch UTC time, 1 mcs resolution (divide by 1M to get time_t)
uint64_t ClockGetTime()
{
    timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    return (uint64_t)ts.tv_sec * 1000000LL + (uint64_t)ts.tv_nsec / 1000LL;
}

das Timing und die Werte:

Absolute values:
rdtsc           = 4571567254267600
clock_gettime   = 1278605535506855

Processing time: (10000000 runs)
rdtsc           = 2292547353
clock_gettime   = 1031119636
Marius
quelle
22

Ich verwende Folgendes, um die gewünschten Ergebnisse zu erzielen:

#include <time.h>
#include <iostream>
using namespace std;

int main (int argc, char** argv)
{
    // reset the clock
    timespec tS;
    tS.tv_sec = 0;
    tS.tv_nsec = 0;
    clock_settime(CLOCK_PROCESS_CPUTIME_ID, &tS);
    ...
    ... <code to check for the time to be put here>
    ...
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tS);
    cout << "Time taken is: " << tS.tv_sec << " " << tS.tv_nsec << endl;

    return 0;
}
gagneet
quelle
2
Ich habe abgestimmt, weil ich beim Anwenden dieses Codes zuerst googeln musste, warum die Zeitangabe nicht definiert ist. Dann musste ich googeln, was POSIX ist ... und so wie ich es verstanden habe, ist dieser Code nicht relevant für Windows-Benutzer, die sich an die Standardbibliothek halten möchten.
Daniel Katz
8

Für C ++ 11 ist hier ein einfacher Wrapper:

#include <iostream>
#include <chrono>

class Timer
{
public:
    Timer() : beg_(clock_::now()) {}
    void reset() { beg_ = clock_::now(); }
    double elapsed() const {
        return std::chrono::duration_cast<second_>
            (clock_::now() - beg_).count(); }

private:
    typedef std::chrono::high_resolution_clock clock_;
    typedef std::chrono::duration<double, std::ratio<1> > second_;
    std::chrono::time_point<clock_> beg_;
};

Oder für C ++ 03 auf * nix,

class Timer
{
public:
    Timer() { clock_gettime(CLOCK_REALTIME, &beg_); }

    double elapsed() {
        clock_gettime(CLOCK_REALTIME, &end_);
        return end_.tv_sec - beg_.tv_sec +
            (end_.tv_nsec - beg_.tv_nsec) / 1000000000.;
    }

    void reset() { clock_gettime(CLOCK_REALTIME, &beg_); }

private:
    timespec beg_, end_;
};

Anwendungsbeispiel:

int main()
{
    Timer tmr;
    double t = tmr.elapsed();
    std::cout << t << std::endl;

    tmr.reset();
    t = tmr.elapsed();
    std::cout << t << std::endl;
    return 0;
}

Von https://gist.github.com/gongzhitaao/7062087

Gongzhitaao
quelle
5

Um festzulegen, wie lange es dauert, eine Funktion aufzurufen, möchten Sie dies im Allgemeinen mehrmals als nur einmal ausführen. Wenn Sie Ihre Funktion nur einmal aufrufen und die Ausführung sehr kurz dauert, haben Sie immer noch den Aufwand, die Timer-Funktionen tatsächlich aufzurufen, und Sie wissen nicht, wie lange dies dauert.

Wenn Sie beispielsweise schätzen, dass die Ausführung Ihrer Funktion 800 ns dauern könnte, rufen Sie sie zehn Millionen Mal in einer Schleife auf (was dann ungefähr 8 Sekunden dauert). Teilen Sie die Gesamtzeit durch zehn Millionen, um die Zeit pro Anruf zu erhalten.

Greg Hewgill
quelle
Eigentlich versuche ich, die Leistung der API für einen bestimmten Anruf zu erhalten. Für jeden Lauf kann es eine andere Zeit geben. Dies kann sich auf das Diagramm auswirken, das ich für die Leistungsverbesserung mache ... daher die Zeit in Nanosekunden. aber ja, das ist eine großartige Idee, werde es in Betracht ziehen.
Gagneet
5

Sie können die folgende Funktion verwenden, wenn gcc unter x86-Prozessoren ausgeführt wird:

unsigned long long rdtsc()
{
  #define rdtsc(low, high) \
         __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))

  unsigned int low, high;
  rdtsc(low, high);
  return ((ulonglong)high << 32) | low;
}

mit Digital Mars C ++:

unsigned long long rdtsc()
{
   _asm
   {
        rdtsc
   }
}

Hiermit wird der Hochleistungstimer auf dem Chip gelesen. Ich benutze dies beim Profiling.

Walter Bright
quelle
2
Dies ist nützlich, ich werde überprüfen, ob der Prozessor x86 ist, da ich einen Apple Mac zum Experimentieren
benutze
1
Welche Werte soll der Benutzer für hoch und niedrig geben? Warum definieren Sie ein Makro im Hauptteil einer Funktion? Außerdem ist ulonglong, vermutlich typisiert bis unsigned long long, kein Standardtyp. Ich würde das gerne benutzen, bin mir aber nicht sicher wie;)
Joseph Garvin
1
unsigned long ist unter Linux nicht das Richtige. Möglicherweise möchten Sie stattdessen int verwenden, da long und long long unter 64-Bit-Linux 64-Bit sind.
Marius
3
Der TSC-Zähler ist heutzutage oft unzuverlässig: Er ändert seine Geschwindigkeit auf vielen Prozessoren, wenn die Frequenz geändert wird, und ist über verschiedene Kerne hinweg inkonsistent, daher wächst der TSC nicht immer.
Blaisorblade
1
@Marius: Ich habe Ihren Kommentar implementiert und unsigned intals internen Typ verwendet.
Blaisorblade
3

Wenn Sie eine Genauigkeit von weniger als einer Sekunde benötigen, müssen Sie systemspezifische Erweiterungen verwenden und die Dokumentation für das Betriebssystem überprüfen. POSIX unterstützt bis zu Mikrosekunden mit gettimeofday , aber nichts präziseres, da Computer keine Frequenzen über 1 GHz hatten.

Wenn Sie Boost verwenden, können Sie boost :: posix_time überprüfen .

Raymond Martineau
quelle
Wenn Sie den Code portabel halten möchten, sehen Sie die Boost-Bibliothek und prüfen Sie, ob ich dies mit dem Code bündeln kann. danke :-)
gagneet
3

Ich verwende hier Borland-Code. Der Code ti_hund gibt mir manchmal eine negative Nummer, aber das Timing ist ziemlich gut.

#include <dos.h>

void main() 
{
struct  time t;
int Hour,Min,Sec,Hun;
gettime(&t);
Hour=t.ti_hour;
Min=t.ti_min;
Sec=t.ti_sec;
Hun=t.ti_hund;
printf("Start time is: %2d:%02d:%02d.%02d\n",
   t.ti_hour, t.ti_min, t.ti_sec, t.ti_hund);
....
your code to time
...

// read the time here remove Hours and min if the time is in sec

gettime(&t);
printf("\nTid Hour:%d Min:%d Sec:%d  Hundreds:%d\n",t.ti_hour-Hour,
                             t.ti_min-Min,t.ti_sec-Sec,t.ti_hund-Hun);
printf("\n\nAlt Ferdig Press a Key\n\n");
getch();
} // end main
Paul J Moesman
quelle
3

Mit der Methode von Brock Adams mit einer einfachen Klasse:

int get_cpu_ticks()
{
    LARGE_INTEGER ticks;
    QueryPerformanceFrequency(&ticks);
    return ticks.LowPart;
}

__int64 get_cpu_clocks()
{
    struct { int32 low, high; } counter;

    __asm cpuid
    __asm push EDX
    __asm rdtsc
    __asm mov counter.low, EAX
    __asm mov counter.high, EDX
    __asm pop EDX
    __asm pop EAX

    return *(__int64 *)(&counter);
}

class cbench
{
public:
    cbench(const char *desc_in) 
         : desc(strdup(desc_in)), start(get_cpu_clocks()) { }
    ~cbench()
    {
        printf("%s took: %.4f ms\n", desc, (float)(get_cpu_clocks()-start)/get_cpu_ticks());
        if(desc) free(desc);
    }
private:
    char *desc;
    __int64 start;
};

Anwendungsbeispiel:

int main()
{
    {
        cbench c("test");
        ... code ...
    }
    return 0;
}

Ergebnis:

Test dauerte: 0,0002 ms

Hat einige Funktionsaufruf-Overhead, sollte aber immer noch mehr als schnell genug sein :)

Thomas
quelle
3

Sie können den Embedded Profiler (kostenlos für Windows und Linux) verwenden, der eine Schnittstelle zu einem Multiplattform-Timer (in einer Prozessorzykluszahl) hat und Ihnen eine Anzahl von Zyklen pro Sekunde geben kann:

EProfilerTimer timer;
timer.Start();

... // Your code here

const uint64_t number_of_elapsed_cycles = timer.Stop();
const uint64_t nano_seconds_elapsed =
    mumber_of_elapsed_cycles / (double) timer.GetCyclesPerSecond() * 1000000000;

Die Neuberechnung der Zyklusanzahl auf die Zeit ist möglicherweise ein gefährlicher Vorgang bei modernen Prozessoren, bei denen die CPU-Frequenz dynamisch geändert werden kann. Um sicherzustellen, dass die konvertierten Zeiten korrekt sind, muss die Prozessorfrequenz vor der Profilerstellung festgelegt werden.

Mi-La
quelle
2

Wenn dies für Linux ist, habe ich die Funktion "gettimeofday" verwendet, die eine Struktur zurückgibt, die die Sekunden und Mikrosekunden seit der Epoche angibt. Sie können dann timersub verwenden, um die beiden zu subtrahieren, um den Zeitunterschied zu erhalten, und ihn in die gewünschte Zeitgenauigkeit konvertieren. Sie geben jedoch Nanosekunden an und es sieht aus wie die Funktion clock_gettime () ist, wonach Sie suchen. Es gibt die Zeit in Sekunden und Nanosekunden in die Struktur ein, die Sie in sie übergehen.

Will Mc
quelle
clock_gettime () sollte vorerst den Trick machen. Ich werde versuchen, dasselbe für meinen Zweck zu verwenden ...
Gagneet
2

Was denkst du darüber:

    int iceu_system_GetTimeNow(long long int *res)
    {
      static struct timespec buffer;
      // 
    #ifdef __CYGWIN__
      if (clock_gettime(CLOCK_REALTIME, &buffer))
        return 1;
    #else
      if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &buffer))
        return 1;
    #endif
      *res=(long long int)buffer.tv_sec * 1000000000LL + (long long int)buffer.tv_nsec;
      return 0;
    }
icegood
quelle
2

Hier ist ein schöner Boost- Timer, der gut funktioniert:

//Stopwatch.hpp

#ifndef STOPWATCH_HPP
#define STOPWATCH_HPP

//Boost
#include <boost/chrono.hpp>
//Std
#include <cstdint>

class Stopwatch
{
public:
    Stopwatch();
    virtual         ~Stopwatch();
    void            Restart();
    std::uint64_t   Get_elapsed_ns();
    std::uint64_t   Get_elapsed_us();
    std::uint64_t   Get_elapsed_ms();
    std::uint64_t   Get_elapsed_s();
private:
    boost::chrono::high_resolution_clock::time_point _start_time;
};

#endif // STOPWATCH_HPP


//Stopwatch.cpp

#include "Stopwatch.hpp"

Stopwatch::Stopwatch():
    _start_time(boost::chrono::high_resolution_clock::now()) {}

Stopwatch::~Stopwatch() {}

void Stopwatch::Restart()
{
    _start_time = boost::chrono::high_resolution_clock::now();
}

std::uint64_t Stopwatch::Get_elapsed_ns()
{
    boost::chrono::nanoseconds nano_s = boost::chrono::duration_cast<boost::chrono::nanoseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(nano_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_us()
{
    boost::chrono::microseconds micro_s = boost::chrono::duration_cast<boost::chrono::microseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(micro_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_ms()
{
    boost::chrono::milliseconds milli_s = boost::chrono::duration_cast<boost::chrono::milliseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(milli_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_s()
{
    boost::chrono::seconds sec = boost::chrono::duration_cast<boost::chrono::seconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(sec.count());
}
Patrick K.
quelle
2

Minimalistische Copy & Paste-Struktur + fauler Gebrauch

Wenn die Idee ist, eine minimalistische Struktur zu haben, die Sie für schnelle Tests verwenden können, dann schlage ich vor, dass Sie direkt nach dem 's irgendwo in Ihre C ++ - Datei kopieren und einfügen#include . Dies ist der einzige Fall, in dem ich die Formatierung im Allman-Stil opfere.

Sie können die Genauigkeit in der ersten Zeile der Struktur leicht anpassen. Mögliche Werte sind: nanoseconds, microseconds, milliseconds, seconds, minutes, oder hours.

#include <chrono>
struct MeasureTime
{
    using precision = std::chrono::microseconds;
    std::vector<std::chrono::steady_clock::time_point> times;
    std::chrono::steady_clock::time_point oneLast;
    void p() {
        std::cout << "Mark " 
                << times.size()/2
                << ": " 
                << std::chrono::duration_cast<precision>(times.back() - oneLast).count() 
                << std::endl;
    }
    void m() {
        oneLast = times.back();
        times.push_back(std::chrono::steady_clock::now());
    }
    void t() {
        m();
        p();
        m();
    }
    MeasureTime() {
        times.push_back(std::chrono::steady_clock::now());
    }
};

Verwendung

MeasureTime m; // first time is already in memory
doFnc1();
m.t(); // Mark 1: next time, and print difference with previous mark
doFnc2();
m.t(); // Mark 2: next time, and print difference with previous mark
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.t(); // prints 'Mark 3: 123123' etc...

Standardausgabeergebnis

Mark 1: 123
Mark 2: 32
Mark 3: 433234

Wenn Sie nach der Ausführung eine Zusammenfassung wünschen

Wenn Sie den Bericht später möchten, weil beispielsweise Ihr Code dazwischen auch in die Standardausgabe schreibt. Fügen Sie dann der Struktur (kurz vor MeasureTime ()) die folgende Funktion hinzu:

void s() { // summary
    int i = 0;
    std::chrono::steady_clock::time_point tprev;
    for(auto tcur : times)
    {
        if(i > 0)
        {
            std::cout << "Mark " << i << ": "
                    << std::chrono::duration_cast<precision>(tprev - tcur).count()
                    << std::endl;
        }
        tprev = tcur;
        ++i;
    }
}

Dann können Sie einfach verwenden:

MeasureTime m;
doFnc1();
m.m();
doFnc2();
m.m();
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.m();
m.s();

Hier werden alle Markierungen wie zuvor aufgelistet, aber dann, nachdem der andere Code ausgeführt wurde. Beachten Sie, dass Sie nicht beide m.s()und verwenden sollten m.t().

Yeti
quelle
Funktioniert perfekt mit OpenMP unter Ubuntu 16.04. Vielen Dank, dies sollte die beste Antwort IMO sein!
Mhor Mé