Misurazione del tempo di esecuzione di una funzione in C ++


138

Voglio scoprire quanto tempo impiega una determinata funzione nel mio programma C ++ per essere eseguita su Linux . Successivamente, voglio fare un confronto di velocità. Ho visto diverse funzioni temporali, ma alla fine ho avuto questa spinta. Crono:

process_user_cpu_clock, captures user-CPU time spent by the current process

Ora, non sono chiaro se utilizzo la funzione sopra, otterrò l'unico tempo che la CPU ha speso per quella funzione?

In secondo luogo, non sono riuscito a trovare alcun esempio di utilizzo della funzione sopra. Qualcuno può aiutarmi per favore come utilizzare la funzione di cui sopra?

PS: In questo momento, sto usando std::chrono::system_clock::now()per ottenere il tempo in secondi, ma questo mi dà risultati diversi a causa del diverso carico della CPU ogni volta.


2
Per Linux usa: clock_gettime.. gcc definisce altri orologi come: typedef system_clock steady_clock; typedef system_clock high_resolution_clock;su Windows, usa QueryPerformanceCounter.
Brandon,

Questa domanda non è un duplicato di questa o gli scenari rendono le soluzioni diverse?
Nord

Ho due implementazioni di una funzione e vorrei trovare quale funziona meglio.
Nord

Molto importante: assicurati di abilitare l'ottimizzazione . Codice Un-ottimizzato ha diversi colli di bottiglia di codice ottimizzato normale, e non non ti dice qualcosa di significativo. Guida all'ottimizzazione del ciclo C per l'assegnazione finale (con l'ottimizzazione del compilatore disabilitata) . E in generale il microbenchmarking presenta molte insidie, in particolare la mancata esecuzione prima di un ciclo di riscaldamento per la frequenza della CPU e gli errori di pagina: modo idiomatico di valutazione delle prestazioni? . E questa risposta
Peter Cordes il

Vedi anche Come giudichi le prestazioni di una funzione? per Google Benchmark che evita molte delle insidie ​​del rotolamento del proprio microbenchmark. Anche il benchmark Simple for () loop richiede lo stesso tempo con qualsiasi loop associato per maggiori informazioni su come l'ottimizzazione interagisce con i loop di benchmark e cosa fare al riguardo.
Peter Cordes,

Risposte:


264

È un metodo molto facile da usare in C ++ 11. Devi usare std::chrono::high_resolution_clockdall'intestazione <chrono>.

Usalo così:

#include <iostream>
#include <chrono>

void function()
{
    long long number = 0;

    for( long long i = 0; i != 2000000; ++i )
    {
       number += 5;
    }
}

int main()
{
    auto t1 = std::chrono::high_resolution_clock::now();
    function();
    auto t2 = std::chrono::high_resolution_clock::now();

    auto duration = std::chrono::duration_cast<std::chrono::microseconds>( t2 - t1 ).count();

    std::cout << duration;
    return 0;
}

Questo misurerà la durata della funzione.

NOTA: non sempre otterrai lo stesso timing per una funzione. Questo perché la CPU della tua macchina può essere meno o più utilizzata da altri processi in esecuzione sul tuo computer, così come la tua mente può essere più o meno concentrata quando risolvi un esercizio di matematica. Nella mente umana, possiamo ricordare la soluzione di un problema di matematica, ma per un computer lo stesso processo sarà sempre qualcosa di nuovo; quindi, come ho detto, non otterrai sempre lo stesso risultato!


Quando utilizzo questa funzione, alla prima esecuzione mi ha dato 118440535 microsecondi e alla seconda esecuzione della stessa funzione mi ha dato 83221031 microsecondi. Le due misurazioni del tempo non dovrebbero essere uguali quando misuro solo la durata di quella funzione?
Xara,

