Come calcolare il tempo di esecuzione di uno snippet di codice in C ++

Devo calcolare il tempo di esecuzione di uno snippet di codice C ++ in secondi. Deve funzionare su macchine Windows o Unix.

Io uso il codice il seguente codice per farlo. (importa prima)

clock_t startTime = clock(); // some code here // to compute its execution duration in runtime cout << double( clock() - startTime ) / (double)CLOCKS_PER_SEC<< " seconds." << endl; 

Tuttavia per piccoli input o brevi istruzioni come a = a + 1, ottengo il risultato “0 secondi”. Penso che debba essere qualcosa come 0.0000001 secondi o qualcosa del genere.

Ricordo che System.nanoTime() in Java funziona piuttosto bene in questo caso. Tuttavia non riesco a ottenere le stesse esatte funzionalità dalla funzione clock() di C ++.

hai una soluzione?

Puoi usare questa funzione che ho scritto. Chiami GetTimeMs64() e restituisce il numero di millisecondi trascorsi dall’epoca di unix utilizzando l’orologio di sistema – esattamente come il time(NULL) , tranne in millisecondi.

Funziona su Windows e Linux; è thread-safe

Si noti che la granularità è di 15 ms su Windows; su Linux è dipendente dall’implementazione, ma di solito anche 15 ms.

 #ifdef _WIN32 #include  #else #include  #include  #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 } 

Ho un altro esempio funzionante che utilizza microsecondi (UNIX, POSIX, ecc.).

  #include  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; 

Ecco il file in cui abbiamo codificato questo:

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

Ecco una soluzione semplice in C ++ 11 che ti dà una risoluzione soddisfacente.

 #include  #include  class Timer { public: Timer() : beg_(clock_::now()) {} void reset() { beg_ = clock_::now(); } double elapsed() const { return std::chrono::duration_cast (clock_::now() - beg_).count(); } private: typedef std::chrono::high_resolution_clock clock_; typedef std::chrono::duration > second_; std::chrono::time_point beg_; }; 

O su * nix, per c ++ 03

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

Ecco l’esempio di utilizzo:

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

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

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

Quando progress_timer esce dal campo di applicazione, stamperà il tempo trascorso dalla sua creazione.

AGGIORNAMENTO : Ho realizzato una semplice sostituzione standalone (OSX / iOS ma facile da portare): https://github.com/catnapgames/TestTimerScoped

Windows fornisce la funzione QueryPerformanceCounter () e Unix ha gettimeofday () Entrambe le funzioni possono misurare almeno 1 differenza di microsecondi.

In alcuni programmi che ho scritto ho usato RDTS per questo scopo. RDTSC non è il tempo ma il numero di cicli dall’inizio del processore. Devi calibrarlo sul tuo sistema per ottenere un risultato in secondo, ma è davvero utile quando vuoi valutare le prestazioni, è ancora meglio usare il numero di cicli direttamente senza provare a cambiarli in secondi.

(link sopra è una pagina di Wikipedia in francese, ma ha esempi di codice C ++, la versione inglese è qui )

Suggerisco di utilizzare le funzioni di libreria standard per ottenere informazioni sull’ora dal sistema.

Se si desidera una risoluzione migliore, eseguire più iterazioni di esecuzione. Invece di eseguire il programma una sola volta e ottenere campioni, eseguirlo 1000 volte o più.

È preferibile eseguire più volte il ciclo interno con il timing delle prestazioni solo una volta e la media dividendo le ripetizioni del ciclo interno piuttosto che eseguire l’intera operazione (ciclo + tempo di esecuzione) più volte e nella media. Ciò ridurrà il sovraccarico del codice temporale delle prestazioni rispetto alla sezione profilata effettiva.

Avvolgi le tue chiamate programmate per il sistema appropriato. Per Windows, QueryPerformanceCounter è abbastanza veloce e “sicuro” da usare.

Puoi usare “rdtsc” anche su un PC X86 moderno, ma potrebbero esserci dei problemi su alcune macchine multicore (il core hopping potrebbe cambiare il timer) o se hai un acceleratore di qualche tipo triggersto.

