c++ - pausar - temporizador en c linux
FunciĆ³n de temporizador para proporcionar tiempo en nano segundos usando C++ (16)
Deseo calcular el tiempo que tardó una API en devolver un valor. El tiempo necesario para tal acción es en el espacio de nano segundos. Como la API es una clase / función C ++, estoy usando el timer.h para calcular lo mismo:
#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;
}
El código anterior da el tiempo en segundos. ¿Cómo obtengo lo mismo en nano segundos y con más precisión?
Minimalistic copy & paste-struct + uso lazy
Si la idea es tener una estructura minimalista que pueda usar para las pruebas rápidas, entonces le sugiero que solo copie y pegue en cualquier lugar de su archivo C ++ justo después de la #include
. Esta es la única instancia en la que sacrifico el formato estilo Allman.
Puede ajustar fácilmente la precisión en la primera línea de la estructura. Los valores posibles son: nanoseconds
, microseconds
, milliseconds
, seconds
, minutes
u 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 salida estándar
Mark 1: 123
Mark 2: 32
Mark 3: 433234
Si quieres un resumen después de la ejecución
Si luego desea el informe, porque su código intermedio también escribe en la salida estándar. A continuación, agregue la siguiente función a la estructura (justo 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;
}
}
Entonces puedes usar:
MeasureTime m;
doFnc1();
m.m();
doFnc2();
m.m();
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.m();
m.s();
Que mostrará una lista de todas las marcas como antes, pero luego se ejecutará el otro código. Tenga en cuenta que no debe usar tanto ms()
como mt()
.
Aquí hay un buen temporizador de Boost que funciona bien:
//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());
}
Con ese nivel de precisión, sería mejor razonar en el tic de la CPU que en la llamada al sistema como clock () . Y no olvide que si lleva más de un nanosegundo ejecutar una instrucción ... tener una precisión de nanosegundos es prácticamente imposible.
Aún así, algo así es un comienzo:
Aquí está el código real para recuperar el número de ticks de reloj de la CPU 80x86 aprobados desde la última vez que se inició la CPU. Funcionará en Pentium y versiones posteriores (386/486 no es compatible). Este código es realmente específico de MS Visual C ++, pero puede ser muy fácil de transportar a cualquier otra cosa, siempre y cuando sea compatible con el ensamblaje en línea.
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 función también tiene la ventaja de ser extremadamente rápida: normalmente no requiere más de 50 ciclos de CPU para ejecutarse.
Usando las figuras de sincronización :
Si necesita traducir los conteos del reloj al tiempo real transcurrido, divida los resultados por la velocidad de reloj de su chip. Recuerde que el GHz "clasificado" es probable que sea ligeramente diferente de la velocidad real de su chip. Para verificar la velocidad real de su chip, puede usar varias utilidades muy buenas o la llamada Win32, QueryPerformanceFrequency ().
En general, para cronometrar cuánto tiempo lleva llamar una función, desea hacerlo muchas más veces que una sola vez. Si llamas a tu función solo una vez y te lleva muy poco tiempo ejecutarla, aún tienes la carga de llamar a las funciones del temporizador y no sabes cuánto tiempo lleva.
Por ejemplo, si estima que su función podría tardar 800 ns en ejecutarse, llámela en un ciclo diez millones de veces (lo que luego tomará unos 8 segundos). Divida el tiempo total en diez millones para obtener el tiempo por llamada.
Esta nueva respuesta usa la función <chrono>
C ++ 11. Si bien hay otras respuestas que muestran cómo usar <chrono>
, ninguna de ellas muestra cómo usar <chrono>
con la función RDTSC
mencionada en varias de las otras respuestas aquí. Entonces pensé que mostraría cómo usar RDTSC
con <chrono>
. Además, demostraré cómo puede templatar el código de prueba en el reloj para que pueda cambiar rápidamente entre RDTSC
y las RDTSC
reloj integradas de su sistema (que probablemente se basarán en clock()
, clock_gettime()
y / o QueryPerformanceCounter
.
Tenga en cuenta que la instrucción RDTSC
es específica de x86. QueryPerformanceCounter
es solo Windows. Y clock_gettime()
es solo POSIX. A continuación, presento dos nuevos relojes: std::chrono::high_resolution_clock
y std::chrono::system_clock
, que, si puede suponer C ++ 11, ahora son multiplataforma.
En primer lugar, así es como se crea un reloj compatible con C ++ 11 en las instrucciones de ensamblaje de Intel rdtsc
. Lo llamaré 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
Todo lo que este reloj hace es contar los ciclos de la CPU y almacenarlos en un entero sin signo de 64 bits. Es posible que necesite modificar la sintaxis del lenguaje ensamblador para su compilador. O su compilador puede ofrecer un intrínseco que puede usar en su lugar (por ejemplo, now() {return __rdtsc();}
).
Para construir un reloj, debe darle la representación (tipo de almacenamiento). También debe proporcionar el período de reloj, que debe ser una constante de tiempo de compilación, aunque su máquina puede cambiar la velocidad del reloj en diferentes modos de potencia. Y a partir de ellos, puede definir fácilmente la duración y el punto de tiempo "nativos" de su reloj en términos de estos fundamentos.
Si todo lo que quiere hacer es generar la cantidad de marcas de reloj, realmente no importa qué número da para el período de reloj. Esta constante solo entra en juego si desea convertir la cantidad de marcas de reloj en alguna unidad en tiempo real como nanosegundos. Y en ese caso, cuanto más preciso sea capaz de suministrar la velocidad del reloj, más precisa será la conversión a nanosegundos, (milisegundos, lo que sea).
A continuación se muestra un código de ejemplo que muestra cómo usar x::clock
. De hecho, he modelado el código en el reloj porque me gustaría mostrar cómo puedes usar muchos relojes diferentes con la misma sintaxis. Esta prueba en particular muestra qué es la sobrecarga de bucle al ejecutar lo que desea cronometrar en un ciclo:
#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";
}
Lo primero que hace este código es crear una unidad de "tiempo real" para mostrar los resultados. Elegí picosegundos, pero puede elegir cualquier unidad que desee, ya sea integral o basada en coma flotante. Como ejemplo, hay una unidad std::chrono::nanoseconds
prefabricada que podría haber usado.
Como otro ejemplo, quiero imprimir el número promedio de ciclos de reloj por iteración como un punto flotante, así que creo otra duración, basada en el doble, que tiene las mismas unidades que el tic del reloj (llamado Cycle
en el código).
El ciclo está sincronizado con llamadas a clock::now()
en cualquier lado. Si desea nombrar el tipo devuelto por esta función, es:
typename clock::time_point t0 = clock::now();
(como se muestra claramente en el ejemplo x::clock
, y también es cierto para los relojes suministrados por el sistema).
Para obtener una duración en términos de tiempos de reloj de coma flotante, uno simplemente resta los dos puntos de tiempo, y para obtener el valor por iteración, divida esa duración por el número de iteraciones.
Puede obtener el recuento en cualquier duración mediante el uso de la función de miembro count()
. Esto devuelve la representación interna. Finalmente utilizo std::chrono::duration_cast
para convertir el Cycle
duración a los picoseconds
duración e imprimir eso.
Para usar este código es simple:
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>();
}
Más arriba ejercito la prueba usando nuestro x::clock
casero, y comparo esos resultados con el uso de dos de los relojes suministrados por el sistema: std::chrono::high_resolution_clock
y std::chrono::system_clock
. Para mí esto se 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
Esto muestra que cada uno de estos relojes tiene un período de tic diferente, ya que los tics por iteración son muy diferentes para cada reloj. Sin embargo, cuando se convierte a una unidad de tiempo conocida (por ejemplo, picosegundos), obtengo aproximadamente el mismo resultado para cada reloj (su kilometraje puede variar).
Tenga en cuenta que mi código está completamente libre de "constantes de conversión mágicas". De hecho, solo hay dos números mágicos en todo el ejemplo:
- La velocidad del reloj de mi máquina para definir
x::clock
. - El número de iteraciones para probar. Si cambiar este número hace que sus resultados varíen mucho, entonces probablemente deba aumentar el número de iteraciones, o vaciar su computadora de procesos competidores durante la prueba.
Estoy usando el código de Borland aquí, el código ti_hund me da algunas veces un número negativo pero el tiempo es bastante bueno.
#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
Estoy usando lo siguiente para obtener los resultados deseados:
#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;
}
Lo que otros han publicado sobre ejecutar la función repetidamente en un bucle es correcto.
Para Linux (y BSD), quiere 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 Windows, quiere usar QueryPerformanceCounter . Y aquí hay más sobre QPC
Aparentemente hay un issue conocido con QPC en algunos chipsets, por lo que es posible que desee asegurarse de que no tenga esos chipset. Además, algunos AMD de doble núcleo también pueden causar un problem . Ver la segunda publicación de sebbbi, donde dice:
QueryPerformanceCounter () y QueryPerformanceFrequency () ofrecen una resolución un poco mejor, pero tienen problemas diferentes. Por ejemplo, en Windows XP, todas las CPU AMD Athlon X2 de doble núcleo devuelven la PC de cualquiera de los núcleos "aleatoriamente" (a veces la PC salta un poco hacia atrás), a menos que instale especialmente el paquete de controlador dual-core AMD para solucionar el problema. No hemos notado ninguna otra CPU dual + core que tenga problemas similares (p4 dual, p4 ht, core2 dual, core2 quad, phenom quad).
EDITAR 16/07/07:
Parece que hay cierta controversia sobre la eficacia de QPC en determinadas circunstancias, como se indica en http://msdn.microsoft.com/en-us/library/windows/desktop/ee417693(v=vs.85).aspx
... Mientras que QueryPerformanceCounter y QueryPerformanceFrequency suelen ajustarse para varios procesadores, los errores en el BIOS o los controladores pueden dar como resultado que estas rutinas devuelvan valores diferentes a medida que el hilo se mueve de un procesador a otro ...
Sin embargo, esta respuesta de https://.com/a/4588605/34329 establece que QPC debería funcionar bien en cualquier sistema operativo MS después del paquete de servicio de Win XP 2.
Este artículo muestra que Windows 7 puede determinar si el (los) procesador (es) tiene (n) un TSC invariable y si no regresa a un temporizador externo. http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html Sincronizar entre procesadores sigue siendo un problema.
Otra buena lectura relacionada con los temporizadores:
- https://blogs.oracle.com/dholmes/entry/inside_the_hotspot_vm_clocks
- http://lwn.net/Articles/209101/
- http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html
- Estado QueryPerformanceCounter?
Ver los comentarios para más detalles.
Para C++11 , aquí hay un envoltorio simple:
#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_;
};
O para C ++ 03 en * 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_;
};
Ejemplo 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;
}
Para hacer esto correctamente, puede usar una de estas dos formas, ya sea ir con RDTSC
o con clock_gettime()
. El segundo es aproximadamente 2 veces más rápido y tiene la ventaja de dar el tiempo absoluto correcto. Tenga en cuenta que para que RDTSC
funcione correctamente, debe usarlo como se indica (otros comentarios en esta página tienen errores y pueden dar valores de tiempo incorrectos en ciertos procesadores)
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;
}
y para clock_gettime: (Elegí una resolución microsegunda 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;
}
el tiempo y los valores producidos:
Absolute values:
rdtsc = 4571567254267600
clock_gettime = 1278605535506855
Processing time: (10000000 runs)
rdtsc = 2292547353
clock_gettime = 1031119636
Puede usar Embedded Profiler (gratuito para Windows y Linux) que tiene una interfaz para un temporizador multiplataforma (en un recuento de ciclo del procesador) y puede darle una cantidad de 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;
El nuevo cálculo de la cuenta del ciclo al tiempo es posiblemente una operación peligrosa con los procesadores modernos donde la frecuencia de la CPU se puede cambiar dinámicamente. Por lo tanto, para garantizar que los tiempos de conversión sean los correctos, es necesario fijar la frecuencia del procesador antes de crear el perfil.
Puede usar la siguiente función con gcc corriendo bajo procesadores 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;
}
con Digital Mars C ++:
unsigned long long rdtsc()
{
_asm
{
rdtsc
}
}
que lee el temporizador de alto rendimiento en el chip. Lo uso cuando hago perfiles.
Qué piensas sobre eso:
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;
}
Si esto es para Linux, he estado usando la función "gettimeofday", que devuelve una estructura que da los segundos y microsegundos desde la Época. Luego puede usar timersub para restar los dos para obtener la diferencia en el tiempo y convertirla a la precisión de tiempo que desee. Sin embargo, especifica nanosegundos, y parece que la función clock_gettime() es lo que está buscando. Pone el tiempo en términos de segundos y nanosegundos en la estructura que pasas a él.
Si necesita una precisión posterior, debe usar extensiones específicas del sistema y deberá verificar con la documentación del sistema operativo. POSIX admite hasta microsegundos con gettimeofday , pero nada más preciso ya que las computadoras no tienen frecuencias superiores a 1GHz.
Si está utilizando Boost, puede verificar boost::posix_time .
Usando el método de Brock Adams, con una clase simple:
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;
};
Ejemplo de uso:
int main()
{
{
cbench c("test");
... code ...
}
return 0;
}
Resultado:
la prueba tomó: 0.0002 ms
Tiene alguna función llamada sobrecarga, pero debería ser más que suficientemente rápida :)