1
No. Il processore del tuo computer può essere usato meno o più. Il high_resolution_clockvi darà il tempo fisico e reale che la funzione prende per l'esecuzione. Quindi, nella tua prima corsa, la tua CPU veniva utilizzata meno della prossima. Per "usato" intendo quale altro lavoro dell'applicazione utilizza la CPU.
Victor,

1
Sì, se hai bisogno della media del tempo, questo è un buon modo per ottenerlo. eseguire tre corse e calcolare la media.
Victor,

3
Potresti postare il codice senza "usare lo spazio dei nomi" in generale. Rende più facile vedere cosa viene da dove.
Pupazzo di neve

1
Questo non dovrebbe essere un steady_clock? Non è possibile high_resolution_clockpotrebbe essere un orologio non monotonico?
Gillespie,

15

Ecco una funzione che misurerà il tempo di esecuzione di qualsiasi funzione passata come argomento:

#include <chrono>
#include <utility>

typedef std::chrono::high_resolution_clock::time_point TimeVar;

#define duration(a) std::chrono::duration_cast<std::chrono::nanoseconds>(a).count()
#define timeNow() std::chrono::high_resolution_clock::now()

template<typename F, typename... Args>
double funcTime(F func, Args&&... args){
    TimeVar t1=timeNow();
    func(std::forward<Args>(args)...);
    return duration(timeNow()-t1);
}

Esempio di utilizzo:

#include <iostream>
#include <algorithm>

typedef std::string String;

//first test function doing something
int countCharInString(String s, char delim){
    int count=0;
    String::size_type pos = s.find_first_of(delim);
    while ((pos = s.find_first_of(delim, pos)) != String::npos){
        count++;pos++;
    }
    return count;
}

//second test function doing the same thing in different way
int countWithAlgorithm(String s, char delim){
    return std::count(s.begin(),s.end(),delim);
}


int main(){
    std::cout<<"norm: "<<funcTime(countCharInString,"precision=10",'=')<<"\n";
    std::cout<<"algo: "<<funcTime(countWithAlgorithm,"precision=10",'=');
    return 0;
}

Produzione:

norm: 15555
algo: 2976

2
@ RestlessC0bra: è definita da implementaion, high_resolution_clockpuò essere un alias di system_clock(orologio da parete) steady_clocko un terzo orologio indipendente. Vedi i dettagli qui . Per l'orologio della CPU, std::clockpuò essere usato
Jahid

2
Due macro e un typedef globale - nessuno dei quali sicuro un singolo tasto - è certamente nulla che definirei elegante. Anche passare un oggetto funzione e inoltrare perfettamente gli argomenti separatamente è un po 'eccessivo (e nel caso di funzioni sovraccariche anche scomodo), quando puoi semplicemente richiedere che il codice temporale sia inserito in una lambda. Ma bene, purché passare argomenti sia facoltativo.
MikeMB,

2
E questa è una giustificazione per violare ogni linea guida sulla denominazione delle macro? Non li prefissi, non usi maiuscole, scegli un nome molto comune che ha un'alta probabilità di scontrarsi con un simbolo locale e soprattutto: perché stai usando una macro (invece di una funzione )? E mentre ci siamo: perché stai restituendo la durata come doppio che rappresenta i nanosecondi in primo luogo? Probabilmente dovremmo essere d'accordo sul fatto che non siamo d'accordo. La mia opinione originale è: "Questo non è ciò che definirei un codice elegante".
MikeMB,

1
Il problema è che non sono coperti. Ciò di cui sono preoccupato è che tali macro finiscono in un file di intestazione che viene (forse indirettamente parte di una libreria) incluso nel mio codice. Se vuoi avere un assaggio di ciò che accade se i nomi comuni sono usati per le macro, inclusi windows.hin un progetto c ++ non banale. Per quanto riguarda assertinnanzitutto: "quod licet iovi non licet bovi";). In secondo luogo, non tutte le decisioni nella biblioteca standard (a volte risalenti a decenni fa) sono in realtà considerate una buona idea dagli standard moderni. C'è un motivo per cui il progettista di moduli c ++ si impegna molto a non esportare macro per impostazione predefinita.
MikeMB,

