Função de timer para fornecer tempo em nano segundos usando C ++

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

#include  #include  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 indica o tempo em segundos. Como obtenho o mesmo em nano segundos e com mais precisão?

O que outros postaram sobre a execução repetida da function em um loop está correto.

Para Linux (e BSD) você quer usar clock_gettime () .

 #include  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 no QPC

Aparentemente, há um problema conhecido com o QPC em alguns chipsets, portanto, convém ter certeza de que você não possui esse chipset. Além disso, alguns AMDs de núcleo duplo também podem causar um problema . Veja o segundo post de 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 dual-core AMD Athlon X2 retornam o PC de qualquer um dos núcleos “aleatoriamente” (o PC às vezes salta um pouco para trás), a menos que você instale especialmente o pacote de driver AMD dual core para corrigir o problema. Nós não notamos nenhum outro dual + core tendo problemas similares (p4 dual, p4 ht, core2 dual, core2 quádruplo, phenom quad).

EDITAR 2013/07/16:

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

… Embora o QueryPerformanceCounter e o QueryPerformanceFrequency normalmente se ajustem a vários processadores, bugs no BIOS ou drivers podem fazer com que essas rotinas retornem valores diferentes à medida que o thread se move de um processador para outro …

No entanto, esta resposta StackOverflow https://stackoverflow.com/a/4588605/34329 afirma que o QPC deve funcionar bem em qualquer sistema operacional Microsoft após o Windows XP Service Pack 2.

Este artigo mostra que o Windows 7 pode determinar se o (s) processador (es) tem um TSC invariante e retorna a um timer externo, se não o fizer. http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html A synchronization entre processadores ainda é um problema.

Outra boa leitura relacionada aos timers:

Veja os comentários para mais detalhes.

