Medindo o tempo de execução de uma função em C ++


138

Quero descobrir quanto tempo uma determinada função leva no meu programa C ++ para executar no Linux . Depois, quero fazer uma comparação de velocidade. Eu vi várias funções de tempo, mas acabei com isso do impulso. Crono:

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

Agora, não estou claro se eu uso a função acima, receberei o único tempo que a CPU gastou nessa função?

Em segundo lugar, não encontrei nenhum exemplo de uso da função acima. Alguém pode me ajudar como usar a função acima?

PS: No momento, estou usando std::chrono::system_clock::now()para obter tempo em segundos, mas isso me dá resultados diferentes devido à carga de CPU diferente a cada vez.


2
Para uso no Linux: clock_gettime.. gcc define outros relógios como: typedef system_clock steady_clock; typedef system_clock high_resolution_clock;no Windows, use QueryPerformanceCounter.
Brandon

Esta questão não é uma duplicata desta ou os cenários tornam as soluções diferentes?
Northerner

Eu tenho duas implementações de uma função e gostaria de descobrir qual funciona melhor.
Northerner

Muito importante: ative a otimização . O código não otimizado possui gargalos diferentes do código otimizado normal e não informa nada significativo. Ajuda de otimização do loop C para atribuição final (com a otimização do compilador desativada) . E, em geral, o microbenchmarking tem muitas armadilhas, principalmente a falha em fazer um loop de aquecimento primeiro para a frequência da CPU e falhas de página: maneira linguística de avaliação de desempenho? . E esta resposta
Peter Cordes

Veja também Como você avaliaria o desempenho de uma função? para o Google Benchmark, que evita muitas das armadilhas de lançar sua própria marca de microbench. Também simples para () loop de referência leva o mesmo tempo com qualquer circuito ligado para saber mais sobre como interage otimização com loops de referência, e o que fazer sobre isso.
Peter Cordes

Respostas:


264

É um método muito fácil de usar no C ++ 11. Você tem que usar std::chrono::high_resolution_clockdo <chrono>cabeçalho.

Use-o assim:

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

Isso medirá a duração da função.

NOTA: Você nem sempre terá o mesmo tempo para uma função. Isso ocorre porque a CPU da sua máquina pode ser menos ou mais usada por outros processos em execução no seu computador, assim como sua mente pode estar mais ou menos concentrada quando você resolve um exercício de matemática. Na mente humana, podemos lembrar a solução de um problema de matemática, mas para um computador o mesmo processo sempre será algo novo; assim, como eu disse, você nem sempre obterá o mesmo resultado!


Quando uso essa função, na primeira execução, ele me fornece 118440535 microssegundos e na segunda execução da mesma função, me fornece 83221031 microssegundos. As duas medidas de tempo não devem ser iguais quando estou medindo apenas a duração dessa função?
Xara

1
Não. O processador do seu computador pode ser usado menos ou mais. O high_resolution_clocklhe dará o tempo físico e real que a sua função leva para executar. Portanto, em sua primeira execução, sua CPU estava sendo usada menos do que na próxima execução. Por "usado", quero dizer que outro trabalho de aplicativo usa a CPU.
Victor

1
Sim, se você precisar da média do tempo, é uma boa maneira de obtê-lo. faça três corridas e calcule a média.
Victor

3
Você poderia postar código sem "usar o namespace" em geral. Torna mais fácil ver o que vem de onde.
21919 Boneco de neve

1
Isso não deveria ser um steady_clock? Não é possível high_resolution_clockque seja um relógio não monotônico?
Gillespie

15

Aqui está uma função que medirá o tempo de execução de qualquer função passada como argumento:

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

Exemplo de uso:

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

Resultado:

norm: 15555
algo: 2976

2
@ RestlessC0bra: Sua implementação é definida, high_resolution_clockpode ser um pseudônimo de system_clock(relógio de parede) steady_clockou um terceiro relógio independente. Veja detalhes aqui . Para relógio da CPU, std::clockpode ser usado
Jahid

