Função de temporizador para fornecer tempo em nanossegundos usando C ++

101

Desejo calcular o tempo que levou para uma API retornar um valor. O tempo gasto para tal ação é de nano segundos. Como a API é uma classe / função C ++, estou usando o timer.h para calcular o mesmo:

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

O código acima fornece o tempo em segundos. Como faço para obter o mesmo em nano segundos e com mais precisão?

gagneet
fonte
o código acima é calculado em segundos, quero obter a resposta em nano segundos ...
gagneet
É necessário adicionar a plataforma à pergunta (e de preferência ao título também) para obter uma boa resposta.
Patrick Johnmeyer
Além de obter o tempo, é necessário procurar problemas com o microbenchmarking (que é extremamente complexo) - apenas fazer uma execução e obter o tempo no início e no fim dificilmente dará precisão suficiente.
Blaisorblade
@Blaisorblade: Especialmente depois que descobri em alguns de meus testes que clock()não é tão rápido quanto eu pensava.
Mooing Duck

Respostas:

83

O que outros postaram sobre como executar a função repetidamente em um loop está correto.

Para Linux (e BSD), você deseja usar 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
}

Para janelas, você deseja usar o QueryPerformanceCounter . E aqui está mais sobre QPC

Aparentemente, há um problema conhecido com o QPC em alguns chipsets, portanto, verifique se você não tem esses chipset. Além disso, alguns AMDs de núcleo duplo também podem causar problemas . Veja a segunda postagem do sebbbi, onde ele afirma:

QueryPerformanceCounter () e QueryPerformanceFrequency () oferecem uma resolução um pouco melhor, mas têm problemas diferentes. Por exemplo, no Windows XP, todas as CPUs AMD Athlon X2 dual core retornam o PC de qualquer um dos núcleos "aleatoriamente" (o PC às vezes pula um pouco para trás), a menos que você instale especialmente o pacote de driver AMD dual core para corrigir o problema. Não notamos nenhum outro processador dual + core com problemas semelhantes (p4 dual, p4 ht, core2 dual, core2 quad, phenom quad).

EDITAR 16/07/2013:

Parece que há alguma controvérsia sobre a eficácia do QPC sob certas circunstâncias, conforme declarado em http://msdn.microsoft.com/en-us/library/windows/desktop/ee417693(v=vs.85).aspx

... Embora QueryPerformanceCounter e QueryPerformanceFrequency normalmente se ajustem para vários processadores, bugs no BIOS ou drivers podem resultar nessas rotinas retornando valores diferentes conforme o thread se move de um processador para outro ...

No entanto, esta resposta do StackOverflow https://stackoverflow.com/a/4588605/34329 afirma que o QPC deve funcionar bem em qualquer sistema operacional MS após o service pack 2 do Win XP.

Este artigo mostra que o Windows 7 pode determinar se o (s) processador (es) têm um TSC invariável e recorre a um cronômetro externo se não tiver. http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html A sincronização entre processadores ainda é um problema.

Outra boa leitura relacionada a temporizadores:

Veja os comentários para mais detalhes.

lamentar
fonte
1
Eu vi o clock do TSC distorcer em um PC Xeon dual mais antigo, mas não tão ruim quanto em um Athlon X2 com rampa de clock C1 habilitada. Com a aceleração do clock C1, a execução de uma instrução HLT desacelera o clock, fazendo com que o TSC nos núcleos inativos aumente mais lentamente do que nos núcleos ativos.
bk1e
6
CLOCK_MONOTONIC funciona nas versões do Linux que tenho disponíveis.
Bernard
1
@Bernard - Deve ter sido adicionado desde a última vez que li isso. Obrigado pelo aviso.
luto em
3
Na verdade, você tem que usar CLOCK_MONOTONIC_RAW, se estiver disponível, para obter o tempo de hardware não ajustado pelo NTP.
Conforme discutido aqui, a implementação correta do QPC não usa o contador TSC, pelo menos onde ele não é confiável: stackoverflow.com/q/510462/53974
Blaisorblade
69

