So berechnen Sie die Ausführungszeit eines Code-Snippets in C ++


121

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


29
Beachten Sie, dass ein zeitdifferenzbasierter Vergleich möglicherweise ungenau ist, da das Betriebssystem Ihren Thread möglicherweise nicht von Anfang bis Ende ausführt. Es kann es unterbrechen und andere Threads ausführen, die mit Ihren verknüpft sind. Dies hat erhebliche Auswirkungen auf die tatsächliche Zeit, die für den Abschluss Ihres Vorgangs benötigt wird. Sie können mehrere Male ausführen und die Ergebnisse mitteln. Sie können die Anzahl der anderen ausgeführten Prozesse minimieren. Aber keines davon wird den Fadenaufhängungseffekt vollständig beseitigen.
Mordachai

14
Mordachi, warum willst du es beseitigen? Sie möchten sehen, wie Ihre Funktion in einer realen Umgebung ausgeführt wird, nicht in einem magischen Bereich, in dem Threads niemals unterbrochen werden. Solange Sie es mehrmals ausführen und einen Durchschnitt erstellen, ist es sehr genau.
Thomas Bonini

Ja, ich führe es ein paar Mal aus und erreiche durchschnittliche Ergebnisse.
AhmetB - Google

14
Andreas, Mordachais Kommentar ist relevant, wenn das OP die Leistung seines Codes mit einem anderen Algorithmus vergleichen möchte. Wenn er beispielsweise heute Nachmittag mehrere Uhrentests durchführt und dann morgen früh einen anderen Algorithmus testet, ist sein Vergleich möglicherweise nicht zuverlässig, da er am Nachmittag möglicherweise Ressourcen mit viel mehr Prozessen als am Morgen teilt. Oder ein Satz Code führt dazu, dass das Betriebssystem weniger Verarbeitungszeit benötigt. Es gibt zahlreiche Gründe, warum diese Art der Leistungsmessung unzuverlässig ist, wenn er einen zeitbasierten Vergleich durchführen möchte.
weberc2

4
@Mordachai Ich weiß, dass ich auf einen alten Kommentar antworte, aber für jeden, der wie ich darauf stößt - für die zeitliche Leistung von Algorithmen möchten Sie das Minimum von ein paar Läufen nehmen, nicht den Durchschnitt. Dies ist diejenige, die die geringsten Unterbrechungen durch das Betriebssystem hatte, und so ist das Timing hauptsächlich Ihres Codes.
Baruch

Antworten:


