Funzione timer per fornire il tempo in nano secondi usando C ++

Desidero calcolare il tempo impiegato da un’API per restituire un valore. Il tempo impiegato per tale azione è nello spazio di nano secondi. Poiché l’API è una class / funzione C ++, sto usando il timer.h per cacolare lo stesso:

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

Il codice sopra indica il tempo in secondi. Come ottengo lo stesso in nano secondi e con maggiore precisione?

Quello che altri hanno pubblicato sull’esecuzione ripetuta della funzione in un ciclo è corretto.

Per Linux (e BSD) si desidera utilizzare clock_gettime () .

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

Per Windows si desidera utilizzare QueryPerformanceCounter . E qui c’è di più su QPC

Apparentemente c’è un problema noto con QPC su alcuni chipset, quindi dovresti assicurarti di non avere quel chipset. Inoltre alcuni AMD dual core possono anche causare un problema . Vedi il secondo post di sebbbi, dove afferma:

QueryPerformanceCounter () e QueryPerformanceFrequency () offrono una risoluzione leggermente migliore, ma presentano problemi diversi. Ad esempio, in Windows XP, tutte le CPU dual core AMD Athlon X2 restituiscono “casualmente” il PC di uno dei core (il PC talvolta salta un po ‘indietro), a meno che non si installi appositamente il pacchetto driver dual core AMD per risolvere il problema. Non abbiamo notato nessun’altra CPU dual + core con problemi simili (p4 dual, p4 ht, core2 dual, core2 quad, phenom quad).

MODIFICA 2013/07/16:

Sembra che ci siano alcune controversie sull’efficacia di QPC in determinate circostanze, come indicato in http://msdn.microsoft.com/en-us/library/windows/desktop/ee417693(v=vs.85).aspx

… Mentre QueryPerformanceCounter e QueryPerformanceFrequency di solito si adattano a più processori, i bug nel BIOS o nei driver possono causare che queste routine restituiscano valori diversi mentre il thread si sposta da un processore a un altro …

Tuttavia, questa risposta StackOverflow https://stackoverflow.com/a/4588605/34329 afferma che QPC dovrebbe funzionare correttamente su qualsiasi sistema operativo MS dopo il service pack 2 di Win XP.

Questo articolo mostra che Windows 7 può determinare se il / i processore / i hanno un TSC invariante e ricade su un timer esterno se non lo fa. http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html La sincronizzazione tra processori è ancora un problema.

Altre letture fini relative ai timer:

Vedi i commenti per maggiori dettagli.