Essa nova resposta usa os recursos do C ++ 11 <chrono>. Embora existam outras respostas que mostram como usar <chrono>, nenhuma delas mostra como usar <chrono>com a RDTSCfacilidade mencionada em várias das outras respostas aqui. Então pensei em mostrar como usar RDTSCcom <chrono>. Além disso eu vou demonstrar como você pode templatize o código de teste no relógio de modo que você pode alternar rapidamente entre RDTSCe seu sistema está integrado em instalações de clock (que provavelmente será baseado em clock(), clock_gettime()e / ou QueryPerformanceCounter.

Observe que a RDTSCinstrução é específica para x86. QueryPerformanceCounteré apenas para Windows. E clock_gettime()é apenas POSIX. A seguir, apresento dois novos relógios: std::chrono::high_resolution_clockand std::chrono::system_clock, que, se você pode assumir o C ++ 11, agora são multiplataforma.

Primeiro, aqui está como você cria um relógio compatível com C ++ 11 a partir das rdtscinstruções de montagem da Intel . Eu vou chamá-lo 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

Tudo o que esse relógio faz é contar os ciclos da CPU e armazená-los em um inteiro não assinado de 64 bits. Você pode precisar ajustar a sintaxe da linguagem assembly para o seu compilador. Ou seu compilador pode oferecer um intrínseco que você pode usar em seu lugar (por exemplo now() {return __rdtsc();}).

Para construir um relógio, você deve dar a ele a representação (tipo de armazenamento). Você também deve fornecer o período de clock, que deve ser uma constante de tempo de compilação, mesmo que sua máquina possa alterar a velocidade do clock em diferentes modos de energia. E, a partir deles, você pode definir facilmente a duração e o ponto de tempo "nativo" do seu relógio em termos desses fundamentos.

Se tudo o que você deseja fazer é mostrar o número de tiques do relógio, realmente não importa o número fornecido para o período do relógio. Essa constante só entra em ação se você quiser converter o número de tiques do relógio em alguma unidade de tempo real, como nanossegundos. E, nesse caso, quanto mais preciso você for capaz de fornecer a velocidade do clock, mais precisa será a conversão em nanossegundos (milissegundos, o que for).

Abaixo está o código de exemplo que mostra como usar x::clock. Na verdade, criei um modelo para o código do relógio, pois gostaria de mostrar como você pode usar muitos relógios diferentes com a mesma sintaxe exata. Este teste específico está mostrando qual é a sobrecarga do loop ao executar o que você deseja cronometrar em um 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";
}

A primeira coisa que esse código faz é criar uma unidade de "tempo real" para exibir os resultados. Eu escolhi picossegundos, mas você pode escolher qualquer unidade que desejar, seja integral ou baseada em ponto flutuante. Como exemplo, há uma std::chrono::nanosecondsunidade pré-fabricada que eu poderia ter usado.

Como outro exemplo, quero imprimir o número médio de ciclos de clock por iteração como um ponto flutuante, então crio outra duração, com base em double, que tem as mesmas unidades que o tique do relógio (chamado Cycleno código).

O loop é cronometrado com chamadas para clock::now()ambos os lados. Se você deseja nomear o tipo retornado por esta função, é:

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

(como mostrado claramente no x::clockexemplo, e também é verdadeiro para os relógios fornecidos pelo sistema).

Para obter uma duração em termos de tiques do relógio de ponto flutuante, basta subtrair os dois pontos no tempo e, para obter o valor por iteração, divida essa duração pelo número de iterações.

Você pode obter a contagem em qualquer duração usando a count()função de membro. Isso retorna a representação interna. Finalmente, costumo std::chrono::duration_castconverter a duração Cycleem duração picosecondse imprimi-la.

Usar este código é simples:

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

Acima, eu exercito o teste usando nosso feito em casa x::clocke comparo esses resultados com o uso de dois relógios fornecidos pelo sistema: std::chrono::high_resolution_clocke std::chrono::system_clock. Para mim, isso imprime:

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

Isso mostra que cada um desses relógios tem um período de tique diferente, já que os tiques por iteração são muito diferentes para cada relógio. No entanto, quando convertido para uma unidade de tempo conhecida (por exemplo, picossegundos), obtenho aproximadamente o mesmo resultado para cada relógio (sua milhagem pode variar).

Observe como meu código está completamente livre de "constantes de conversão mágicas". Na verdade, existem apenas dois números mágicos em todo o exemplo:

  1. A velocidade do relógio da minha máquina para definir x::clock.
  2. O número de iterações a serem testadas. Se a alteração desse número fizer seus resultados variarem muito, você provavelmente deve aumentar o número de iterações ou esvaziar o computador de processos concorrentes durante o teste.