2
@Jahid: grazie. In tal caso, considera i miei commenti nulli e nulli.
MikeMB,

9

semplice programma per trovare un tempo di esecuzione della funzione.

#include <iostream>
#include <ctime> // time_t
#include <cstdio>

void function()
{
     for(long int i=0;i<1000000000;i++)
     {
        // do nothing
     }
}

int main()
{

time_t begin,end; // time_t is a datatype to store time values.

time (&begin); // note time before execution
function();
time (&end); // note time after execution

double difference = difftime (end,begin);
printf ("time taken for function() %.2lf seconds.\n", difference );

return 0;
}

6
è molto impreciso, mostra solo pochi secondi, ma non millisecondi
utente25

7

Nel libro di Scott Meyers ho trovato un esempio di espressione lambda generica universale che può essere utilizzata per misurare il tempo di esecuzione della funzione. (C ++ 14)

auto timeFuncInvocation = 
    [](auto&& func, auto&&... params) {
        // get time before function invocation
        const auto& start = std::chrono::high_resolution_clock::now();
        // function invocation using perfect forwarding
        std::forward<decltype(func)>(func)(std::forward<decltype(params)>(params)...);
        // get time after function invocation
        const auto& stop = std::chrono::high_resolution_clock::now();
        return stop - start;
     };

Il problema è che si misura solo un'esecuzione, quindi i risultati possono essere molto diversi. Per ottenere un risultato affidabile è necessario misurare un numero elevato di esecuzioni. Secondo la conferenza di Andrei Alexandrescu alla conferenza code :: dive 2015 - Scrivere Fast Code I:

Tempo misurato: tm = t + tq + tn + a

dove:

tm - tempo misurato (osservato)

t - l'ora effettiva di interesse

tq - tempo aggiunto dal rumore di quantizzazione

tn - tempo aggiunto da varie fonti di rumore

a - tempo di overhead (misurazione, loop, funzioni di chiamata)

Secondo quanto ha detto più avanti nella lezione, dovresti prendere un minimo di questo gran numero di esecuzioni come risultato. Ti incoraggio a guardare la lezione in cui spiega perché.

Inoltre c'è un'ottima libreria di google - https://github.com/google/benchmark . Questa libreria è molto semplice da usare e potente. Puoi dare un'occhiata ad alcune lezioni di Chandler Carruth su YouTube dove sta usando questa libreria in pratica. Ad esempio CppCon 2017: Chandler Carruth “Going Nowhere Faster”;

Esempio di utilizzo:

#include <iostream>
#include <chrono>
#include <vector>
auto timeFuncInvocation = 
    [](auto&& func, auto&&... params) {
        // get time before function invocation
        const auto& start = high_resolution_clock::now();
        // function invocation using perfect forwarding
        for(auto i = 0; i < 100000/*largeNumber*/; ++i) {
            std::forward<decltype(func)>(func)(std::forward<decltype(params)>(params)...);
        }
        // get time after function invocation
        const auto& stop = high_resolution_clock::now();
        return (stop - start)/100000/*largeNumber*/;
     };

void f(std::vector<int>& vec) {
    vec.push_back(1);
}

void f2(std::vector<int>& vec) {
    vec.emplace_back(1);
}
int main()
{
    std::vector<int> vec;
    std::vector<int> vec2;
    std::cout << timeFuncInvocation(f, vec).count() << std::endl;
    std::cout << timeFuncInvocation(f2, vec2).count() << std::endl;
    std::vector<int> vec3;
    vec3.reserve(100000);
    std::vector<int> vec4;
    vec4.reserve(100000);
    std::cout << timeFuncInvocation(f, vec3).count() << std::endl;
    std::cout << timeFuncInvocation(f2, vec4).count() << std::endl;
    return 0;
}

