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


101

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

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

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


il codice sopra si calcola in secondi, voglio ottenere la risposta in nano secondi ...
gagneet

È necessario aggiungere la piattaforma alla domanda (e preferibilmente anche al titolo) per ottenere una buona risposta.
Patrick Johnmeyer

Oltre a ottenere il tempo, è necessario cercare i problemi con il microbenchmarking (che è estremamente complesso): è improbabile che eseguire una sola esecuzione e ottenere il tempo all'inizio e alla fine dia una precisione sufficiente.
Blaisorblade

@ Blaisorblade: Soprattutto da quando ho scoperto in alcuni dei miei test che clock()non è così veloce come pensavo.
Mooing Duck

Risposte:


83

Ciò che altri hanno pubblicato sull'esecuzione ripetuta della funzione in un ciclo è corretto.

Per Linux (e BSD) vuoi usare clock_gettime () .

#include <sys/time.h>

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

Per le finestre si desidera utilizzare QueryPerformanceCounter . E qui c'è di più su QPC

Apparentemente c'è un problema noto con QPC su alcuni chipset, quindi potresti voler assicurarti di non avere quei chipset. Inoltre, anche alcuni AMD dual core possono 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 il PC di uno dei core "in modo casuale" (il PC a volte salta un po 'indietro), a meno che non si installi appositamente il pacchetto driver dual core AMD per risolvere il problema. Non abbiamo notato altre CPU dual + core con problemi simili (p4 dual, p4 ht, core2 dual, core2 quad, phenom quad).

EDIT 2013/07/16:

Sembra che ci sia qualche controversia sull'efficacia di QPC in determinate circostanze, come dichiarato in http://msdn.microsoft.com/en-us/library/windows/desktop/ee417693(v=vs.85).aspx

... Mentre QueryPerformanceCounter e QueryPerformanceFrequency in genere si adattano a più processori, bug nel BIOS o nei driver possono far sì che queste routine restituiscano valori diversi mentre il thread si sposta da un processore a un altro ...

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

Questo articolo mostra che Windows 7 può determinare se i processori hanno un TSC invariante e, in caso contrario, ricorre a un timer esterno. http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html La sincronizzazione tra i processori è ancora un problema.

Altre letture fini relative ai timer:

Vedere i commenti per maggiori dettagli.


1
Ho visto l'inclinazione del clock TSC su un vecchio PC dual Xeon, ma non così grave come su un Athlon X2 con la rampa di clock C1 abilitata. Con la rampa di clock C1, l'esecuzione di un'istruzione HLT rallenta il clock, facendo sì che il TSC sui core inattivi aumenti più lentamente rispetto ai core attivi.
bk1e

6
CLOCK_MONOTONIC funziona sulle versioni di Linux che ho a disposizione.
Bernard,

1
@Bernard - Deve essere aggiunto di recente dall'ultima volta che l'ho guardato. Grazie per il testa a testa.
addolorato

3
In effetti, è necessario utilizzare CLOCK_MONOTONIC_RAW, se disponibile, per ottenere l'ora dell'hardware non regolata da NTP.

Come discusso qui, corretta attuazione QPC non utilizzare il contatore TSC, almeno quando è noto per essere inaffidabili: stackoverflow.com/q/510462/53974
Blaisorblade

69

Questa nuova risposta utilizza la funzionalità di C ++ 11 <chrono>. Mentre ci sono altre risposte che mostrano come usare <chrono>, nessuna di esse mostra come usare <chrono>con la RDTSCstruttura menzionata in molte delle altre risposte qui. Quindi ho pensato di mostrare come usare RDTSCcon <chrono>. Inoltre dimostrerò come si può modellare il codice di test sull'orologio in modo da poter passare rapidamente tra RDTSCe le funzionalità dell'orologio integrate nel sistema (che saranno probabilmente basate su clock(), clock_gettime()e / oQueryPerformanceCounter .

Notare che l' RDTSCistruzione è specifica per x86. QueryPerformanceCounterè solo Windows. Ed clock_gettime()è solo POSIX. Di seguito presento due nuovi orologi:std::chrono::high_resolution_clock e std::chrono::system_clock, che, se puoi assumere C ++ 11, ora sono multipiattaforma.

Innanzitutto, ecco come creare un clock compatibile con C ++ 11 dall'istruzione di rdtscassemblaggio Intel . Lo chiamerò x::clock:

#include <chrono>

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<rep, period> duration;
    typedef std::chrono::time_point<clock>     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<rep>(hi) << 32 | lo));
    }
};

}  // x