115

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 wie time(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.

#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/time.h>
#include <ctime>
#endif

/* Remove if already defined */
typedef long long int64; typedef unsigned long long uint64;

/* Returns the amount of milliseconds elapsed since the UNIX epoch. Works on both
 * windows and linux. */

uint64 GetTimeMs64()
{
#ifdef _WIN32
 /* Windows */
 FILETIME ft;
 LARGE_INTEGER li;

 /* Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC) and copy it
  * to a LARGE_INTEGER structure. */
 GetSystemTimeAsFileTime(&ft);
 li.LowPart = ft.dwLowDateTime;
 li.HighPart = ft.dwHighDateTime;

 uint64 ret = li.QuadPart;
 ret -= 116444736000000000LL; /* Convert from file time to UNIX epoch time. */
 ret /= 10000; /* From 100 nano seconds (10^-7) to 1 millisecond (10^-3) intervals */

 return ret;
#else
 /* Linux */
 struct timeval tv;

 gettimeofday(&tv, NULL);

 uint64 ret = tv.tv_usec;
 /* Convert from micro seconds (10^-6) to milliseconds (10^-3) */
 ret /= 1000;

 /* Adds the seconds (10^0) after converting them to milliseconds (10^-3) */
 ret += (tv.tv_sec * 1000);

 return ret;
#endif
}

1
Zum späteren Nachschlagen: Ich werfe es einfach in eine Header-Datei und verwende es. Freut mich, es zu haben.
Daniel Handojo

1
Ich glaube, dass die Methode gettimeofdayein unbeabsichtigtes Ergebnis liefern kann, wenn die Systemuhr geändert wird. Wenn dies ein Problem für Sie wäre, sollten Sie es sich clock_gettimestattdessen ansehen .
Azmisov

Hat diese Methode für Windows Vorteile gegenüber GetTickCount?
MicroVirus

Kompiliert nicht mitgcc -std=c99
Assimilater

@MicroVirus: Ja, es GetTickCountist 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.
Thomas Bonini

43

Ich habe ein anderes Arbeitsbeispiel, das Mikrosekunden verwendet (UNIX, POSIX usw.).

    #include <sys/time.h>
    typedef unsigned long long timestamp_t;

    static timestamp_t
    get_timestamp ()
    {
      struct timeval now;
      gettimeofday (&now, NULL);
      return  now.tv_usec + (timestamp_t)now.tv_sec * 1000000;
    }

    ...
    timestamp_t t0 = get_timestamp();
    // Process
    timestamp_t t1 = get_timestamp();

    double secs = (t1 - t0) / 1000000.0L;

Hier ist die Datei, in der wir dies codiert haben:

https://github.com/arhuaco/junkcode/blob/master/emqbit-bench/bench.c


5
Sie sollten #include <sys/time.h>zu Beginn Ihres Beispiels hinzufügen .
Niekas

40

Hier ist eine einfache Lösung in C ++ 11, die Ihnen eine zufriedenstellende Auflösung bietet.

#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 auf * nix für c ++ 03

#include <iostream>
#include <ctime>

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

Hier ist die Beispielverwendung:

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


Ich /usr/lib/x86_64-linux-gnu/libstdc++.so.6: version GLIBCXX_3.4.19 not found (required by ../cpu_2d/g500)
erhalte

@ Julianromera Welche Plattform benutzt du? Haben Sie die libstdc ++ - Bibliothek und g ++ installiert?
Gongzhitaao

Es ist ein Slurm-Grid von Linux Ubuntu 12. Ich habe es gerade repariert. Ich habe -static-libstdc ++ am Ende des Linkers hinzugefügt. Vielen Dank für die Frage @gongzhitaao
user9869932

18
#include <boost/progress.hpp>

using namespace boost;

int main (int argc, const char * argv[])
{
  progress_timer timer;

  // do stuff, preferably in a 100x loop to make it take longer.

  return 0;
}

Wenn progress_timerder 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):

#include <chrono>
#include <string>
#include <iostream>
#include <math.h>
#include <unistd.h>

class NLTimerScoped {
private:
    const std::chrono::steady_clock::time_point start;
    const std::string name;

public:
    NLTimerScoped( const std::string & name ) : name( name ), start( std::chrono::steady_clock::now() ) {
    }


    ~NLTimerScoped() {
        const auto end(std::chrono::steady_clock::now());
        const auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>( end - start ).count();

        std::cout << name << " duration: " << duration_ms << "ms" << std::endl;
    }

};

int main(int argc, const char * argv[]) {

    {
        NLTimerScoped timer( "sin sum" );

        float a = 0.0f;

        for ( int i=0; i < 1000000; i++ ) {
            a += sin( (float) i / 100 );
        }

        std::cout << "sin sum = " << a << std::endl;
    }



    {
        NLTimerScoped timer( "sleep( 4 )" );

        sleep( 4 );
    }



    return 0;
}

2
Dies funktioniert, aber beachten Sie, dass progress_timer veraltet ist (einige Zeit vor Boost 1.50) - auto_cpu_timer ist möglicherweise besser geeignet.
DavidA

3
@meowsqueak hmm, auto_cpu_timer scheint zu erfordern, dass die Boost-Systembibliothek verknüpft ist, sodass es sich nicht mehr nur um eine Header-Lösung handelt. Schade ... macht die anderen Optionen plötzlich attraktiver.
Tomas Andrle

1
Ja, das ist ein guter Punkt. Wenn Sie Boost noch nicht verknüpfen, ist es mehr Ärger als es wert ist. Aber wenn Sie es bereits tun, funktioniert es ganz gut.
DavidA

@meowsqueak Ja, oder für einige schnelle Benchmark-Tests holen Sie sich einfach diese ältere Version von Boost.
Tomas Andrle