EDIT: ovviamente devi sempre ricordare che il tuo compilatore può ottimizzare qualcosa o no. Strumenti come perf possono essere utili in questi casi.


Interessante: qual è il vantaggio di utilizzare un lambda qui su un modello di funzione?
user48956

1
La differenza principale sarebbe che si tratta di un oggetto richiamabile ma in effetti è possibile ottenere qualcosa di molto simile con il modello variadic e std :: result_of_t.
Krzysztof Sommerfeld,

@KrzysztofSommerfeld Come fare questo per i metodi di funzione, quando passo il timing (Object.Method1) restituisce l'errore "sintassi non standard; usa '&' per creare un puntatore al membro"
RobinAtTech

timeFuncInvocation ([& objectName] (auto && ... args) {objectName.methodName (std :: forward <decltype (args)> (args) ...);}, arg1, arg2, ...); o ommita e firma prima di objectName (quindi avrai una copia dell'oggetto)
Krzysztof Sommerfeld

4

Modo semplice per C ++ precedente o C:

#include <time.h> // includes clock_t and CLOCKS_PER_SEC

int main() {

    clock_t start, end;

    start = clock();
    // ...code to measure...
    end = clock();

    double duration_sec = double(end-start)/CLOCKS_PER_SEC;
    return 0;
}

La precisione dei tempi in secondi è 1.0/CLOCKS_PER_SEC


1
Questo non è portatile. Misura l'ora del processore su Linux e l'ora dell'orologio su Windows.
BugSquasher

2
  • È un metodo molto facile da usare in C ++ 11.
  • Possiamo usare std :: chrono :: high_resolution_clock dall'intestazione
  • Possiamo scrivere un metodo per stampare il tempo di esecuzione del metodo in una forma molto leggibile.

Ad esempio, per trovare tutti i numeri primi tra 1 e 100 milioni, sono necessari circa 1 minuto e 40 secondi. Quindi i tempi di esecuzione vengono stampati come:

Execution Time: 1 Minutes, 40 Seconds, 715 MicroSeconds, 715000 NanoSeconds

Il codice è qui:

#include <iostream>
#include <chrono>

using namespace std;
using namespace std::chrono;

typedef high_resolution_clock Clock;
typedef Clock::time_point ClockTime;

void findPrime(long n, string file);
void printExecutionTime(ClockTime start_time, ClockTime end_time);

int main()
{
    long n = long(1E+8);  // N = 100 million

    ClockTime start_time = Clock::now();

    // Write all the prime numbers from 1 to N to the file "prime.txt"
    findPrime(n, "C:\\prime.txt"); 

    ClockTime end_time = Clock::now();

    printExecutionTime(start_time, end_time);
}

void printExecutionTime(ClockTime start_time, ClockTime end_time)
{
    auto execution_time_ns = duration_cast<nanoseconds>(end_time - start_time).count();
    auto execution_time_ms = duration_cast<microseconds>(end_time - start_time).count();
    auto execution_time_sec = duration_cast<seconds>(end_time - start_time).count();
    auto execution_time_min = duration_cast<minutes>(end_time - start_time).count();
    auto execution_time_hour = duration_cast<hours>(end_time - start_time).count();

    cout << "\nExecution Time: ";
    if(execution_time_hour > 0)
    cout << "" << execution_time_hour << " Hours, ";
    if(execution_time_min > 0)
    cout << "" << execution_time_min % 60 << " Minutes, ";
    if(execution_time_sec > 0)
    cout << "" << execution_time_sec % 60 << " Seconds, ";
    if(execution_time_ms > 0)
    cout << "" << execution_time_ms % long(1E+3) << " MicroSeconds, ";
    if(execution_time_ns > 0)
    cout << "" << execution_time_ns % long(1E+6) << " NanoSeconds, ";
}

0

Ecco un eccellente modello di classe solo intestazione per misurare il tempo trascorso di una funzione o di qualsiasi blocco di codice:

#ifndef EXECUTION_TIMER_H
#define EXECUTION_TIMER_H

template<class Resolution = std::chrono::milliseconds>
class ExecutionTimer {
public:
    using Clock = std::conditional_t<std::chrono::high_resolution_clock::is_steady,
                                     std::chrono::high_resolution_clock,
                                     std::chrono::steady_clock>;
private:
    const Clock::time_point mStart = Clock::now();

public:
    ExecutionTimer() = default;
    ~ExecutionTimer() {
        const auto end = Clock::now();
        std::ostringstream strStream;
        strStream << "Destructor Elapsed: "
                  << std::chrono::duration_cast<Resolution>( end - mStart ).count()
                  << std::endl;
        std::cout << strStream.str() << std::endl;
    }    

    inline void stop() {
        const auto end = Clock::now();
        std::ostringstream strStream;
        strStream << "Stop Elapsed: "
                  << std::chrono::duration_cast<Resolution>(end - mStart).count()
                  << std::endl;
        std::cout << strStream.str() << std::endl;
    }

}; // ExecutionTimer

#endif // EXECUTION_TIMER_H

Ecco alcuni usi di esso:

int main() {
    { // empty scope to display ExecutionTimer's destructor's message
         // displayed in milliseconds
         ExecutionTimer<std::chrono::milliseconds> timer;

         // function or code block here

         timer.stop();

    } 

    { // same as above
        ExecutionTimer<std::chrono::microseconds> timer;

        // code block here...

        timer.stop();
    }

    {  // same as above
       ExecutionTimer<std::chrono::nanoseconds> timer;

       // code block here...

       timer.stop();

    }

    {  // same as above
       ExecutionTimer<std::chrono::seconds> timer;

       // code block here...

       timer.stop();

    }              

    return 0;
}

Poiché la classe è un modello, possiamo specificare facilmente in modo reale come vogliamo che il nostro tempo venga misurato e visualizzato. Questo è un modello di classe di utilità molto utile per eseguire la marcatura al banco ed è molto facile da usare.


Personalmente, la stop()funzione membro non è necessaria perché il distruttore ferma il timer per te.
Casey,

@Casey Il design della classe non richiede necessariamente la funzione di arresto, tuttavia è lì per un motivo specifico. Il costrutto predefinito quando si crea l'oggetto prima di test codeavviare il timer. Quindi dopo test codeaver utilizzato esplicitamente l'oggetto timer e chiamare il suo metodo di arresto. Devi invocarlo manualmente quando vuoi stopil timer. La classe non accetta alcun parametro. Inoltre, se hai usato questa classe proprio come ho mostrato, vedrai che tra la chiamata obj.stope la sua è trascorso un tempo minimo destructor.
Francis Cugler,

@Casey ... Questo permette anche di avere più oggetti timer nello stesso ambito, non che uno ne avrebbe davvero bisogno, ma solo un'altra opzione praticabile.
Francis Cugler,

Questo esempio non può essere compilato nel modulo presentato. L'errore è legato a "nessuna corrispondenza per l'operatore << ..."!
Celdor

@Celdor devi includere opportunamente; come <chrono>?
Francis Cugler,

0

Consiglio di usare il steady_clockquale è garantito per essere monotonico, a differenza high_resolution_clock.

#include <iostream>
#include <chrono>

using namespace std;

unsigned int stopwatch()
{
    static auto start_time = chrono::steady_clock::now();

    auto end_time = chrono::steady_clock::now();
    auto delta    = chrono::duration_cast<chrono::microseconds>(end_time - start_time);

    start_time = end_time;

    return delta.count();
}

int main() {
  stopwatch(); //Start stopwatch
  std::cout << "Hello World!\n";
  cout << stopwatch() << endl; //Time to execute last line
  for (int i=0; i<1000000; i++)
      string s = "ASDFAD";
  cout << stopwatch() << endl; //Time to execute for loop
}

Produzione:

Hello World!
62
163514

0

Puoi avere una classe semplice che può essere utilizzata per questo tipo di misurazioni.

class duration_printer {
public:
    duration_printer() : __start(std::chrono::high_resolution_clock::now()) {}
    ~duration_printer() {
        using namespace std::chrono;
        high_resolution_clock::time_point end = high_resolution_clock::now();
        duration<double> dur = duration_cast<duration<double>>(end - __start);
        std::cout << dur.count() << " seconds" << std::endl;
    }
private:
    std::chrono::high_resolution_clock::time_point __start;
};

L'unica cosa che è necessario fare è creare un oggetto nella tua funzione all'inizio di quella funzione

void veryLongExecutingFunction() {
    duration_calculator dc;
    for(int i = 0; i < 100000; ++i) std::cout << "Hello world" << std::endl;
}

int main() {
    veryLongExecutingFunction();
    return 0;
}

e basta. La classe può essere modificata in base alle proprie esigenze.


0

Poiché nessuna delle risposte fornite è molto accurata o fornisce risultati riproducibili, ho deciso di aggiungere un link al mio codice con precisione al di sotto dei nanosecondi e statistiche scientifiche.

Si noti che questo funzionerà solo per misurare il codice che richiede un tempo (molto) breve (ovvero alcuni cicli di clock a qualche migliaio): se durano così a lungo da essere probabilmente interrotti da qualche interruzione -heh- , quindi non è chiaramente possibile dare un risultato riproducibile e preciso; la conseguenza è che la misurazione non finisce mai: vale a dire, continua a misurare fino a quando non è statisticamente sicura al 99,9% che abbia la risposta giusta che non accade mai su una macchina che ha altri processi in esecuzione quando il codice impiega troppo tempo.

https://github.com/CarloWood/cwds/blob/master/benchmark.h#L40


0

Se si desidera proteggere il tempo e le righe di codice, è possibile trasformare la misurazione del tempo di esecuzione della funzione in una macro di una riga:

a) Implementare una classe di misurazione del tempo come già suggerito sopra (ecco la mia implementazione per Android):