Tutto ciò che fa questo orologio è contare i cicli della CPU e memorizzarli in un numero intero a 64 bit senza segno. Potrebbe essere necessario modificare la sintassi del linguaggio assembly per il compilatore. Oppure il tuo compilatore potrebbe offrire un intrinseco che puoi usare al suo posto (ad esempio now() {return __rdtsc();}).

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

Se tutto ciò che vuoi fare è visualizzare il numero di tick dell'orologio, non importa quale numero dai per il periodo dell'orologio. Questa costante entra in gioco solo se si desidera convertire il numero di battiti dell'orologio in un'unità in tempo reale come i nanosecondi. E in quel caso, più preciso sei in 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 utilizzare x::clock. In realtà ho modellato il codice sull'orologio perché vorrei mostrare come è possibile utilizzare molti orologi diversi con la stessa identica sintassi. Questo particolare test mostra qual è l'overhead di loop quando si esegue ciò che si desidera cronometrare in un loop:

#include <iostream>

template <class clock>
void
test_empty_loop()
{
    // Define real time units
    typedef std::chrono::duration<unsigned long long, std::pico> picoseconds;
    // or:
    // typedef std::chrono::nanoseconds nanoseconds;
    // Define double-based unit of clock tick
    typedef std::chrono::duration<double, typename clock::period> 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<picoseconds>(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 preferisci, sia integrale che in virgola mobile. Ad esempio, c'è un'unità prefabbricata std::chrono::nanosecondsche avrei potuto usare.

Come altro esempio, voglio stampare il numero medio di cicli di clock per iterazione come virgola mobile, quindi creo un'altra durata, basata su double, che ha le stesse unità del tick dell'orologio (chiamato Cyclenel codice).

Il ciclo è programmato con le chiamate clock::now()su entrambi i lati. Se vuoi nominare il tipo restituito da questa funzione è:

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

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

Per ottenere una durata in termini di tick di clock in virgola mobile, è sufficiente sottrarre i due punti temporali e per ottenere il valore per iterazione, dividere tale durata per il numero di iterazioni.

È possibile ottenere il conteggio in qualsiasi durata utilizzando la count()funzione membro. Ciò restituisce la rappresentazione interna. Infine uso std::chrono::duration_castper convertire la durata Cyclein durata picosecondse stamparla.

Usare questo codice è semplice:

int main()
{
    std::cout << "\nUsing rdtsc:\n";
    test_empty_loop<x::clock>();

    std::cout << "\nUsing std::chrono::high_resolution_clock:\n";
    test_empty_loop<std::chrono::high_resolution_clock>();

    std::cout << "\nUsing std::chrono::system_clock:\n";
    test_empty_loop<std::chrono::system_clock>();
}

Sopra eseguo il test usando il nostro fatto in casa x::clocke confronto questi risultati con l'utilizzo di due degli orologi forniti dal sistema: std::chrono::high_resolution_clocke 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

Ciò mostra che ciascuno di questi orologi ha un periodo di tick diverso, poiché i tick per iterazione sono molto diversi per ogni orologio. Tuttavia, se convertito in un'unità di tempo nota (ad esempio 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 magica". In effetti, 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 su cui eseguire il test. Se la modifica di questo numero fa variare notevolmente i risultati, probabilmente dovresti aumentare il numero di iterazioni o svuotare il computer dai processi concorrenti durante il test.

5
Con "RDTSC è solo Intel", ti riferisci veramente all'architettura x86 e ai derivati, vero? I chip AMD, Cyrix, Transmeta x86 hanno le istruzioni , mentre i processori Intel RISC e ARM no.
Ben Voigt

1
@BenVoigt: +1 Sì, la tua correzione è abbastanza corretta, grazie.
Howard Hinnant

1
In che modo la limitazione della CPU influirà su questo? La velocità di clock non cambia in base al carico della CPU?
Tejas Kale

@TejasKale: Questo è descritto nella risposta nei due paragrafi consecutivi che iniziano con "Per costruire un orologio tu ...". In genere il codice di temporizzazione non misura il lavoro che blocca un thread (ma può farlo). E così in genere la tua CPU non rallenta. Ma se stai misurando il codice che coinvolge sleep, mutex lock, condition_variable wait, ecc., rdtscÈ probabile che l' orologio abbia conversioni imprecise su altre unità. È una buona idea impostare le misurazioni in modo da poter facilmente modificare e confrontare gli orologi (come mostrato in questa risposta).
Howard Hinnant

27

Con quel livello di accuratezza, sarebbe meglio ragionare sul tick della CPU piuttosto che su una chiamata di sistema come clock () . E non dimenticare che se ci vuole più di un nanosecondo per eseguire un'istruzione ... avere una precisione al nanosecondo è praticamente impossibile.

Tuttavia, qualcosa del genere è un inizio:

Ecco il codice effettivo per recuperare il numero di tick di clock della CPU 80x86 passati dall'ultimo avvio della CPU. Funzionerà su Pentium e versioni successive (386/486 non supportato). Questo codice è in realtà specifico per MS Visual C ++, ma può essere probabilmente molto facile portato su 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 sono necessari più di 50 cicli della CPU per essere eseguita.

Utilizzo delle cifre di temporizzazione :
se è necessario tradurre i conteggi dell'orologio in tempo reale trascorso, dividere i risultati per la velocità di clock del chip. Ricorda che il GHz "nominale" potrebbe essere leggermente diverso dalla velocità effettiva del tuo chip. Per controllare la velocità reale del tuo chip, puoi usare molte ottime utilità o la chiamata Win32, QueryPerformanceFrequency ().


grazie per le informazioni, questo è utile. Non ho pensato ai cicli della CPU per calcolare il tempo, penso che sia un ottimo punto da tenere a mente :-)
gagneet

4
L'utilizzo di QueryPerformanceFrequency () per trasformare i conteggi TSC in tempo trascorso potrebbe non funzionare. QueryPerformanceCounter () utilizza HPET (High Precision Event Timer) su Vista, se disponibile. Utilizza il timer di gestione dell'alimentazione ACPI se l'utente aggiunge / USEPMTIMER a boot.ini.
bk1e

23

Per farlo correttamente puoi usare uno dei due modi, andare con RDTSCo con clock_gettime(). Il secondo è circa 2 volte più veloce e ha il vantaggio di dare il giusto tempo assoluto. Nota che per RDTSCfunzionare correttamente devi usarlo come indicato (altri commenti in questa pagina contengono errori e potrebbero produrre valori di temporizzazione errati su alcuni 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 in microsecondi)

#include <time.h>
#include <sys/timeb.h>
// needs -lrt (real-time lib)
// 1970-01-01 epoch UTC time, 1 mcs resolution (divide by 1M to get time_t)
uint64_t ClockGetTime()
{
    timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    return (uint64_t)ts.tv_sec * 1000000LL + (uint64_t)ts.tv_nsec / 1000LL;
}

i tempi ed i valori prodotti:

Absolute values:
rdtsc           = 4571567254267600
clock_gettime   = 1278605535506855

Processing time: (10000000 runs)
rdtsc           = 2292547353
clock_gettime   = 1031119636

22

Sto usando quanto segue per ottenere i risultati desiderati:

#include <time.h>
#include <iostream>
using namespace std;

int main (int argc, char** argv)
{
    // reset the clock
    timespec tS;
    tS.tv_sec = 0;
    tS.tv_nsec = 0;
    clock_settime(CLOCK_PROCESS_CPUTIME_ID, &tS);
    ...
    ... <code to check for the time to be put here>
    ...
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tS);
    cout << "Time taken is: " << tS.tv_sec << " " << tS.tv_nsec << endl;

    return 0;
}

