C++ multiplataforma de alta resolución temporizador
cross-platform timer (14)
Esta no es la mejor respuesta, pero aquí hay algunas conversaciones en un sitio de Desarrollo de juegos con respecto a los temporizadores de alta resolución:
Estoy buscando implementar un mecanismo de temporizador simple en C ++. El código debería funcionar en Windows y Linux. La resolución debe ser lo más precisa posible (al menos en milisegundos de precisión). Esto se usará simplemente para rastrear el paso del tiempo, no para implementar ningún tipo de diseño impulsado por eventos. ¿Cuál es la mejor herramienta para lograr esto?
He visto esto implementado algunas veces como soluciones internas de código cerrado ... que recurrieron a #ifdef
soluciones #ifdef
alrededor de los temporizadores de alta resolución nativos de Windows, por un lado, y a los temporizadores del kernel de Linux que usan struct timeval
(ver man timeradd
) por otra parte.
Puede abstraer esto y algunos proyectos de código abierto lo han hecho; el último que miré fue CoinTimer, pero seguramente hay más.
La biblioteca de ACE también tiene temporizadores portátiles de alta resolución.
Doxygen para el temporizador de alta resolución:
http://www.dre.vanderbilt.edu/Doxygen/5.7.2/html/ace/a00244.html
La biblioteca de código abierto StlSoft proporciona un temporizador bastante bueno tanto en Windows como en plataformas Linux. Si quieres que se implemente por tu cuenta, solo echa un vistazo a sus fuentes.
La primera respuesta a las preguntas de la biblioteca C ++ es generalmente BOOST: Boost.Timer . ¿Hace esto lo que quieres? Probablemente no, pero es un comienzo.
El problema es que desea que las funciones portátiles y de temporizador no sean universales en los sistemas operativos.
No estoy seguro acerca de su requisito, si desea calcular el intervalo de tiempo, consulte el siguiente hilo
Cálculo del tiempo transcurrido en un programa C en milisegundos
Recomiendo la biblioteca boost :: posix_time para eso. Admite temporizadores en varias resoluciones hasta microsegundos, creo
Respuesta actualizada para una vieja pregunta:
En C ++ 11 puede llegar al temporizador de resolución más portátil con:
#include <iostream>
#include <chrono>
#include "chrono_io"
int main()
{
typedef std::chrono::high_resolution_clock Clock;
auto t1 = Clock::now();
auto t2 = Clock::now();
std::cout << t2-t1 << ''/n'';
}
Ejemplo de salida:
74 nanoseconds
"chrono_io" es una extensión para facilitar los problemas de E / S con estos nuevos tipos y está disponible gratuitamente here .
También hay una implementación de <chrono>
disponible en boost (aún puede estar en tip-of-trunk, no estoy seguro de que haya sido liberado).
Actualizar
Esto es en respuesta al comentario de Ben a continuación que las llamadas posteriores a std::chrono::high_resolution_clock
toman varios milisegundos en VS11. A continuación se muestra una solución compatible con <chrono>
. Sin embargo, solo funciona en hardware Intel, necesita sumergirse en el ensamblaje en línea (la sintaxis para hacer eso varía con el compilador), y debe conectar la velocidad del reloj de la máquina al reloj:
#include <chrono>
struct clock
{
typedef unsigned long long rep;
typedef std::ratio<1, 2800000000> 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));
}
private:
static
unsigned
get_clock_speed()
{
int mib[] = {CTL_HW, HW_CPU_FREQ};
const std::size_t namelen = sizeof(mib)/sizeof(mib[0]);
unsigned freq;
size_t freq_len = sizeof(freq);
if (sysctl(mib, namelen, &freq, &freq_len, nullptr, 0) != 0)
return 0;
return freq;
}
static
bool
check_invariants()
{
static_assert(1 == period::num, "period must be 1/freq");
assert(get_clock_speed() == period::den);
static_assert(std::is_same<rep, duration::rep>::value,
"rep and duration::rep must be the same type");
static_assert(std::is_same<period, duration::period>::value,
"period and duration::period must be the same type");
static_assert(std::is_same<duration, time_point::duration>::value,
"duration and time_point::duration must be the same type");
return true;
}
static const bool invariants;
};
const bool clock::invariants = clock::check_invariants();
Entonces no es portátil. Pero si quieres experimentar con un reloj de alta resolución en tu propio hardware de inteligencia, no se vuelve más fino que esto. Aunque estén prevenidos, las velocidades de reloj de hoy en día pueden cambiar dinámicamente (no son realmente una constante en tiempo de compilación). Y con una máquina multiprocesador, incluso puede obtener marcas de tiempo de diferentes procesadores. Pero aún así, los experimentos en mi hardware funcionan bastante bien. Si tiene una resolución de milisegundos, esto podría ser una solución.
Este reloj tiene una duración en términos de la velocidad de reloj de tu CPU (como informaste). Es decir, este reloj marca una vez cada 1 / 2,800,000,000 de segundo. Si lo desea, puede convertir esto en nanosegundos (por ejemplo) con:
using std::chrono::nanoseconds;
using std::chrono::duration_cast;
auto t0 = clock::now();
auto t1 = clock::now();
nanoseconds ns = duration_cast<nanoseconds>(t1-t0);
La conversión truncará fracciones de un ciclo de CPU para formar el nanosegundo. Otros modos de redondeo son posibles, pero ese es un tema diferente.
Para mí, esto devolverá una duración tan baja como 18 tics de reloj, que trunca a 6 nanosegundos.
He agregado algunas "comprobaciones invariables" al reloj anterior, la más importante de las cuales es verificar que el clock::period
sea el correcto para la máquina. De nuevo, este no es un código portable, pero si está usando este reloj, ya se ha comprometido a eso. La función privada get_clock_speed()
que se muestra aquí obtiene la frecuencia máxima de la CPU en OS X, y ese debería ser el mismo número que el denominador constante de clock::period
.
Agregar esto le ahorrará un poco de tiempo de depuración cuando transfiera este código a su nueva máquina y olvide actualizar el clock::period
a la velocidad de su nueva máquina. Todas las comprobaciones se realizan ya sea en tiempo de compilación o en el momento del inicio del programa. Por lo tanto, no afectará el rendimiento de clock::now()
en lo mínimo.
Si uno está utilizando el marco Qt en el proyecto, la mejor solución es probablemente usar QElapsedTimer.
Tarde en la fiesta aquí, pero estoy trabajando en una base de código heredada que aún no se puede actualizar a c ++ 11. Nadie en nuestro equipo es muy hábil en C ++, por lo que agregar una biblioteca como STL está resultando difícil (además de las preocupaciones potenciales que otros han planteado sobre los problemas de implementación). Realmente necesitaba un cronómetro multiplataforma extremadamente simple que pudiera funcionar solo sin más que simples bibliotecas de sistema estándar. Esto es lo que encontré:
songho.ca/misc/timer/timer.html
Reposicionando toda la fuente aquí solo para que no se pierda si el sitio alguna vez muere:
//////////////////////////////////////////////////////////////////////////////
// Timer.cpp
// =========
// High Resolution Timer.
// This timer is able to measure the elapsed time with 1 micro-second accuracy
// in both Windows, Linux and Unix system
//
// AUTHOR: Song Ho Ahn ([email protected]) - http://www.songho.ca/misc/timer/timer.html
// CREATED: 2003-01-13
// UPDATED: 2017-03-30
//
// Copyright (c) 2003 Song Ho Ahn
//////////////////////////////////////////////////////////////////////////////
#include "Timer.h"
#include <stdlib.h>
///////////////////////////////////////////////////////////////////////////////
// constructor
///////////////////////////////////////////////////////////////////////////////
Timer::Timer()
{
#if defined(WIN32) || defined(_WIN32)
QueryPerformanceFrequency(&frequency);
startCount.QuadPart = 0;
endCount.QuadPart = 0;
#else
startCount.tv_sec = startCount.tv_usec = 0;
endCount.tv_sec = endCount.tv_usec = 0;
#endif
stopped = 0;
startTimeInMicroSec = 0;
endTimeInMicroSec = 0;
}
///////////////////////////////////////////////////////////////////////////////
// distructor
///////////////////////////////////////////////////////////////////////////////
Timer::~Timer()
{
}
///////////////////////////////////////////////////////////////////////////////
// start timer.
// startCount will be set at this point.
///////////////////////////////////////////////////////////////////////////////
void Timer::start()
{
stopped = 0; // reset stop flag
#if defined(WIN32) || defined(_WIN32)
QueryPerformanceCounter(&startCount);
#else
gettimeofday(&startCount, NULL);
#endif
}
///////////////////////////////////////////////////////////////////////////////
// stop the timer.
// endCount will be set at this point.
///////////////////////////////////////////////////////////////////////////////
void Timer::stop()
{
stopped = 1; // set timer stopped flag
#if defined(WIN32) || defined(_WIN32)
QueryPerformanceCounter(&endCount);
#else
gettimeofday(&endCount, NULL);
#endif
}
///////////////////////////////////////////////////////////////////////////////
// compute elapsed time in micro-second resolution.
// other getElapsedTime will call this first, then convert to correspond resolution.
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTimeInMicroSec()
{
#if defined(WIN32) || defined(_WIN32)
if(!stopped)
QueryPerformanceCounter(&endCount);
startTimeInMicroSec = startCount.QuadPart * (1000000.0 / frequency.QuadPart);
endTimeInMicroSec = endCount.QuadPart * (1000000.0 / frequency.QuadPart);
#else
if(!stopped)
gettimeofday(&endCount, NULL);
startTimeInMicroSec = (startCount.tv_sec * 1000000.0) + startCount.tv_usec;
endTimeInMicroSec = (endCount.tv_sec * 1000000.0) + endCount.tv_usec;
#endif
return endTimeInMicroSec - startTimeInMicroSec;
}
///////////////////////////////////////////////////////////////////////////////
// divide elapsedTimeInMicroSec by 1000
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTimeInMilliSec()
{
return this->getElapsedTimeInMicroSec() * 0.001;
}
///////////////////////////////////////////////////////////////////////////////
// divide elapsedTimeInMicroSec by 1000000
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTimeInSec()
{
return this->getElapsedTimeInMicroSec() * 0.000001;
}
///////////////////////////////////////////////////////////////////////////////
// same as getElapsedTimeInSec()
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTime()
{
return this->getElapsedTimeInSec();
}
y el archivo de encabezado:
//////////////////////////////////////////////////////////////////////////////
// Timer.h
// =======
// High Resolution Timer.
// This timer is able to measure the elapsed time with 1 micro-second accuracy
// in both Windows, Linux and Unix system
//
// AUTHOR: Song Ho Ahn ([email protected]) - http://www.songho.ca/misc/timer/timer.html
// CREATED: 2003-01-13
// UPDATED: 2017-03-30
//
// Copyright (c) 2003 Song Ho Ahn
//////////////////////////////////////////////////////////////////////////////
#ifndef TIMER_H_DEF
#define TIMER_H_DEF
#if defined(WIN32) || defined(_WIN32) // Windows system specific
#include <windows.h>
#else // Unix based system specific
#include <sys/time.h>
#endif
class Timer
{
public:
Timer(); // default constructor
~Timer(); // default destructor
void start(); // start timer
void stop(); // stop the timer
double getElapsedTime(); // get elapsed time in second
double getElapsedTimeInSec(); // get elapsed time in second (same as getElapsedTime)
double getElapsedTimeInMilliSec(); // get elapsed time in milli-second
double getElapsedTimeInMicroSec(); // get elapsed time in micro-second
protected:
private:
double startTimeInMicroSec; // starting time in micro-second
double endTimeInMicroSec; // ending time in micro-second
int stopped; // stop flag
#if defined(WIN32) || defined(_WIN32)
LARGE_INTEGER frequency; // ticks per second
LARGE_INTEGER startCount; //
LARGE_INTEGER endCount; //
#else
timeval startCount; //
timeval endCount; //
#endif
};
#endif // TIMER_H_DEF
STLsoft tiene una Biblioteca de rendimiento , que incluye un conjunto de clases de temporizador, algunas que funcionan tanto para UNIX como para Windows.
STLsoft Matthew Wilson proporcionan varios tipos de temporizadores, con interfaces congruentes para que pueda enchufar y STLsoft . Entre las ofertas se encuentran los temporizadores que son de bajo costo pero de baja resolución, y los que tienen alta resolución pero tienen un alto costo. También existen para medir los tiempos previos a la rosca y para medir los tiempos por proceso, así como también para medir los tiempos transcurridos.
Hay un artículo exhaustivo que lo abarca en el Dr. Dobb desde hace algunos años, aunque solo cubre los de Windows, aquellos definidos en el subproyecto de WinSTL. STLSoft también proporciona temporizadores UNIX en el subproyecto UNIXSTL, y puede usar el de "PlatformSTL", que incluye UNIX o Windows, según corresponda, como en:
#include <platformstl/performance/performance_counter.hpp>
#include <iostream>
int main()
{
platformstl::performance_counter c;
c.start();
for(int i = 0; i < 1000000000; ++i);
c.stop();
std::cout << "time (s): " << c.get_seconds() << std::endl;
std::cout << "time (ms): " << c.get_milliseconds() << std::endl;
std::cout << "time (us): " << c.get_microseconds() << std::endl;
}
HTH
Para C ++ 03 :
Boost.Timer podría funcionar, pero depende del clock
función C y, por lo tanto, es posible que no tenga una resolución lo suficientemente buena para usted.
Boost.Date_Time incluye una clase ptime
que se ha recomendado anteriormente en . Consulte sus documentos en microsec_clock::local_time
y microsec_clock::universal_time
, pero tenga en cuenta su advertencia de que "los sistemas Win32 a menudo no logran una resolución de microsegundos a través de esta API".
STLsoft proporciona, entre otras cosas, thin-cross-platform (Windows y Linux / Unix) C ++ envolturas alrededor de las API específicas del sistema operativo. Su biblioteca de rendimiento tiene varias clases que harían lo que necesita. (Para hacerlo en plataforma cruzada, elija una clase como el winstl
performance_counter
que exista en los unixstl
nombres winstl
y unixstl
, luego use el espacio de nombre que coincida con su plataforma).
Para C ++ 11 y superior :
La biblioteca std::chrono
tiene esta funcionalidad incorporada. Consulte esta respuesta por @HowardHinnant para obtener más detalles.