Ich muss die Ausführungszeit eines C ++ - Code-Snippets in Sekunden berechnen. Es muss entweder auf Windows- oder Unix-Computern funktionieren.
Ich benutze Code den folgenden Code, um dies zu tun. (vorher importieren)
clock_t startTime = clock();
// some code here
// to compute its execution duration in runtime
cout << double( clock() - startTime ) / (double)CLOCKS_PER_SEC<< " seconds." << endl;
Bei kleinen Eingaben oder kurzen Anweisungen wie a = a + 1 erhalte ich jedoch das Ergebnis "0 Sekunden". Ich denke, es muss ungefähr 0,0000001 Sekunden oder so sein.
Ich erinnere mich, dass System.nanoTime()
in Java in diesem Fall ziemlich gut funktioniert. Ich kann jedoch nicht genau die gleiche Funktionalität von der clock()
Funktion von C ++ erhalten.
Hast du eine lösung
c++
benchmarking
AhmetB - Google
quelle
quelle
Antworten:
Sie können diese Funktion verwenden, die ich geschrieben habe. Sie rufen
GetTimeMs64()
an und es wird die Anzahl der Millisekunden zurückgegeben, die seit der Unix-Epoche mit der Systemuhr verstrichen sind - genau wietime(NULL)
in Millisekunden.Es funktioniert sowohl unter Windows als auch unter Linux. es ist threadsicher.
Beachten Sie, dass die Granularität unter Windows 15 ms beträgt. Unter Linux ist es implementierungsabhängig, aber normalerweise auch 15 ms.
quelle
gettimeofday
ein unbeabsichtigtes Ergebnis liefern kann, wenn die Systemuhr geändert wird. Wenn dies ein Problem für Sie wäre, sollten Sie es sichclock_gettime
stattdessen ansehen .GetTickCount
?gcc -std=c99
GetTickCount
ist die Zeit, die seit dem Start des Systems vergangen ist, während meine Funktion die Zeit seit der UNIX-Epoche zurückgibt, was bedeutet, dass Sie sie für Datum und Uhrzeit verwenden können. Wenn Sie nur an der Zeit interessiert sind, die zwischen zwei Ereignissen verstrichen ist, ist meine immer noch die bessere Wahl, da es sich um eine int64 handelt. GetTickCount ist ein int32 und läuft alle 50 Tage über, was bedeutet, dass Sie seltsame Ergebnisse erzielen können, wenn sich die beiden von Ihnen registrierten Ereignisse zwischen dem Überlauf befinden.Ich habe ein anderes Arbeitsbeispiel, das Mikrosekunden verwendet (UNIX, POSIX usw.).
Hier ist die Datei, in der wir dies codiert haben:
https://github.com/arhuaco/junkcode/blob/master/emqbit-bench/bench.c
quelle
#include <sys/time.h>
zu Beginn Ihres Beispiels hinzufügen .Hier ist eine einfache Lösung in C ++ 11, die Ihnen eine zufriedenstellende Auflösung bietet.
Oder auf * nix für c ++ 03
Hier ist die Beispielverwendung:
Von https://gist.github.com/gongzhitaao/7062087
quelle
/usr/lib/x86_64-linux-gnu/libstdc++.so.6: version GLIBCXX_3.4.19 not found (required by ../cpu_2d/g500)
Wenn
progress_timer
der Gültigkeitsbereich verlassen wird, wird die seit seiner Erstellung verstrichene Zeit ausgedruckt.UPDATE : Hier ist eine Version, die ohne Boost funktioniert (getestet unter macOS / iOS):
quelle
Windows bietet die Funktion QueryPerformanceCounter () und Unix die Funktion gettimeofday (). Beide Funktionen können einen Unterschied von mindestens 1 Mikrosekunde messen.
quelle
#ifdef
muss in Ordnung sein (und es ist die Beurteilung von der Antwort , die Sie angenommen haben), und dann sehe ich nicht das Problem:#ifdef WIN32 #include <windows.h> ... #else ... #endif
.In einigen Programmen, die ich geschrieben habe, habe ich RDTS für diesen Zweck verwendet. Bei RDTSC geht es nicht um die Zeit, sondern um die Anzahl der Zyklen ab dem Prozessorstart. Sie müssen es auf Ihrem System kalibrieren, um ein Ergebnis in Sekunden zu erhalten. Es ist jedoch sehr praktisch, wenn Sie die Leistung bewerten möchten. Es ist sogar noch besser, die Anzahl der Zyklen direkt zu verwenden, ohne zu versuchen, sie auf Sekunden zurückzusetzen.
(Der obige Link führt zu einer französischen Wikipedia-Seite, enthält jedoch C ++ - Codebeispiele. Die englische Version finden Sie hier. )
quelle
Ich schlage vor, die Standardbibliotheksfunktionen zu verwenden, um Zeitinformationen vom System zu erhalten.
Wenn Sie eine feinere Auflösung wünschen, führen Sie mehr Ausführungsiterationen durch. Anstatt das Programm einmal auszuführen und Beispiele zu erhalten, führen Sie es 1000 Mal oder öfter aus.
quelle
Es ist besser, die innere Schleife mehrmals mit dem Leistungszeitpunkt nur einmal und durchschnittlich durch Teilen der Wiederholungen der inneren Schleife auszuführen, als das Ganze (Schleife + Leistungszeitpunkt) mehrmals und durchschnittlich auszuführen. Dies reduziert den Overhead des Performance-Timing-Codes im Vergleich zu Ihrem tatsächlichen Profilabschnitt.
Wickeln Sie Ihre Timer-Anrufe für das entsprechende System ein. Für Windows ist QueryPerformanceCounter ziemlich schnell und "sicher" zu verwenden.
Sie können "rdtsc" auch auf jedem modernen X86-PC verwenden, aber auf einigen Multicore-Computern können Probleme auftreten (Core-Hopping kann den Timer ändern) oder wenn Sie einen Geschwindigkeitsschritt aktiviert haben.
quelle
(Windows-spezifische Lösung) Die aktuelle (ca. 2017) Möglichkeit, genaue Timings unter Windows zu erhalten, ist die Verwendung von "QueryPerformanceCounter". Dieser Ansatz hat den Vorteil, dass sehr genaue Ergebnisse erzielt werden, und wird von den MS empfohlen. Fügen Sie einfach den Code-Blob in eine neue Konsolen-App ein, um ein funktionierendes Beispiel zu erhalten. Hier gibt es eine lange Diskussion: Erfassen von hochauflösenden Zeitstempeln
quelle
Eine vollständige Lösung für die Thread-Planung, die bei jedem Test genau die gleichen Zeiten liefern sollte, besteht darin, Ihr Programm so zu kompilieren, dass es vom Betriebssystem unabhängig ist, und Ihren Computer hochzufahren, um das Programm in einer betriebssystemfreien Umgebung auszuführen. Dies ist jedoch weitgehend unpraktisch und bestenfalls schwierig.
Ein guter Ersatz für die Betriebssystemfreiheit besteht darin, die Affinität des aktuellen Threads auf 1 Kern und die Priorität auf die höchste zu setzen. Diese Alternative sollte konsistent genug Ergebnisse liefern.
Außerdem sollten Sie Optimierungen deaktivieren, die das Debuggen beeinträchtigen würden, was für g ++ oder gcc das Hinzufügen
-Og
zur Befehlszeile bedeutet , um zu verhindern, dass der getestete Code optimiert wird. Das-O0
Flag sollte nicht verwendet werden, da es zusätzlichen unnötigen Overhead verursacht, der in den Timing-Ergebnissen enthalten wäre, wodurch die zeitgesteuerte Geschwindigkeit des Codes verzerrt wird.Im Gegenteil, sowohl unter der Annahme, dass Sie den endgültigen Produktionsbuild verwenden
-Ofast
(oder zumindest verwenden-O3
) als auch das Problem der "toten" Code-Eliminierung ignorieren, werden im-Og
Vergleich zu nur sehr wenige Optimierungen durchgeführt-Ofast
. Dies-Og
kann die tatsächliche Geschwindigkeit des Codes im Endprodukt falsch darstellen.Darüber hinaus sind alle Geschwindigkeitstests (bis zu einem gewissen Grad) gültig: In dem endgültigen Produktionsprodukt, mit dem kompiliert wurde
-Ofast
, ist nicht jedes Snippet / jeder Abschnitt / jede Funktion des Codes isoliert. Vielmehr fließt jedes Codeausschnitt kontinuierlich in das nächste, sodass der Compiler potenzielle Codeteile von überall her zusammenfügen, zusammenführen und optimieren kann.Wenn Sie ein Code-Snippet vergleichen, das stark genutzt wird
realloc()
, wird das Code-Snippet in einem Produktionsprodukt mit ausreichend hoher Speicherfragmentierung möglicherweise langsamer ausgeführt. Daher gilt für diese Situation der Ausdruck "Das Ganze ist mehr als die Summe seiner Teile", da Code im endgültigen Produktionsbuild möglicherweise merklich schneller oder langsamer ausgeführt wird als das einzelne Snippet, das Sie auf Geschwindigkeit testen.Eine Teillösung, die die Inkongruenz verringern kann, ist die Verwendung
-Ofast
für Geschwindigkeitstests mit Hinzufügungasm volatile("" :: "r"(var))
zu den am Test beteiligten Variablen, um die Beseitigung von totem Code / Schleife zu verhindern.Hier ist ein Beispiel für das Benchmarking von Quadratwurzelfunktionen auf einem Windows-Computer.
Dank auch an Mike Jarvis für seinen Timer.
Bitte beachten Sie (dies ist sehr wichtig), dass Sie die Anzahl der Iterationen verringern müssen, wenn Sie größere Codefragmente ausführen möchten, damit Ihr Computer nicht einfriert.
quelle
-O0
Code ist eine große Verschwendung von Zeit , weil der Aufwand für-O0
anstelle einem normalen-O2
oder-O3 -march=native
variiert wild abhängig von dem Code und der Arbeitsbelastung. zB extra benannte tmp vars kosten zeit bei-O0
. Es gibt andere Möglichkeiten, um zu vermeiden, dass Dinge wegoptimiert werden, z. B. das Ausblenden von Dingen vor dem Optimierer mitvolatile
, Nicht-Inline-Funktionen oder leeren Inline-asm-Anweisungen.-O0
ist nicht einmal annähernd verwendbar, da Code unterschiedliche Engpässe aufweist-O0
, nicht gleich, aber schlimmer.-Og
ist immer noch nicht sehr realistisch, je nach Code. Zumindest-O2
ist vorzugsweise-O3
realistischer. Verwenden Sieasm volatile("" ::: "+r"(var))
oder etwas, um den Compiler dazu zu bringen, einen Wert in einem Register zu materialisieren und die konstante Ausbreitung durch dieses Register zu verhindern.-O3
und das Code-Snippet mit aktualisiertasm volatile("" ::: "+r"(var))
.asm volatile("" ::: "+r"( i ));
scheint unnötig. In optimiertem Code gibt es keinen Grund, den Compiler zu zwingen,i
sowohli<<7
innerhalb als auch innerhalb der Schleife zu materialisieren . Sie verhindern, dass es optimiert wird,tmp -= 128
anstatt jedes Mal zu wechseln. Die Verwendung des Ergebnisses eines Funktionsaufrufs ist jedoch gut, wenn dies nicht der Fall istvoid
. Wieint result = (*function_to_do)( i << 7 );
. Sie könnten eineasm
Aussage zu diesem Ergebnis verwenden.function_to_do
,function_to_do
damit diese ohne Eliminierung eingefügt werden können. Bitte lassen Sie mich wissen, wenn Sie weitere Vorschläge haben.In Fällen, in denen Sie bei jeder Ausführung dieselbe Codestrecke zeitlich festlegen möchten (z. B. für die Profilerstellung von Code, von dem Sie glauben, dass er ein Engpass ist), finden Sie hier eine Zusammenfassung (eine geringfügige Änderung an) der Funktion von Andreas Bonini, die ich nützlich finde:
quelle
Nur eine einfache Klasse, die den Codeblock bewertet:
quelle
boost :: timer gibt Ihnen wahrscheinlich so viel Genauigkeit, wie Sie benötigen. Es ist bei weitem nicht genau genug, um Ihnen zu sagen, wie lange
a = a+1;
es dauern wird, aber aus welchem Grund müssten Sie etwas zeitlich festlegen, das ein paar Nanosekunden dauert?quelle
clock()
Funktion aus dem C ++ - Standardheader.Ich habe ein Lambda erstellt, das Sie N-mal aufruft und Ihnen den Durchschnitt zurückgibt.
Den c ++ 11-Header finden Sie hier .
quelle
Ich habe ein einfaches Dienstprogramm zum Messen der Leistung von Codeblöcken mithilfe der hochauflösenden Uhr der Chronobibliothek erstellt: https://github.com/nfergu/codetimer .
Timings können für verschiedene Tasten aufgezeichnet werden, und eine aggregierte Ansicht der Timings für jede Taste kann angezeigt werden.
Die Verwendung ist wie folgt:
quelle
Sie können sich auch den
[cxx-rtimers][1]
on GitHub ansehen , der einige Nur-Header-Routinen zum Sammeln von Statistiken zur Laufzeit eines Codeblocks bereitstellt, in dem Sie eine lokale Variable erstellen können. Diese Timer haben Versionen, die std :: chrono unter C ++ 11 verwenden, oder Timer aus der Boost-Bibliothek oder Standard-POSIX-Timerfunktionen. Diese Timer geben die durchschnittliche, maximale und minimale Dauer einer Funktion sowie die Häufigkeit an, mit der sie aufgerufen wird. Sie können wie folgt verwendet werden:quelle
So mache ich das, nicht viel Code, leicht zu verstehen, passt zu meinen Bedürfnissen:
Verwendung:
quelle
quelle