2
Ho downvoted perché cercando di applicare questo codice ho dovuto prima google perché il timespec non è definito. Poi ho dovuto cercare su Google che cos'è POSIX ... e così come l'ho capito, questo codice non è rilevante per gli utenti Windows che hanno cosa attenersi alla libreria standard.
Daniel Katz

8

Per C ++ 11 , ecco un semplice wrapper:

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

O 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


5

In generale, per calcolare quanto tempo ci vuole per chiamare una funzione, è meglio farlo molte più volte di una sola volta. Se chiami la tua funzione solo una volta e l'esecuzione richiede pochissimo tempo, hai ancora l'overhead di chiamare effettivamente le funzioni timer e non sai quanto tempo ci vuole.

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


in realtà, sto cercando di ottenere le prestazioni dell'API per una chiamata particolare. per ogni corsa, potrebbe dare un tempo diverso, questo potrebbe influenzare il grafico che creo per il miglioramento delle prestazioni ... da qui il tempo in nano secondi. ma sì, questa è un'ottima idea, la prenderò in considerazione.
gagneet

5

È possibile utilizzare la seguente funzione con gcc in esecuzione su 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 durante la profilazione.


2
questo è utile, controllerò se il processore è x86, dato che sto usando un apple mac per la sperimentazione ... grazie :-)
gagneet