@TomasAndrle Der Link existiert nicht mehr.
Zheng Qu

5

Windows bietet die Funktion QueryPerformanceCounter () und Unix die Funktion gettimeofday (). Beide Funktionen können einen Unterschied von mindestens 1 Mikrosekunde messen.


Die Verwendung von windows.h ist jedoch eingeschränkt. Dieselbe kompilierte Quelle muss unter Windows und Unix ausgeführt werden. Wie gehe ich mit diesem Problem um?
AhmetB - Google

2
Dann suchen Sie nach einer Wrapper-Bibliothek stackoverflow.com/questions/1487695/…
Captain Comic

4
Dieselbe kompilierte Quelle klingt so, als ob Sie auf beiden Systemen dieselbe Binärdatei ausführen möchten, was anscheinend nicht der Fall ist. wenn Sie bedeuten die gleiche Quelle dann ein #ifdefmuss 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.
Nur jemand

3

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. )


2

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.


2

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.


2

(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

#include <iostream>
#include <tchar.h>
#include <windows.h>

int main()
{
constexpr int MAX_ITER{ 10000 };
constexpr __int64 us_per_hour{ 3600000000ull }; // 3.6e+09
constexpr __int64 us_per_min{ 60000000ull };
constexpr __int64 us_per_sec{ 1000000ull };
constexpr __int64 us_per_ms{ 1000ull };

// easy to work with
__int64 startTick, endTick, ticksPerSecond, totalTicks = 0ull;

QueryPerformanceFrequency((LARGE_INTEGER *)&ticksPerSecond);

for (int iter = 0; iter < MAX_ITER; ++iter) {// start looping
    QueryPerformanceCounter((LARGE_INTEGER *)&startTick); // Get start tick
    // code to be timed
    std::cout << "cur_tick = " << iter << "\n";
    QueryPerformanceCounter((LARGE_INTEGER *)&endTick); // Get end tick
    totalTicks += endTick - startTick; // accumulate time taken
}

// convert to elapsed microseconds
__int64 totalMicroSeconds =  (totalTicks * 1000000ull)/ ticksPerSecond;

__int64 hours = totalMicroSeconds / us_per_hour;
totalMicroSeconds %= us_per_hour;
__int64 minutes = totalMicroSeconds / us_per_min;
totalMicroSeconds %= us_per_min;
__int64 seconds = totalMicroSeconds / us_per_sec;
totalMicroSeconds %= us_per_sec;
__int64 milliseconds = totalMicroSeconds / us_per_ms;
totalMicroSeconds %= us_per_ms;


std::cout << "Total time: " << hours << "h ";
std::cout << minutes << "m " << seconds << "s " << milliseconds << "ms ";
std::cout << totalMicroSeconds << "us\n";

return 0;
}

2

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 -Ogzur Befehlszeile bedeutet , um zu verhindern, dass der getestete Code optimiert wird. Das -O0Flag 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 -OgVergleich zu nur sehr wenige Optimierungen durchgeführt -Ofast. Dies -Ogkann 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 -Ofastfür Geschwindigkeitstests mit Hinzufügung asm 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.

// set USE_ASM_TO_PREVENT_ELIMINATION  to 0 to prevent `asm volatile("" :: "r"(var))`
// set USE_ASM_TO_PREVENT_ELIMINATION  to 1 to enforce `asm volatile("" :: "r"(var))`
#define USE_ASM_TO_PREVENT_ELIMINATION 1

#include <iostream>
#include <iomanip>
#include <cstdio>
#include <chrono>
#include <cmath>
#include <windows.h>
#include <intrin.h>
#pragma intrinsic(__rdtsc)
#include <cstdint>

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

unsigned int guess_sqrt32(register unsigned int n) {
    register unsigned int g = 0x8000;
    if(g*g > n) {
        g ^= 0x8000;
    }
    g |= 0x4000;
    if(g*g > n) {
        g ^= 0x4000;
    }
    g |= 0x2000;
    if(g*g > n) {
        g ^= 0x2000;
    }
    g |= 0x1000;
    if(g*g > n) {
        g ^= 0x1000;
    }
    g |= 0x0800;
    if(g*g > n) {
        g ^= 0x0800;
    }
    g |= 0x0400;
    if(g*g > n) {
        g ^= 0x0400;
    }
    g |= 0x0200;
    if(g*g > n) {
        g ^= 0x0200;
    }
    g |= 0x0100;
    if(g*g > n) {
        g ^= 0x0100;
    }
    g |= 0x0080;
    if(g*g > n) {
        g ^= 0x0080;
    }
    g |= 0x0040;
    if(g*g > n) {
        g ^= 0x0040;
    }
    g |= 0x0020;
    if(g*g > n) {
        g ^= 0x0020;
    }
    g |= 0x0010;
    if(g*g > n) {
        g ^= 0x0010;
    }
    g |= 0x0008;
    if(g*g > n) {
        g ^= 0x0008;
    }
    g |= 0x0004;
    if(g*g > n) {
        g ^= 0x0004;
    }
    g |= 0x0002;
    if(g*g > n) {
        g ^= 0x0002;
    }
    g |= 0x0001;
    if(g*g > n) {
        g ^= 0x0001;
    }
    return g;
}

unsigned int empty_function( unsigned int _input ) {
    return _input;
}

unsigned long long empty_ticks=0;
double empty_seconds=0;
Timer my_time;

template<unsigned int benchmark_repetitions>
void benchmark( char* function_name, auto (*function_to_do)( auto ) ) {
    register unsigned int i=benchmark_repetitions;
    register unsigned long long start=0;
    my_time.reset();
    start=__rdtsc();
    while ( i-- ) {
        auto result = (*function_to_do)( i << 7 );
        #if USE_ASM_TO_PREVENT_ELIMINATION == 1
            asm volatile("" :: "r"(
                // There is no data type in C++ that is smaller than a char, so it will
                //  not throw a segmentation fault error to reinterpret any arbitrary
                //  data type as a char. Although, the compiler might not like it.
                result
            ));
        #endif
    }
    if ( function_name == nullptr ) {
        empty_ticks = (__rdtsc()-start);
        empty_seconds = my_time.elapsed();
        std::cout<< "Empty:\n" << empty_ticks
              << " ticks\n" << benchmark_repetitions << " repetitions\n"
               << std::setprecision(15) << empty_seconds
                << " seconds\n\n";
    } else {
        std::cout<< function_name<<":\n" << (__rdtsc()-start-empty_ticks)
              << " ticks\n" << benchmark_repetitions << " repetitions\n"
               << std::setprecision(15) << (my_time.elapsed()-empty_seconds)
                << " seconds\n\n";
    }
}


int main( void ) {
    void* Cur_Thread=   GetCurrentThread();
    void* Cur_Process=  GetCurrentProcess();
    unsigned long long  Current_Affinity;
    unsigned long long  System_Affinity;
    unsigned long long furthest_affinity;
    unsigned long long nearest_affinity;

    if( ! SetThreadPriority(Cur_Thread,THREAD_PRIORITY_TIME_CRITICAL) ) {
        SetThreadPriority( Cur_Thread, THREAD_PRIORITY_HIGHEST );
    }
    if( ! SetPriorityClass(Cur_Process,REALTIME_PRIORITY_CLASS) ) {
        SetPriorityClass( Cur_Process, HIGH_PRIORITY_CLASS );
    }
    GetProcessAffinityMask( Cur_Process, &Current_Affinity, &System_Affinity );
    furthest_affinity = 0x8000000000000000ULL>>__builtin_clzll(Current_Affinity);
    nearest_affinity  = 0x0000000000000001ULL<<__builtin_ctzll(Current_Affinity);
    SetProcessAffinityMask( Cur_Process, furthest_affinity );
    SetThreadAffinityMask( Cur_Thread, furthest_affinity );

    const int repetitions=524288;

    benchmark<repetitions>( nullptr, empty_function );
    benchmark<repetitions>( "Standard Square Root", standard_sqrt );
    benchmark<repetitions>( "Original Guess Square Root", original_guess_sqrt32 );
    benchmark<repetitions>( "New Guess Square Root", new_guess_sqrt32 );


    SetThreadPriority( Cur_Thread, THREAD_PRIORITY_IDLE );
    SetPriorityClass( Cur_Process, IDLE_PRIORITY_CLASS );
    SetProcessAffinityMask( Cur_Process, nearest_affinity );
    SetThreadAffinityMask( Cur_Thread, nearest_affinity );
    for (;;) { getchar(); }

    return 0;
}

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.


2
Gute Antwort, außer die Optimierung zu deaktivieren. Benchmarking - -O0Code ist eine große Verschwendung von Zeit , weil der Aufwand für -O0 anstelle einem normalen -O2oder -O3 -march=nativevariiert 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 mit volatile, Nicht-Inline-Funktionen oder leeren Inline-asm-Anweisungen. -O0ist nicht einmal annähernd verwendbar, da Code unterschiedliche Engpässe aufweist -O0, nicht gleich, aber schlimmer.
Peter Cordes

1
Ugh, -Ogist immer noch nicht sehr realistisch, je nach Code. Zumindest -O2ist vorzugsweise -O3realistischer. Verwenden Sie asm 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.
Peter Cordes

@ PeterCordes Nochmals vielen Dank für Ihre Erkenntnisse. Ich habe den Inhalt mit -O3und das Code-Snippet mit aktualisiert asm volatile("" ::: "+r"(var)).
Jack Giffin

1
asm volatile("" ::: "+r"( i ));scheint unnötig. In optimiertem Code gibt es keinen Grund, den Compiler zu zwingen, isowohl i<<7innerhalb als auch innerhalb der Schleife zu materialisieren . Sie verhindern, dass es optimiert wird, tmp -= 128anstatt jedes Mal zu wechseln. Die Verwendung des Ergebnisses eines Funktionsaufrufs ist jedoch gut, wenn dies nicht der Fall ist void. Wie int result = (*function_to_do)( i << 7 );. Sie könnten eine asmAussage zu diesem Ergebnis verwenden.
Peter Cordes

@PeterCordes Nochmals vielen Dank oder Ihre Erkenntnisse. Mein Beitrag enthält jetzt die Korrekturen für den Rückgabewert von function_to_do, function_to_dodamit diese ohne Eliminierung eingefügt werden können. Bitte lassen Sie mich wissen, wenn Sie weitere Vorschläge haben.
Jack Giffin

1

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:

#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/time.h>
#endif

/*
 *  A simple timer class to see how long a piece of code takes. 
 *  Usage:
 *
 *  {
 *      static Timer timer("name");
 *
 *      ...
 *
 *      timer.start()
 *      [ The code you want timed ]
 *      timer.stop()
 *
 *      ...
 *  }
 *
 *  At the end of execution, you will get output:
 *
 *  Time for name: XXX seconds
 */
class Timer
{
public:
    Timer(std::string name, bool start_running=false) : 
        _name(name), _accum(0), _running(false)
    {
        if (start_running) start();
    }

    ~Timer() { stop(); report(); }

    void start() {
        if (!_running) {
            _start_time = GetTimeMicroseconds();
            _running = true;
        }
    }
    void stop() {
        if (_running) {
            unsigned long long stop_time = GetTimeMicroseconds();
            _accum += stop_time - _start_time;
            _running = false;
        }
    }
    void report() { 
        std::cout<<"Time for "<<_name<<": " << _accum / 1.e6 << " seconds\n"; 
    }
private:
    // cf. http://stackoverflow.com/questions/1861294/how-to-calculate-execution-time-of-a-code-snippet-in-c
    unsigned long long GetTimeMicroseconds()
    {
#ifdef _WIN32
        /* Windows */
        FILETIME ft;
        LARGE_INTEGER li;

        /* Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC) and copy it
         *   * to a LARGE_INTEGER structure. */
        GetSystemTimeAsFileTime(&ft);
        li.LowPart = ft.dwLowDateTime;
        li.HighPart = ft.dwHighDateTime;

        unsigned long long ret = li.QuadPart;
        ret -= 116444736000000000LL; /* Convert from file time to UNIX epoch time. */
        ret /= 10; /* From 100 nano seconds (10^-7) to 1 microsecond (10^-6) intervals */
#else
        /* Linux */
        struct timeval tv;

        gettimeofday(&tv, NULL);

        unsigned long long ret = tv.tv_usec;
        /* Adds the seconds (10^0) after converting them to microseconds (10^-6) */
        ret += (tv.tv_sec * 1000000);
#endif
        return ret;
    }
    std::string _name;
    long long _accum;
    unsigned long long _start_time;
    bool _running;
};

1

Nur eine einfache Klasse, die den Codeblock bewertet:

using namespace std::chrono;

class benchmark {
  public:
  time_point<high_resolution_clock>  t0, t1;
  unsigned int *d;
  benchmark(unsigned int *res) : d(res) { 
                 t0 = high_resolution_clock::now();
  }
  ~benchmark() { t1 = high_resolution_clock::now();
                  milliseconds dur = duration_cast<milliseconds>(t1 - t0);
                  *d = dur.count();
  }
};
// simple usage 
// unsigned int t;
// { // put the code in a block
//  benchmark bench(&t);
//  // ...
//  // code to benchmark
// }
// HERE the t contains time in milliseconds

// one way to use it can be :
#define BENCH(TITLE,CODEBLOCK) \
  unsigned int __time__##__LINE__ = 0;  \
  { benchmark bench(&__time__##__LINE__); \
      CODEBLOCK \
  } \
  printf("%s took %d ms\n",(TITLE),__time__##__LINE__);


int main(void) {
  BENCH("TITLE",{
    for(int n = 0; n < testcount; n++ )
      int a = n % 3;
  });
  return 0;
}

0

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?


Es basiert auf der clock()Funktion aus dem C ++ - Standardheader.
Petter

0

Ich habe ein Lambda erstellt, das Sie N-mal aufruft und Ihnen den Durchschnitt zurückgibt.

double c = BENCHMARK_CNT(25, fillVectorDeque(variable));

Den c ++ 11-Header finden Sie hier .


0

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:

#include <chrono>
#include <iostream>
#include "codetimer.h"

int main () {
    auto start = std::chrono::high_resolution_clock::now();
    // some code here
    CodeTimer::record("mykey", start);
    CodeTimer::printStats();
    return 0;
}

0

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:

#include <rtimers/cxx11.hpp>

void expensiveFunction() {
    static rtimers::cxx11::DefaultTimer timer("expensive");
    auto scopedStartStop = timer.scopedStart();
    // Do something costly...
}

0

So mache ich das, nicht viel Code, leicht zu verstehen, passt zu meinen Bedürfnissen:

void bench(std::function<void()> fnBench, std::string name, size_t iterations)
{
    if (iterations == 0)
        return;
    if (fnBench == nullptr)
        return;
    std::chrono::high_resolution_clock::time_point start, end;
    if (iterations == 1)
    {
        start = std::chrono::high_resolution_clock::now();
        fnBench();
        end = std::chrono::high_resolution_clock::now();
    }
    else
    {
        start = std::chrono::high_resolution_clock::now();
        for (size_t i = 0; i < iterations; ++i)
            fnBench();
        end = std::chrono::high_resolution_clock::now();
    }
    printf
    (
        "bench(*, \"%s\", %u) = %4.6lfs\r\n",
        name.c_str(),
        iterations,
        std::chrono::duration_cast<std::chrono::duration<double>>(end - start).count()
    );
}

Verwendung:

bench
(
    []() -> void // function
    {
        // Put your code here
    },
    "the name of this", // name
    1000000 // iterations
);

0
#include <omp.h>

double start = omp_get_wtime();

// code 

double finish = omp_get_wtime();

double total_time = finish - start;

2
Während dieser Code die Frage lösen kann, einschließlich einer Erklärung, wie und warum dies das Problem löst, würde dies wirklich dazu beitragen, die Qualität Ihres Beitrags zu verbessern, und wahrscheinlich zu mehr Up-Votes führen. Denken Sie daran, dass Sie in Zukunft die Frage für die Leser beantworten, nicht nur für die Person, die jetzt fragt. Bitte bearbeiten Sie Ihre Antwort, um Erklärungen hinzuzufügen und anzugeben, welche Einschränkungen und Annahmen gelten.
Dharman
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.