Esta nova resposta usa o recurso do C ++ 11. Embora existam outras respostas que mostram como usar , nenhuma delas mostra como usar com o recurso RDTSC mencionado em várias outras respostas aqui. Então eu pensei em mostrar como usar RDTSC com . Além disso, demonstrarei como você pode modelar o código de teste no relógio para alternar rapidamente entre o RDTSC e os resources de relógio integrados do sistema (que provavelmente serão baseados em clock() , clock_gettime() e / ou QueryPerformanceCounter .

Observe que a instrução RDTSC é específica do x86. QueryPerformanceCounter é apenas o Windows. E clock_gettime() é apenas POSIX. Abaixo eu introduzo dois novos relógios: std::chrono::high_resolution_clock e std::chrono::system_clock , que, se você pode assumir o C ++ 11, agora são multi-plataforma.

Primeiro, aqui está como você cria um relógio compatível com C ++ 11 a partir da instrução de assembly Intel rdtsc . Eu chamarei de x::clock :

 #include  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 duration; typedef std::chrono::time_point 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(hi) << 32 | lo)); } }; } // x 

Tudo o que esse relógio faz é contar os ciclos da CPU e armazená-los em um inteiro de 64 bits sem sinal. Talvez seja necessário ajustar a syntax do assembly para seu compilador. Ou o seu compilador pode oferecer um intrínseco que você pode usar (por exemplo, now() {return __rdtsc();} ).

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

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

Abaixo está o código de exemplo que mostra como usar x::clock . Na verdade, eu modelei o código no relógio, pois gostaria de mostrar como você pode usar muitos relógios diferentes com a mesma syntax. Este teste em particular está mostrando qual é a sobrecarga do loop ao executar o que você deseja em um loop:

 #include  template  void test_empty_loop() { // Define real time units typedef std::chrono::duration picoseconds; // or: // typedef std::chrono::nanoseconds nanoseconds; // Define double-based unit of clock tick typedef std::chrono::duration 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(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 picosegundos, mas você pode escolher qualquer unidade que desejar, seja integral ou baseada em ponto flutuante. Por exemplo, há uma unidade std::chrono::nanoseconds pré-fabricada que eu poderia ter usado.

Como outro exemplo eu quero imprimir o número médio de ciclos de clock por iteração como um ponto flutuante, então eu crio outra duração, baseada em double, que tem as mesmas unidades que o tick do clock (chamada Cycle no código).

O loop é cronometrado com chamadas para clock::now() em ambos os lados. Se você quiser nomear o tipo retornado desta function, é:

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

(como mostrado claramente no exemplo x::clock , e também é verdade dos relógios fornecidos pelo sistema).

Para obter uma duração em termos de pulsos de clock do ponto flutuante, meramente subtrai os dois pontos de 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 function de membro count() . Isso retorna a representação interna. Finalmente eu uso std::chrono::duration_cast para converter o Cycle duração para os picoseconds duração e imprimi-lo.

Para usar este código é simples:

 int main() { std::cout << "\nUsing rdtsc:\n"; test_empty_loop(); std::cout << "\nUsing std::chrono::high_resolution_clock:\n"; test_empty_loop(); std::cout << "\nUsing std::chrono::system_clock:\n"; test_empty_loop(); } 

Acima eu exercito o teste usando nossa x::clock feita em casa, e comparo esses resultados usando dois dos relógios fornecidos pelo sistema: std::chrono::high_resolution_clock e 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 clocks tem um período de tick diferente, já que os ticks por iteração são muito diferentes para cada clock. No entanto, quando convertido para uma unidade de tempo conhecida (por exemplo, picossegundos), recebo 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ágica". De fato, existem apenas dois números mágicos em todo o exemplo:

  1. A velocidade do clock da minha máquina para definir x::clock .
  2. O número de iterações para testar. Se a alteração desse número fizer com que seus resultados variem muito, provavelmente você deverá aumentar o número de iterações ou esvaziar o computador de processos concorrentes durante o teste.

Com esse nível de precisão, seria melhor raciocinar no tick da CPU em vez de 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 nanossegundos é praticamente impossível.

Ainda assim, algo assim é um começo:

Aqui está o código real para recuperar o número de pulsos de clock da CPU de 80×86 passados ​​desde que a CPU foi iniciada pela última vez. Ele funcionará no Pentium e acima (386/486 não suportado). Este código é, na verdade, específico do MS Visual C ++, mas pode ser, provavelmente, muito fácil de portar para qualquer outra coisa, desde que suporte a 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); } 

Esta function também tem a vantagem de ser extremamente rápida – geralmente não leva mais de 50 ciclos de CPU para executar.

Usando as figuras de tempo :
Se você precisar converter as contagens de relógio em tempo real, divida os resultados pela velocidade do clock do seu chip. Lembre-se de que o GHz “classificado” provavelmente é 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 ().

