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?
clock()
nicht so schnell ist, wie ich dachte.Antworten:
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 .
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:
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
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.
quelle
CLOCK_MONOTONIC_RAW
, wenn es verfügbar ist, um Hardware-Zeit nicht von NTP angepasst zu bekommen.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 derRDTSC
in mehreren der anderen Antworten hier erwähnten Funktion. Also dachte ich , ich würde zeigen , wie die VerwendungRDTSC
mit<chrono>
. Ich werde zusätzlich zeigen , wie Sie den Testcode auf der Uhr templatize können , so dass Sie schnell umschalten können zwischenRDTSC
und Ihrem System eingebaute Uhr Einrichtungen (was wahrscheinlich basierend auf wirdclock()
,clock_gettime()
und / oderQueryPerformanceCounter
.Beachten Sie, dass die
RDTSC
Anweisung x86-spezifisch ist.QueryPerformanceCounter
ist nur Windows. Undclock_gettime()
ist nur POSIX. Im Folgenden stelle ich zwei neue Uhren vor:std::chrono::high_resolution_clock
undstd::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-
rdtsc
Assembly-Anweisung eine C ++ 11-kompatible Uhr erstellen . Ich werde es nennenx::clock
: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: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::nanoseconds
Einheit, 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 (
Cycle
im 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:(wie im
x::clock
Beispiel 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ßlichstd::chrono::duration_cast
konvertiere ich die DauerCycle
in die Dauerpicoseconds
und drucke sie aus.Die Verwendung dieses Codes ist einfach:
Oben übe ich den Test mit unseren hausgemachten
x::clock
Uhren aus und vergleiche diese Ergebnisse mit zwei der vom System gelieferten Uhren:std::chrono::high_resolution_clock
undstd::chrono::system_clock
. Für mich druckt dies aus: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:
x::clock
.quelle
rdtsc
Uhr 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).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.
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.
quelle
Um dies richtig zu machen, können Sie eine von zwei Möglichkeiten verwenden, entweder mit
RDTSC
oder mitclock_gettime()
. Die Sekunde ist ungefähr zweimal schneller und hat den Vorteil, die richtige absolute Zeit anzugeben. Beachten Sie, dassRDTSC
Sie 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).und für clock_gettime: (Ich habe die Auflösung von Mikrosekunden willkürlich gewählt)
das Timing und die Werte:
quelle
Ich verwende Folgendes, um die gewünschten Ergebnisse zu erzielen:
quelle
Für C ++ 11 ist hier ein einfacher Wrapper:
Oder für C ++ 03 auf * nix,
Anwendungsbeispiel:
Von https://gist.github.com/gongzhitaao/7062087
quelle
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.
quelle
Sie können die folgende Funktion verwenden, wenn gcc unter x86-Prozessoren ausgeführt wird:
mit Digital Mars C ++:
Hiermit wird der Hochleistungstimer auf dem Chip gelesen. Ich benutze dies beim Profiling.
quelle
unsigned int
als internen Typ verwendet.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 .
quelle
Ich verwende hier Borland-Code. Der Code ti_hund gibt mir manchmal eine negative Nummer, aber das Timing ist ziemlich gut.
quelle
Mit der Methode von Brock Adams mit einer einfachen Klasse:
Anwendungsbeispiel:
Ergebnis:
Test dauerte: 0,0002 ms
Hat einige Funktionsaufruf-Overhead, sollte aber immer noch mehr als schnell genug sein :)
quelle
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:
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.
quelle
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.
quelle
Was denkst du darüber:
quelle
Hier ist ein schöner Boost- Timer, der gut funktioniert:
quelle
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
, oderhours
.Verwendung
Standardausgabeergebnis
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:
Dann können Sie einfach verwenden:
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 solltenm.t()
.quelle