Se vuoi ottenere buoni risultati esatti, come indicato sopra, il tempo di esecuzione dipende dalla programmazione dei thread. Una soluzione infallibile a questo, che dovrebbe produrre esattamente lo stesso tempo per ogni test, è quella di compilare il programma in modo che sia indipendente dal sistema operativo e avviare il computer in modo da eseguire il programma in un ambiente privo di OS. Tuttavia, un buon sostituto a questo è solo per impostare l’affinità del thread corrente su 1 core e la priorità sul più alto. Il risultato di ciò è un risultato molto coerente. E, ancora una volta, dovresti distriggersre le ottimizzazioni, che per g ++ o gcc significa aggiungere -O0 alla riga di comando, per evitare che il tuo codice venga testato dall’ottimizzazione. Ecco un esempio di come sto eseguendo il benchmarking delle funzioni della radice quadrata su un computer Windows.

 #include  #include  #include  #include  #include  #include  #include  #pragma intrinsic(__rdtsc) class Timer { public: Timer() : beg_(clock_::now()) {} void reset() { beg_ = clock_::now(); } double elapsed() const { return std::chrono::duration_cast (clock_::now() - beg_).count(); } private: typedef std::chrono::high_resolution_clock clock_; typedef std::chrono::duration > second_; std::chrono::time_point 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 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-- ) { (*function_to_do)( i << 7 ); } 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( nullptr, empty_function ); benchmark( "Standard Square Root", standard_sqrt ); benchmark( "Original Guess Square Root", original_guess_sqrt32 ); benchmark( "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; } 

Inoltre, merito a Mike Jarvis per il suo Timer.

E, per favore, nota (questo è molto importante) che se hai intenzione di eseguire snippet di codice più grandi, devi VERAMENTE ridimensionare il numero di iterazioni per evitare il blocco del tuo computer.

(soluzione specifica per Windows) Il modo corrente (circa 2017) per ottenere tempi precisi sotto Windows è usare “QueryPerformanceCounter”. Questo approccio ha il vantaggio di fornire risultati molto accurati ed è raccomandato dalla SM. Basta plopare il codice blob in una nuova app per console per ottenere un campione funzionante. C’è una lunga discussione qui: Acquisire i timestamp ad alta risoluzione

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

solo una semplice class che mette a confronto il codeblock:

 using namespace std::chrono; class benchmark { public: time_point 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(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; } 

boost :: timer ti darà probabilmente la precisione necessaria. Non è neanche lontanamente abbastanza preciso da dirti quanto a = a+1; prenderà, ma io quale motivo avresti tempo per qualcosa che richiede un paio di nanosecondi?

Ho creato un lambda che ti chiama funzione chiamata N volte e ti restituisce la media.

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

Puoi trovare l’intestazione di c ++ 11 qui .

Per i casi in cui si desidera registrare lo stesso intervallo di codice ogni volta che viene eseguito (ad esempio per il codice di profilazione che si ritiene possa essere un collo di bottiglia), ecco un involucro (una leggera modifica alla funzione di Andreas Bonini che trovo utile:

 #ifdef _WIN32 #include  #else #include  #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; }; 

Ho creato una semplice utility per misurare le prestazioni dei blocchi di codice, utilizzando la libreria chrono high_resolution_clock: https://github.com/nfergu/codetimer .

I tempi possono essere registrati con chiavi diverse e può essere visualizzata una vista aggregata dei tempi per ciascun tasto.

L’utilizzo è il seguente:

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

Si può anche guardare [cxx-rtimers][1] su GitHub, che fornisce alcune routine di solo intestazione per raccogliere statistiche sul tempo di esecuzione di qualsiasi blocco di codice in cui è ansible creare una variabile locale. Questi timer hanno versioni che utilizzano std :: chrono su C ++ 11, o timer dalla libreria Boost, o funzioni timer POSIX standard. Questi timer riporteranno la durata media, massima e minima spesa all’interno di una funzione, nonché il numero di volte in cui viene chiamato. Possono essere usati semplicemente come segue:

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