1
Quali valori dovrebbe dare l'utente per alto e basso? Perché definisci una macro all'interno del corpo di una funzione? Inoltre, ulonglong, presumibilmente typedef trasformato in unsigned long long, non è un tipo standard. Mi piacerebbe usarlo ma non sono sicuro di come;)
Joseph Garvin,

1
unsigned long non è la cosa giusta da usare sotto linux. Potresti prendere in considerazione l'utilizzo di int invece fintanto che long e long long sono entrambi a 64 bit su Linux a 64 bit.
Marius

3
Il contatore TSC è oggigiorno spesso inaffidabile: cambia la sua velocità su molti processori quando la frequenza viene modificata ed è incoerente tra i diversi core, quindi il TSC non cresce sempre.
Blaisorblade

1
@Mario: ho implementato il tuo commento, usando unsigned intcome tipo interno.
Blaisorblade

3

Se è necessaria una precisione inferiore al secondo, è necessario utilizzare estensioni specifiche del sistema e sarà necessario verificare con la documentazione del sistema operativo. POSIX supporta fino a microsecondi con gettimeofday , ma niente di più preciso dato che i computer non avevano frequenze superiori a 1GHz.

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


voglio mantenere il codice portatile, vedrà la libreria boost e verificherà se posso raggrupparlo con il codice. grazie :-)
gagneet

3

Sto usando il codice Borland qui è il codice ti_hund mi dà alcune volte un numero negativo ma il tempismo è abbastanza buono.

#include <dos.h>

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

3

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

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:

il test ha richiesto: 0.0002 ms

Ha qualche sovraccarico di chiamata di funzione, ma dovrebbe essere ancora più che abbastanza veloce :)


3

Puoi usare Embedded Profiler (gratuito per Windows e Linux) che ha un'interfaccia per un timer multipiattaforma (in un conteggio di cicli del processore) e può darti 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 in base al tempo è probabilmente un'operazione pericolosa con i processori moderni in cui la frequenza della CPU può essere modificata dinamicamente. Pertanto, per essere sicuri che i tempi convertiti siano corretti, è necessario fissare la frequenza del processore prima della profilazione.


2

Se questo è per Linux, sto usando la funzione "gettimeofday", che restituisce una struttura che fornisce i secondi ei microsecondi da Epoch. È quindi possibile utilizzare timersub per sottrarre i due per ottenere la differenza di tempo e convertirla in qualsiasi precisione di tempo si desideri. 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 gli passi.


clock_gettime () dovrebbe fare il trucco per ora. proverò a usare lo stesso per il mio scopo ...
gagneet

2

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

2

Ecco un bel timer Boost che funziona bene:

//Stopwatch.hpp

#ifndef STOPWATCH_HPP
#define STOPWATCH_HPP

//Boost
#include <boost/chrono.hpp>
//Std
#include <cstdint>

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::nanoseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(nano_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_us()
{
    boost::chrono::microseconds micro_s = boost::chrono::duration_cast<boost::chrono::microseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(micro_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_ms()
{
    boost::chrono::milliseconds milli_s = boost::chrono::duration_cast<boost::chrono::milliseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(milli_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_s()
{
    boost::chrono::seconds sec = boost::chrono::duration_cast<boost::chrono::seconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(sec.count());
}

2

Copia e incolla minimalista-struttura + utilizzo pigro

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

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

#include <chrono>
struct MeasureTime
{
    using precision = std::chrono::microseconds;
    std::vector<std::chrono::steady_clock::time_point> times;
    std::chrono::steady_clock::time_point oneLast;
    void p() {
        std::cout << "Mark " 
                << times.size()/2
                << ": " 
                << std::chrono::duration_cast<precision>(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();
m.t(); // Mark 1: next time, and print difference with previous mark
doFnc2();
m.t(); // Mark 2: next time, and print difference with previous mark
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.t(); // prints 'Mark 3: 123123' etc...

Risultato di output standard

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

Se vuoi un riepilogo dopo l'esecuzione

Se vuoi il rapporto dopo, perché ad esempio il tuo codice in mezzo scrive anche sullo standard output. Quindi aggiungi la seguente funzione alla struttura (subito prima di 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<precision>(tprev - tcur).count()
                    << std::endl;
        }
        tprev = tcur;
        ++i;
    }
}

Quindi puoi semplicemente usare:

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

Che elencherà tutti i segni proprio come prima, ma poi dopo che l'altro codice è stato eseguito. Nota che non dovresti usare sia m.s()e m.t().


Funziona perfettamente con OpenMP su Ubuntu 16.04. Grazie mille, questa dovrebbe essere la migliore risposta IMO!
Íhor Mé
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.