2013-04-01 14 views
5

Sto cercando di utilizzare chrono::steady_clock per misurare i frazionari secondi trascorsi tra un blocco di codice nel mio programma. Ho questo blocco di codice lavorare in LiveWorkSpace (http://liveworkspace.org/code/YT1I$9):Il tempo di misurazione produce valori di ritorno pari a 0 o 0,001

#include <chrono> 
#include <iostream> 
#include <vector> 

int main() 
{ 
    auto start = std::chrono::steady_clock::now(); 
    for (unsigned long long int i = 0; i < 10000; ++i) { 
     std::vector<int> v(i, 1); 
    } 
    auto end = std::chrono::steady_clock::now(); 

    auto difference = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count(); 

    std::cout << "seconds since start: " << ((double)difference/1000000); 
} 

Quando a implementare la stessa idea nel mio programma in questo modo:

auto start = std::chrono::steady_clock::now(); 
// block of code to time 
auto end = std::chrono::stead_clock::now(); 

auto difference = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() 

std::cout << "seconds since start: " << ((double) difference/1000000); 

Il programma solo stampare i valori di 0 e 0.001 . Dubito fortemente che il tempo di esecuzione per il mio blocco di codice sia sempre uguale a 0 o 1000 microsecondi, quindi qual è la spiegazione di questo arrotondamento e come posso eliminarlo in modo da ottenere i valori frazionari corretti?

Questo è un programma per Windows.

+0

Provare invece 'std :: chrono :: high_resolution_clock'. –

+0

@KerrekSB: Questo è un buon suggerimento, ma 'steady_clock :: now()' dovrebbe essere già preciso ai nanosecondi, o almeno ai tick (intervalli di 100 nanosecondi). Sospetto che ci sia un problema di matematica qui. –

+0

@RobertHarvey: Oh, forse le parentesi errate nel cast? –

risposta

2

Dopo aver eseguito alcuni test su MSVC2012, ho potuto confermare che gli orologi C++ 11 nell'implementazione di Microsoft non hanno una risoluzione sufficientemente alta. Vedere C++ header's high_resolution_clock does not have high resolution per un bug report relativo a questo problema.

Così, purtroppo per un timer risoluzione più alta, è necessario utilizzare boost::chrono o QueryPerformanceCounter direttamente in questo modo fino a quando non risolvere il bug:

#include <iostream> 
#include <Windows.h> 

int main() 
{ 
    LARGE_INTEGER frequency; 
    QueryPerformanceFrequency(&frequency); 

    LARGE_INTEGER start; 
    QueryPerformanceCounter(&start); 

    // Put code here to time 

    LARGE_INTEGER end; 
    QueryPerformanceCounter(&end); 

    // for microseconds use 1000000.0 
    double interval = static_cast<double>(end.QuadPart- start.QuadPart)/
         frequency.QuadPart; // in seconds 
    std::cout << interval; 
} 
+0

Ah, non c'è da stupirsi che non funzionasse correttamente. Mi piacerebbe capire di più sulla funzione 'QueryPerformanceFrequency()' e su ciò che effettivamente sta facendo. Devo chiamare sempre quella funzione prima di avviare il timer o è una funzione che deve essere richiamata una sola volta durante il programma? Abbiamo diviso la frequenza correttamente? I risultati ora mi stanno dando centinaia di secondi, il che non sembra neanche corretto. – raphnguyen

+0

@raphnguyen: 'QueryPerformanceFrequency' dovrebbe essere chiamato una sola volta durante il programma per ottenere la frequenza. L'esempio che ho mostrato era in 'microsecondi', se vuoi rimuovere i secondi' 1000000.0'. Ho cambiato il codice in secondi. –

+0

@raphnguyen: 'QueryPerformanceFrequency' restituisce la frequenza in conteggi al secondo, quindi se dividi il numero di conteggi per quello, ottieni secondi. –

5

Questa domanda ha già una buona risposta. Ma mi piacerebbe aggiungere un altro suggerimento:

Lavoro all'interno del quadro <chrono>. Costruisci il tuo orologio. Costruisci il tuo punto temporale. Costruisci la tua durata. Il framework <chrono> è molto personalizzabile. Lavorando all'interno di quel sistema, non solo impari lo std::chrono, ma quando il tuo venditore inizia a spedire gli orologi di cui sei soddisfatto, sarà banale trasferire il tuo codice dal tuo crono :: clock a mano a std::high_resolution_clock (o qualsiasi altra cosa).

Prima, però, un minorenne critica sulla tua codice originale:

std::cout << "seconds since start: " << ((double) difference/1000000); 

Ogni volta che ti vedi l'introduzione di costanti di conversione (come 1000000) per ottenere ciò che si vuole, non si sta usando chrono correttamente. Il tuo codice non è errato, solo fragile. Sei il sicuro di hai il giusto numero di zeri in quella costante ?!

Anche in questo semplice esempio si dovrebbe dire a te stesso:

Voglio vedere l'uscita in termini di secondi rappresentati da un doppio.

E quindi si dovrebbe usare chrono farlo per voi. E 'molto facile una volta che si impara:

typedef std::chrono::duration<double> sec; 
sec difference = end - start; 
std::cout << "seconds since start: " << difference.count() << '\n'; 

La prima riga crea un tipo con un periodo di 1 secondo, rappresentato da un doppio.

La seconda riga sottrae semplicemente i tuoi time_points e li assegna al tipo di durata personalizzato. La conversione dalle unità di steady_clock::time_point alla durata personalizzata (un doppio secondo) viene eseguita automaticamente dalla libreria chrono.Questo è molto più semplice:

auto difference = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() 

E poi finalmente basta stampare il risultato con la funzione .count() membro. Anche questo è molto più semplice rispetto:

std::cout << "seconds since start: " << ((double) difference/1000000); 


Ma dal momento che non sei felice con la precisione di std::chrono::steady_clock, e si ha accesso a QueryPerformanceCounter, si può fare di meglio. Puoi costruire il tuo orologio su QueryPerformanceCounter.

<disclaimer>

non ho un sistema Windows per testare il codice seguente.

</disclaimer>

struct my_clock 
{ 
    typedef double        rep; 
    typedef std::ratio<1>      period; 
    typedef std::chrono::duration<rep, period> duration; 
    typedef std::chrono::time_point<my_clock> time_point; 
    static const bool is_steady =    false; 

    static time_point now() 
    { 
     static const long long frequency = init_frequency(); 
     long long t; 
     QueryPerformanceCounter(&t); 
     return time_point(duration(static_cast<rep>(t)/frequency)); 
    } 
private: 
    static long long init_frequency() 
    { 
     long long f; 
     QueryPerformanceFrequency(&f); 
     return f; 
    } 
}; 

Dal momento che si voleva la vostra uscita in termini di un doppio secondo, ho fatto la rep di questo orologio un double e la period 1 secondo. Si potrebbe facilmente realizzare l'integrale rep e l'period qualche altra unità come microsecondi o nanosecondi. È sufficiente regolare i valori typedef e la conversione da QueryPerformanceCounter a duration in now().

E ora il codice può guardare molto come il tuo codice originale:

int main() 
{ 
    auto start = my_clock::now(); 
    for (unsigned long long int i = 0; i < 10000; ++i) { 
     std::vector<int> v(i, 1); 
    } 
    auto end = my_clock::now(); 

    auto difference = end - start; 
    std::cout << "seconds since start: " << difference.count() << '\n'; 
} 

Ma senza le costanti di conversione in codice a mano, e con (quello che spero è) la precisione sufficiente per la vostra esigenze. E con un molto più facile porting percorso per una futura implementazione std::chrono::steady_clock.

<chrono> è stato progettato per essere una libreria estendibile. Si prega di estenderlo. :-)

Problemi correlati