Para fazer isso corretamente, você pode usar uma das duas maneiras, ou vá com RDTSC ou com clock_gettime() . O segundo é cerca de 2 vezes mais rápido e tem a vantagem de dar o tempo absoluto certo. Observe que para o RDTSC funcionar corretamente, é necessário usá-lo como indicado (outros comentários nesta página possuem erros e podem gerar valores de tempo incorretos em determinados 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: (eu escolhi a resolução de microssegundos arbitrariamente)

 #include  #include  // 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 timing e os valores produzidos:

 Absolute values: rdtsc = 4571567254267600 clock_gettime = 1278605535506855 Processing time: (10000000 runs) rdtsc = 2292547353 clock_gettime = 1031119636 

Eu estou usando o seguinte para obter os resultados desejados:

 #include  #include  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); ... ...  ... clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tS); cout << "Time taken is: " << tS.tv_sec << " " << tS.tv_nsec << endl; return 0; } 

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

 #include  #include  class Timer { public: Timer() : beg_(clock_::now()) {} void reset() { beg_ = clock_::now(); } double elapsed() const { return std::chrono::duration_cast (clock_::now() - beg_).count(); } private: typedef std::chrono::high_resolution_clock clock_; typedef std::chrono::duration > second_; std::chrono::time_point beg_; }; 

Ou para C ++ 03 no * 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

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

Por exemplo, se você estimar que sua function 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 em dez milhões para obter o tempo por chamada.

Você pode usar a seguinte function com o gcc rodando sob 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 o Digital Mars C ++:

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

que lê o timer de alto desempenho no chip. Eu uso isso ao fazer o perfil.

Usando o método de Brock Adams, com uma class 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 demorou: 0.0002 ms

Tem alguma function chamada sobrecarga, mas deve ser ainda mais do que rápido o suficiente 🙂

Se você precisar de precisão de subsegundos, será necessário usar extensões específicas do sistema e verificar a documentação do sistema operacional. O POSIX suporta até microssegundos com o gettimeofday , mas nada mais preciso, pois os computadores não têm freqüências acima de 1GHz.

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

Estou usando o código da Borland aqui é o código ti_hund me dá algumas vezes um negativnumber mas o tempo é bastante bom.

 #include  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 

Você pode usar o Embedded Profiler (gratuito para Windows e Linux) que possui uma interface para um timer de multiplataforma (em uma contagem de ciclos de processador) e pode fornecer vários ciclos por segundo:

 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 de ciclos para o 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 horários convertidos estão corretos, é necessário corrigir a freqüência do processador antes de criar o perfil.

Se isso é para o Linux, eu tenho usado a function “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 no tempo e convertê-lo para qualquer precisão de tempo desejada. No entanto, você especifica nanossegundos e parece que a function clock_gettime () é o que você está procurando. Ele coloca o tempo em termos de segundos e nanossegundos na estrutura que você passa para ele.

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

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

 //Stopwatch.hpp #ifndef STOPWATCH_HPP #define STOPWATCH_HPP //Boost #include  //Std #include  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::high_resolution_clock::now() - _start_time); return static_cast(nano_s.count()); } std::uint64_t Stopwatch::Get_elapsed_us() { boost::chrono::microseconds micro_s = boost::chrono::duration_cast(boost::chrono::high_resolution_clock::now() - _start_time); return static_cast(micro_s.count()); } std::uint64_t Stopwatch::Get_elapsed_ms() { boost::chrono::milliseconds milli_s = boost::chrono::duration_cast(boost::chrono::high_resolution_clock::now() - _start_time); return static_cast(milli_s.count()); } std::uint64_t Stopwatch::Get_elapsed_s() { boost::chrono::seconds sec = boost::chrono::duration_cast(boost::chrono::high_resolution_clock::now() - _start_time); return static_cast(sec.count()); } 

Cópia minimalista e pasta-struct + uso preguiçoso

Se a idéia é ter uma estrutura minimalista que você possa usar para testes rápidos, então eu sugiro que você copie e cole em qualquer lugar do seu arquivo C + + logo após o #include ‘s. Esta é a única instância em que eu sacrifico a formatação no estilo Allman.

Você pode facilmente ajustar a precisão na primeira linha da estrutura. Os valores possíveis são: nanoseconds , microseconds , milliseconds , seconds , minutes ou hours .

 #include  struct MeasureTime { using precision = std::chrono::microseconds; std::vector times; std::chrono::steady_clock::time_point oneLast; void p() { std::cout << "Mark " << times.size()/2 << ": " << std::chrono::duration_cast(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(); mt(); // Mark 1: next time, and print difference with previous mark doFnc2(); mt(); // Mark 2: next time, and print difference with previous mark doStuff = doMoreStuff(); andDoItAgain = doStuff.aoeuaoeu(); mt(); // 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 no meio também grava na saída padrão. Em seguida, adicione a seguinte function à 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(tprev - tcur).count() << std::endl; } tprev = tcur; ++i; } } 

Então você pode simplesmente usar:

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

Que listará todas as marcas como antes, mas depois que o outro código for executado. Note que você não deve usar ms() e mt() .