Howard Hinnant
fonte
5
Por "RDTSC é somente Intel", você está realmente se referindo à arquitetura x86 e derivados, não é? Os chips AMD, Cyrix, Transmeta x86 têm a instrução , e os processadores Intel RISC e ARM não.
Ben Voigt
1
@BenVoigt: +1 Sim, sua correção está correta, obrigado.
Howard Hinnant,
1
Como o afogamento da CPU afetará isso? A velocidade do clock não muda com base na carga da CPU?
Tejas Kale
@TejasKale: Isso é descrito na resposta nos dois parágrafos consecutivos começando com "Para construir um relógio você ...". Normalmente, o código de temporização não mede o trabalho que bloqueia um thread (mas pode). E então, normalmente, sua CPU não acelera. Mas se você estiver medindo códigos envolvendo sleep, mutex lock, condition_variable wait, etc, o rdtscrelógio provavelmente terá conversões imprecisas para outras unidades. É uma boa ideia configurar suas medições de forma que você possa facilmente alterar e comparar os relógios (como mostrado nesta resposta).
Howard Hinnant
27

Com esse nível de precisão, seria melhor raciocinar no tique da CPU do que na chamada do sistema como clock () . E não se esqueça de que se levar mais de um nanossegundo para executar uma instrução ... ter uma precisão de nanossegundo é praticamente impossível.

Ainda assim, algo assim é um começo:

Aqui está o código real para recuperar o número de tiques do clock da CPU 80x86 passados ​​desde a última inicialização da CPU. Ele funcionará no Pentium e superior (386/486 não compatível). Este código é, na verdade, específico do MS Visual C ++, mas provavelmente pode ser facilmente transportado para qualquer outro lugar, desde que suporte montagem embutida.

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

}

Esta função também tem a vantagem de ser extremamente rápida - normalmente não leva mais de 50 ciclos de CPU para ser executada.

Usando os números de tempo :
Se você precisar traduzir a contagem do relógio em tempo decorrido real, divida os resultados pela velocidade do relógio do seu chip. Lembre-se de que o GHz "nominal" provavelmente será um pouco diferente da velocidade real do seu chip. Para verificar a velocidade real do seu chip, você pode usar vários utilitários muito bons ou a chamada do Win32, QueryPerformanceFrequency ().

VonC
fonte
obrigado pela informação, isso é útil. não pensei nos ciclos da CPU para calcular o tempo, acho que é um ponto muito bom para se ter em mente :-)
gagneet
4
Usar QueryPerformanceFrequency () para transformar contagens TSC em tempo decorrido pode não funcionar. QueryPerformanceCounter () usa o HPET (High Precision Event Timer) no Vista, quando disponível. Ele usa o temporizador de gerenciamento de energia ACPI se o usuário adicionar / USEPMTIMER ao boot.ini.
bk1e
23

Para fazer isso corretamente, você pode usar uma das duas maneiras, ir com RDTSCou com clock_gettime(). O segundo é cerca de 2 vezes mais rápido e tem a vantagem de fornecer o tempo absoluto correto. Observe que para RDTSCfuncionar corretamente, você precisa usá-lo conforme indicado (outros comentários nesta página contêm erros e podem resultar em valores de tempo incorretos em certos processadores)

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 para clock_gettime: (escolhi resolução de microssegundos arbitrariamente)

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

o tempo e os valores produzidos:

Absolute values:
rdtsc           = 4571567254267600
clock_gettime   = 1278605535506855

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

Estou usando o seguinte para obter os resultados desejados:

#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;
}
gagneet
fonte
2
Eu votei contra porque tentando aplicar este código, eu tive que primeiro google porque o timespec não está definido. Então eu tive que pesquisar no Google o que é POSIX ... e pelo que entendi, este código não é relevante para usuários do Windows que querem seguir a biblioteca padrão.
Daniel Katz
8

Para C ++ 11 , aqui está um wrapper simples:

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

Ou para C ++ 03 em * 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_;
};

Exemplo de uso:

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

De https://gist.github.com/gongzhitaao/7062087

gongzhitaao
fonte
5

Em geral, para cronometrar quanto tempo leva para chamar uma função, você deseja fazer isso muito mais vezes do que apenas uma vez. Se você chamar sua função apenas uma vez e levar um tempo muito curto para ser executada, você ainda terá a sobrecarga de realmente chamar as funções do temporizador e não saberá quanto tempo isso levará.

Por exemplo, se você estima que sua função pode levar 800 ns para ser executada, chame-a em um loop dez milhões de vezes (o que levará cerca de 8 segundos). Divida o tempo total por dez milhões para obter o tempo por chamada.

Greg Hewgill
fonte
Na verdade, estou tentando obter o desempenho da API para uma chamada específica. para cada corrida, pode dar um tempo diferente, isso pode afetar o gráfico que fiz para a melhoria de desempenho ... daí o tempo em nano segundos. mas sim, esta é uma ótima ideia, vou considerá-la.
gagneet
5