2
Duas macros e um typedef global - nenhum dos quais segura um único pressionamento de tecla - certamente não são nada que eu chamaria de elegante.Também passar um objeto de função e encaminhar perfeitamente os argumentos separadamente é um exagero (e, no caso de funções sobrecarregadas, mesmo inconveniente), quando você pode exigir apenas que o código cronometrado seja inserido em uma lambda. Mas bem, desde que a passagem de argumentos seja opcional.
MikeMB 3/03

2
E essa é uma justificativa para violar toda e qualquer diretriz sobre a nomeação de macros? Você não os prefixa, não usa letras maiúsculas, escolhe um nome muito comum que tem uma alta probabilidade de colidir com algum símbolo local e acima de tudo: Por que você está usando uma macro (em vez de uma função) )? E enquanto estamos nisso: Por que você está retornando a duração como um duplo representando nanossegundos em primeiro lugar? Provavelmente deveríamos concordar que discordamos. Minha opinião original é: "Não é isso que eu chamaria de código elegante".
MikeMB 8/17/17

1
O problema é que eles estão sem escopo. O que me preocupa é que essas macros acabem em um arquivo de cabeçalho que é incluído (talvez indiretamente como parte de uma biblioteca) no meu código. Se você quiser ter uma amostra do que acontece se nomes comuns são usados ​​para macros, incluídos windows.hem um projeto c ++ não trivial. Em assertprimeiro lugar: "quod licet iovi non licet bovi";). Segundo, nem todas as decisões na biblioteca padrão (às vezes datam de décadas) são realmente consideradas uma boa idéia pelos padrões modernos. Há uma razão pela qual o designer de módulos do c ++ se esforça muito para não exportar macros por padrão.
MikeMB

2
@Jahid: Obrigado. Nesse caso, considere meus comentários nulos e nulos.
MikeMB 20/06

9

programa simples para encontrar o tempo de execução de uma função.

#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
é muito impreciso, mostra apenas segundos, mas não milissegundos #
user25

7

No livro de Scott Meyers, encontrei um exemplo de expressão lambda genérica universal que pode ser usada para medir o tempo de execução da função. (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;
     };

O problema é que você mede apenas uma execução para que os resultados possam ser muito diferentes. Para obter um resultado confiável, você deve medir um grande número de execução. De acordo com a palestra de Andrei Alexandrescu na conferência code :: dive 2015 - Writing Fast Code I:

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

Onde:

tm - tempo medido (observado)

t - o tempo real de interesse

tq - tempo adicionado pelo ruído de quantização

tn - tempo adicionado por várias fontes de ruído

tempo de overhead (funções de medição, loop, chamada)

De acordo com o que ele disse mais adiante na palestra, você deve ter no mínimo esse grande número de execuções como resultado. Encorajo-vos a olhar para a palestra em que ele explica o porquê.

Também há uma biblioteca muito boa do google - https://github.com/google/benchmark . Esta biblioteca é muito simples de usar e poderosa. Você pode conferir algumas palestras de Chandler Carruth no youtube, onde ele está usando esta biblioteca na prática. Por exemplo, CppCon 2017: Chandler Carruth “Indo a lugar nenhum mais rápido”;

Exemplo de uso:

#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: Claro que você sempre precisa se lembrar de que seu compilador pode otimizar algo ou não. Ferramentas como perf podem ser úteis nesses casos.


Interessante - qual é o benefício de usar um lambda aqui sobre um modelo de função?
user48956

1
A principal diferença seria que ele é um objeto que pode ser chamado, mas, de fato, você pode obter algo muito semelhante com o modelo variável e std :: result_of_t.
Krzysztof Sommerfeld

@KrzysztofSommerfeld Como fazer isso para métodos de função, quando passo o tempo (Object.Method1), ele retorna erro "sintaxe fora do padrão; use '&' para criar um ponteiro para o membro"
RobinAtTech

timeFuncInvocation ([& objectName] (auto && ... args) {objectName.methodName (std :: forward <decltype (args)> (args) ...);}, arg1, arg2, ...); ou omitir & sinal antes objectName (então você vai ter uma cópia do objeto)
Krzysztof Sommerfeld

4

Maneira fácil para C ++ mais antigo ou 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;
}

A precisão do tempo em segundos é 1.0/CLOCKS_PER_SEC