Questa nuova risposta utilizza la funzione C ++ 11. Mentre ci sono altre risposte che mostrano come usare , nessuno di loro mostra come usare con la funzione RDTSC menzionata in molte delle altre risposte qui. Quindi ho pensato di mostrare come usare RDTSC con . Inoltre dimostrerò come è ansible templatizzare il codice di test sull’orologio in modo da poter passare rapidamente tra RDTSC e le funzioni dell’orologio integrato del sistema (che sarà probabilmente basato su clock() , clock_gettime() e / o QueryPerformanceCounter .

Si noti che l’istruzione RDTSC è specifica per x86. QueryPerformanceCounter è solo per Windows. E clock_gettime() è solo POSIX. Di seguito ho introdotto due nuovi orologi: std::chrono::high_resolution_clock e std::chrono::system_clock , che, se si può assumere C ++ 11, ora sono multipiattaforma.

Innanzitutto, ecco come si crea un clock compatibile con C ++ 11 con le istruzioni di assemblaggio Intel rdtsc . Lo chiamerò x::clock :

 #include  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 duration; typedef std::chrono::time_point 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(hi) < < 32 | lo)); } }; } // x 

Tutto questo orologio fa contare i cicli della CPU e lo memorizza in un numero intero a 64 bit senza segno. Potrebbe essere necessario modificare la syntax del linguaggio assembly per il compilatore. Oppure il tuo compilatore può offrire un intrinseco che puoi usare (es. now() {return __rdtsc();} ).

Per build un orologio devi dargli la rappresentazione (tipo di archiviazione). È inoltre necessario fornire il periodo di clock, che deve essere una costante di tempo di compilazione, anche se la macchina può cambiare la velocità di clock in diverse modalità di alimentazione. E da quelli puoi facilmente definire la durata e il punto temporale "nativo" del tuo orologio in termini di questi fondamentali.

Se tutto ciò che si vuole fare è emettere il numero di segni di clock, non importa quale numero si attribuisce per il periodo di clock. Questa costante entra in gioco solo se si desidera convertire il numero di segni di clock in unità in tempo reale come i nanosecondi. E in tal caso, più preciso è il grado di fornire la velocità di clock, più accurata sarà la conversione in nanosecondi, (millisecondi, qualunque cosa).

Di seguito è riportato un codice di esempio che mostra come usare x::clock . In realtà ho messo il codice su un orologio come se volessi mostrare come puoi usare molti orologi diversi con la stessa syntax esatta. Questo particolare test mostra quale è l'overhead del loop quando si esegue ciò che si desidera impostare in un ciclo:

 #include  template  void test_empty_loop() { // Define real time units typedef std::chrono::duration picoseconds; // or: // typedef std::chrono::nanoseconds nanoseconds; // Define double-based unit of clock tick typedef std::chrono::duration 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(ticks_per_iter).count() < < "ps per iteration\n"; } 

La prima cosa che fa questo codice è creare un'unità "in tempo reale" per visualizzare i risultati. Ho scelto picosecondi, ma puoi scegliere qualsiasi unità che ti piace, sia in integrale che in virgola mobile. Ad esempio c'è un'unità pre-fatta std::chrono::nanoseconds che avrei potuto usare.

Come altro esempio, voglio stampare il numero medio di cicli di clock per iterazione come un punto mobile, quindi creo un'altra durata, basata sul doppio, che ha le stesse unità del tick (chiamato Cycle nel codice).

Il ciclo è clock::now() con le chiamate a clock::now() su entrambi i lati. Se vuoi dare un nome al tipo restituito da questa funzione, è:

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

(come chiaramente mostrato nell'esempio x::clock , ed è anche vero per gli orologi forniti dal sistema).

Per ottenere una durata in termini di zecche dell'orologio a virgola mobile, è sufficiente sottrarre i due punti temporali e, per ottenere il valore di iterazione, dividere tale durata per il numero di iterazioni.

È ansible ottenere il conteggio in qualsiasi periodo utilizzando la funzione membro count() . Questo restituisce la rappresentazione interna. Finalmente uso std::chrono::duration_cast per convertire il Cycle di durata alla durata dei picoseconds e stamparlo.

Utilizzare questo codice è semplice:

 int main() { std::cout < < "\nUsing rdtsc:\n"; test_empty_loop(); std::cout < < "\nUsing std::chrono::high_resolution_clock:\n"; test_empty_loop(); std::cout < < "\nUsing std::chrono::system_clock:\n"; test_empty_loop(); } 

In alto, eseguo il test utilizzando il nostro x::clock fatto in casa e confrontiamo questi risultati usando due degli orologi forniti dal sistema: std::chrono::high_resolution_clock e std::chrono::system_clock . Per me questo stampa:

 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 

Questo dimostra che ognuno di questi orologi ha un periodo di tick diverso, in quanto i tick per iterazione sono molto diversi per ogni clock. Tuttavia, quando convertito in un'unità di tempo nota (ad es. Picosecondi), ottengo approssimativamente lo stesso risultato per ogni orologio (il tuo chilometraggio può variare).

Nota come il mio codice è completamente privo di "costanti di conversione magiche". Infatti, ci sono solo due numeri magici nell'intero esempio:

  1. La velocità di clock della mia macchina per definire x::clock .
  2. Il numero di iterazioni da testare. Se la modifica di questo numero rende i risultati molto diversi, probabilmente dovresti aumentare il numero di iterazioni o svuotare il tuo computer dai processi concorrenti durante il test.

Con quel livello di accuratezza, sarebbe meglio ragionare nel tick della CPU piuttosto che nella chiamata di sistema come clock () . E non dimenticare che se ci vuole più di un nanosecondo per eseguire un’istruzione … avere una precisione di un nanosecondo è praticamente imansible.

Tuttavia, qualcosa del genere è un inizio:

Ecco il codice effettivo per recuperare il numero di cicli di clock della CPU 80×86 passati dall’ultima volta in cui è stata avviata la CPU. Funzionerà su Pentium e versioni successive (386/486 non supportato). Questo codice è in realtà specifico per MS Visual C ++, ma può essere probabilmente portato con estrema facilità a qualsiasi altra cosa, purché supporti l’assembly inline.

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

Questa funzione ha anche il vantaggio di essere estremamente veloce – di solito non richiede più di 50 cicli di CPU da eseguire.

Usando le cifre del tempo :
Se hai bisogno di tradurre i conteggi del clock in tempo reale, dividi i risultati per la velocità del tuo chip. Ricorda che il “rating” GHz è probabilmente leggermente diverso dalla velocità effettiva del tuo chip. Per verificare la vera velocità del chip, è ansible utilizzare diverse utilities molto buone o la chiamata Win32, QueryPerformanceFrequency ().

Per fare ciò correttamente è ansible utilizzare uno dei due modi, o andare con RDTSC o con clock_gettime() . Il secondo è circa 2 volte più veloce e ha il vantaggio di dare il giusto tempo assoluto. Si noti che per far funzionare correttamente RDTSC è necessario utilizzarlo come indicato (gli altri commenti su questa pagina RDTSC errori e possono produrre valori di sincronizzazione errati su determinati processori)

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

e per clock_gettime: (Ho scelto arbitrariamente la risoluzione al microsecondo)

 #include  

i tempi e i valori prodotti:

 Absolute values: rdtsc = 4571567254267600 clock_gettime = 1278605535506855 Processing time: (10000000 runs) rdtsc = 2292547353 clock_gettime = 1031119636 

Sto usando il seguente per ottenere i risultati desiderati:

 #include 

Per C ++ 11 , ecco un semplice wrapper:

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

Oppure per C ++ 03 su * 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_; }; 

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

In generale, per il tempo necessario per chiamare una funzione, si desidera farlo molte più volte di una sola volta. Se chiami la tua funzione solo una volta e ci vuole un tempo molto breve per eseguire, hai ancora il sovraccarico di chiamare effettivamente le funzioni del timer e non sai quanto tempo ci vuole.

Ad esempio, se si stima che la funzione potrebbe richiedere 800 ns per l’esecuzione, chiamarla in un ciclo dieci milioni di volte (che richiederà circa 8 secondi). Dividere il tempo totale di dieci milioni per ottenere il tempo per chiamata.

Puoi utilizzare la seguente funzione con gcc in esecuzione con processori x86:

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

con Digital Mars C ++:

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

che legge il timer ad alte prestazioni sul chip. Lo uso quando eseguo il profiling.

Utilizzando il metodo di Brock Adams, con una class semplice:

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

Esempio di utilizzo:

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

Risultato:

test ha richiesto: 0.0002 ms

Ha qualche funzione chiamata overhead, ma dovrebbe essere ancora più che abbastanza veloce 🙂

Se è necessaria una precisione inferiore al secondo, è necessario utilizzare estensioni specifiche del sistema e dovrà verificare con la documentazione per il sistema operativo. POSIX supporta fino a microsecondi con gettimeofday , ma nulla di più preciso in quanto i computer non hanno frequenze superiori a 1 GHz.

Se stai usando Boost, puoi controllare boost :: posix_time .

Sto usando il codice Borland qui è il codice ti_hund mi dà alcune volte un numero negativo ma i tempi sono abbastanza buoni.

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

È ansible utilizzare Embedded Profiler (gratuito per Windows e Linux) che ha un’interfaccia per un timer multipiattaforma (in un conteggio del ciclo del processore) e può fornire un numero di cicli al secondo:

 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; 

Il ricalcolo del conteggio del ciclo al tempo è probabilmente un’operazione pericolosa con processori moderni in cui la frequenza della CPU può essere modificata dynamicmente. Pertanto, per essere sicuri che i tempi di conversione siano corretti, è necessario correggere la frequenza del processore prima della profilazione.

Se questo è per Linux, ho usato la funzione “gettimeofday”, che restituisce una struttura che fornisce i secondi e i microsecondi dalla Epoch. È quindi ansible utilizzare timersub per sottrarre i due per ottenere la differenza nel tempo e convertirlo in qualsiasi precisione di tempo che si desidera. Tuttavia, specifichi i nanosecondi e sembra che la funzione clock_gettime () sia ciò che stai cercando. Mette il tempo in termini di secondi e nanosecondi nella struttura che ci passi sopra.

Cosa ne pensi di questo:

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

Ecco un bel timer Boost che funziona bene:

 //Stopwatch.hpp #ifndef STOPWATCH_HPP #define STOPWATCH_HPP //Boost #include  //Std #include  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::high_resolution_clock::now() - _start_time); return static_cast(nano_s.count()); } std::uint64_t Stopwatch::Get_elapsed_us() { boost::chrono::microseconds micro_s = boost::chrono::duration_cast(boost::chrono::high_resolution_clock::now() - _start_time); return static_cast(micro_s.count()); } std::uint64_t Stopwatch::Get_elapsed_ms() { boost::chrono::milliseconds milli_s = boost::chrono::duration_cast(boost::chrono::high_resolution_clock::now() - _start_time); return static_cast(milli_s.count()); } std::uint64_t Stopwatch::Get_elapsed_s() { boost::chrono::seconds sec = boost::chrono::duration_cast(boost::chrono::high_resolution_clock::now() - _start_time); return static_cast(sec.count()); } 

Copia & incolla-struct minimalista + uso pigro

Se l’idea è di avere una struttura minimalista che puoi usare per i test rapidi, allora ti suggerisco di copiare e incollare ovunque nel tuo file C ++ subito dopo #include . Questo è l’unico esempio in cui sacrifico la formattazione in stile Allman.

È ansible regolare facilmente la precisione nella prima riga della struttura. I valori possibili sono: nanoseconds , microseconds , milliseconds , seconds , minutes o hours .

 #include  struct MeasureTime { using precision = std::chrono::microseconds; std::vector times; std::chrono::steady_clock::time_point oneLast; void p() { std::cout < < "Mark " << times.size()/2 << ": " << std::chrono::duration_cast(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()); } }; 

uso

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

Risultato di output standard

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

Se vuoi un sumrio dopo l'esecuzione

Se vuoi il rapporto in seguito, perché ad esempio il tuo codice in mezzo scrive anche sullo standard output. Quindi aggiungi la seguente funzione alla struct (appena prima MeasureTime ()):

 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(tprev - tcur).count() < < std::endl; } tprev = tcur; ++i; } } 

Quindi puoi usare solo:

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

Che elencherà tutti i segni come prima, ma poi dopo l'altro codice viene eseguito. Nota che non dovresti usare entrambi ms() e mt() .