Você pode usar a seguinte função com gcc em execução em processadores 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;
}

com Digital Mars C ++:

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

que lê o temporizador de alto desempenho no chip. Eu uso isso ao fazer perfis.

Walter Bright
fonte
2
isso é útil, vou verificar se o processador é x86, já que estou usando um apple mac para experimentação ... obrigado :-)
gagneet
1
Que valores o usuário deve fornecer para alto e baixo? Por que você define uma macro dentro do corpo de uma função? Além disso, ulonglong, presumivelmente typedef'd para unsigned long long, não é um tipo padrão. Eu gostaria de usar isso, mas não tenho certeza de como;)
Joseph Garvin
1
Unsigned long não é a coisa certa para usar no Linux. Você pode querer considerar o uso de int em vez disso, já que long e long long são ambos de 64 bits no Linux de 64 bits.
Marius
3
O contador TSC hoje em dia muitas vezes não é confiável: ele muda sua velocidade em muitos processadores quando a frequência é alterada e é inconsistente em diferentes núcleos, portanto, o TSC nem sempre aumenta.
Blaisorblade
1
@Marius: Implementei seu comentário, usando unsigned intcomo tipo interno.
Blaisorblade
3

Se precisar de precisão de subsegundo, você precisará usar extensões específicas do sistema e terá que verificar a documentação do sistema operacional. POSIX suporta até microssegundos com gettimeofday , mas nada mais preciso já que os computadores não tinham frequências acima de 1GHz.

Se você estiver usando Boost, você pode verificar boost :: posix_time .

Raymond Martineau
fonte
quero manter o código portátil, verei a biblioteca boost e verificarei se posso agrupar isso com o código. obrigado :-)
gagneet
3

Estou usando o código Borland aqui é o código que ti_hund me dá algumas vezes um número negativo, mas o tempo é bastante bom.

#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
Paul J Moesman
fonte
3

Usando o método de Brock Adams, com uma classe simples:

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

Exemplo de uso:

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

Resultado:

teste realizado: 0,0002 ms

Tem alguma sobrecarga de chamada de função, mas ainda deve ser mais do que rápido o suficiente :)

Thomas
fonte
3

Você pode usar o Embedded Profiler (gratuito para Windows e Linux), que tem uma interface para um temporizador multiplataforma (em uma contagem de ciclos do processador) e pode fornecer um número de ciclos por segundos:

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;

O recálculo da contagem do ciclo no tempo é possivelmente uma operação perigosa com processadores modernos onde a frequência da CPU pode ser alterada dinamicamente. Portanto, para ter certeza de que os tempos convertidos estão corretos, é necessário fixar a frequência do processador antes de criar o perfil.

Mi-La
fonte
2

Se for para Linux, estou usando a função "gettimeofday", que retorna uma estrutura que fornece os segundos e microssegundos desde a época. Você pode então usar timersub para subtrair os dois para obter a diferença de tempo e convertê-lo para a precisão de tempo que desejar. No entanto, você especifica nanossegundos e parece que a função clock_gettime () é o que você está procurando. Ele coloca o tempo em termos de segundos e nanossegundos na estrutura que você passa para ele.

Will Mc
fonte
clock_gettime () deve fazer o truque por enquanto. tentarei usar o mesmo para o meu propósito ...
gagneet
2

O que você acha disso:

    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;
    }
icegood
fonte
2

Aqui está um bom temporizador de Boost que funciona bem:

//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());
}
Patrick K
fonte
2

Copiar e colar minimalista + uso lento

Se a ideia é ter uma estrutura minimalista que você possa usar para testes rápidos, então sugiro que você apenas copie e cole em qualquer lugar do seu arquivo C ++ logo após o#include 's. Este é o único caso em que sacrifico a formatação no estilo Allman.

Você pode ajustar facilmente a precisão na primeira linha da estrutura. Os valores possíveis são: nanoseconds, microseconds, milliseconds, seconds, minutes, ou 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...

Resultado de saída padrão

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

Se você quiser um resumo após a execução

Se você quiser o relatório depois, porque, por exemplo, seu código intermediário também grava na saída padrão. Em seguida, adicione a seguinte função à estrutura (logo antes de 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;
    }
}

Então você pode apenas usar:

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

Que listará todas as marcas como antes, mas depois que o outro código for executado. Observe que você não deve usar ambos m.s()e m.t().

Yeti
fonte
Funciona perfeitamente com OpenMP no Ubuntu 16.04. Muito obrigado, esta deve ser a melhor resposta IMO!
Íhor Mé