1
Isto não é portátil. Ele mede o tempo do processador no Linux e o tempo do relógio no Windows.
BugSquasher 30/03/19

2
  • É um método muito fácil de usar no C ++ 11.
  • Podemos usar std :: chrono :: high_resolution_clock do cabeçalho
  • Podemos escrever um método para imprimir o tempo de execução do método de uma forma muito legível.

Por exemplo, para encontrar todos os números primos entre 1 e 100 milhões, leva aproximadamente 1 minuto e 40 segundos. Portanto, o tempo de execução é impresso como:

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

O código está aqui:

#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

Aqui está um excelente modelo de classe apenas de cabeçalho para medir o tempo decorrido de uma função ou qualquer bloco de código:

#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

Aqui estão alguns usos dele:

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

Como a classe é um modelo, podemos especificar com facilidade como queremos que nosso tempo seja medido e exibido. Este é um modelo de classe de utilidade muito útil para fazer marcações em bancada e é muito fácil de usar.


Pessoalmente, a stop()função de membro não é necessária porque o destruidor interrompe o cronômetro para você.
22418 Casey

@ Casey O design da classe não precisa necessariamente da função de parada, mas existe por um motivo específico. A construção padrão ao criar o objeto antes de você test codeiniciar o cronômetro. Depois, test codevocê explicitamente usa o objeto timer e chama seu método stop. Você precisa invocá-lo manualmente quando quiser stopo cronômetro. A classe não aceita nenhum parâmetro. Além disso, se você usou essa classe, como mostrei, verá que há um tempo mínimo entre a chamada obj.stope a chamada destructor.
Francis Cugler

@ Casey ... Isso também permite ter vários objetos de timer dentro do mesmo escopo, não que um deles realmente precise, mas apenas outra opção viável.
Francis Cugler

Este exemplo não pode ser compilado no formulário apresentado. O erro está relacionado a "nenhuma correspondência para o operador << ..."!
precisa saber é

@Celdor você precisa incluir apropriadamente; como <chrono>?
Francis Cugler

0

Eu recomendo usar o steady_clockque é garantido para ser monotônico, ao contrário 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
}

Resultado:

Hello World!
62
163514

0

Você pode ter uma classe simples que pode ser usada para esse tipo de medida.

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

A única coisa que você precisa fazer é criar um objeto em sua função no início dessa função.

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

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

e é isso. A classe pode ser modificada para atender às suas necessidades.


0

Como nenhuma das respostas fornecidas é muito precisa ou fornece resultados reproduzíveis, decidi adicionar um link ao meu código que possui precisão de menos de nanossegundos e estatísticas científicas.

Observe que isso funcionará apenas para medir o código que leva um tempo (muito) curto para ser executado (ou seja, alguns ciclos de relógio para alguns milhares): se eles executarem tanto tempo que provavelmente serão interrompidos por alguma interrupção -heh- , claramente não é possível fornecer um resultado reproduzível e preciso; a conseqüência disso é que a medição nunca termina: ou seja, continua a medir até ter estatisticamente 99,9% de certeza de que possui a resposta certa, o que nunca acontece em uma máquina que possui outros processos em execução quando o código demora muito.

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


0

Se você deseja economizar tempo e linhas de código, pode tornar a medição do tempo de execução da função uma macro de uma linha:

a) Implemente uma classe de medição de tempo, como já sugerido acima (aqui está minha implementação para o 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) Adicione uma macro conveniente que use o nome da função atual como TAG (usar uma macro aqui é importante; caso contrário, __FUNCTION__será avaliado em MeasureExecutionTimevez da função que você deseja medir

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

c) Escreva sua macro no início da função que você deseja medir. Exemplo:

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

O que resultará na seguinte saída:

ExecutionTime: For DecodeMJPEGtoANativeWindowBuffer is 54ms

Observe que isso (como todas as outras soluções sugeridas) medirá o tempo entre o momento em que sua função foi chamada e quando ela retornou, não necessariamente o tempo em que sua CPU estava executando a função. No entanto, se você não alterar o agendador para suspender o código em execução chamando sleep () ou similar, não há diferença entre eles.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.