class MeasureExecutionTime{
private:
    const std::chrono::steady_clock::time_point begin;
    const std::string caller;
public:
    MeasureExecutionTime(const std::string& caller):caller(caller),begin(std::chrono::steady_clock::now()){}
    ~MeasureExecutionTime(){
        const auto duration=std::chrono::steady_clock::now()-begin;
        LOGD("ExecutionTime")<<"For "<<caller<<" is "<<std::chrono::duration_cast<std::chrono::milliseconds>(duration).count()<<"ms";
    }
};

b) Aggiungi una comoda macro che utilizza l'attuale nome della funzione come TAG (usare una macro qui è importante, altrimenti __FUNCTION__valuterà MeasureExecutionTimeinvece della funzione che vuoi misurare

#ifndef MEASURE_FUNCTION_EXECUTION_TIME
#define MEASURE_FUNCTION_EXECUTION_TIME const MeasureExecutionTime measureExecutionTime(__FUNCTION__);
#endif

c) Scrivi la tua macro all'inizio della funzione che vuoi misurare. Esempio:

 void DecodeMJPEGtoANativeWindowBuffer(uvc_frame_t* frame_mjpeg,const ANativeWindow_Buffer& nativeWindowBuffer){
        MEASURE_FUNCTION_EXECUTION_TIME
        // Do some time-critical stuff 
}

Che risulterà nel seguente output:

ExecutionTime: For DecodeMJPEGtoANativeWindowBuffer is 54ms

Si noti che questo (come tutte le altre soluzioni suggerite) misurerà il tempo tra il momento in cui è stata chiamata la funzione e il momento in cui è tornata, non necessariamente il tempo in cui la CPU ha eseguito la funzione. Tuttavia, se non si apportano modifiche allo scheduler per sospendere il codice in esecuzione chiamando sleep () o simili, non vi è alcuna differenza tra.

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.