Temporizador de alta resolución multiplataforma de C++
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 una precisión de milisegundos). Esto se utilizará simplemente para seguir el paso del tiempo, no para implementar ningún tipo de diseño basado en eventos. ¿Cuál es la mejor herramienta para lograr esto?
Respuesta actualizada para una antigua pregunta:
En C++11 puedes llegar de forma portátil al temporizador de mayor resolución 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';
}
Salida de ejemplo:
74 nanoseconds
"chrono_io" es una extensión para aliviar los problemas de E/S con estos nuevos tipos y está disponible gratuitamente aquí .
También hay una implementación de <chrono>
disponible en impulso (puede que todavía esté en la punta del baúl, no estoy seguro de que se haya lanzado).
Actualizar
Esto es en respuesta al comentario de Ben a continuación de que las llamadas posteriores demoran std::chrono::high_resolution_clock
varios milisegundos en VS11. A continuación se muestra una <chrono>
solución alternativa compatible. Sin embargo, solo funciona en hardware Intel, debe sumergirse en el ensamblaje en línea (la sintaxis para hacerlo varía según 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 desea experimentar con un reloj de alta resolución en su propio hardware Intel, no hay nada mejor que esto. Aunque tenga cuidado, las velocidades de reloj actuales pueden cambiar dinámicamente (en realidad no son una constante en tiempo de compilación). Y con una máquina multiprocesador incluso puedes obtener marcas de tiempo de diferentes procesadores. Pero aun así, los experimentos con mi hardware funcionan bastante bien. Si está atascado con una resolución de milisegundos, esta podría ser una solución.
Este reloj tiene una duración en términos de la velocidad del reloj de su CPU (tal como lo informó). Es decir, para mí este reloj marca una vez cada 1/2.800.000.000 de segundo. Si quieres, puedes convertir esto a 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. Son posibles otros modos de redondeo, pero ese es un tema diferente.
Para mí, esto devolverá una duración tan baja como 18 tics de reloj, que se trunca a 6 nanosegundos.
Agregué algunas "verificaciones invariantes" al reloj anterior, la más importante de las cuales es verificar que sea clock::period
correcto para la máquina. Nuevamente, este no es un código portátil, pero si estás usando este reloj, ya te has comprometido a hacerlo. La get_clock_speed()
función privada que se muestra aquí obtiene la frecuencia máxima de la CPU en OS X, y 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 se olvide de actualizarlo a clock::period
la velocidad de su nueva máquina. Toda la verificación se realiza en tiempo de compilación o en el momento de inicio del programa. Por lo que no afectará clock::now()
en lo más mínimo el rendimiento.
Para C++03 :
Boost.Timer puede funcionar, pero depende de la función C clock
y, por lo tanto, es posible que no tenga una resolución lo suficientemente buena para usted.
Boost.Date_Time incluye una ptime
clase que se recomendó anteriormente en Stack Overflow. Consulte sus documentos en microsec_clock::local_time
y microsec_clock::universal_time
, pero tenga en cuenta la advertencia de que "los sistemas Win32 a menudo no alcanzan una resolución de microsegundos a través de esta API".
STLsoft proporciona, entre otras cosas, envoltorios C++ ligeros multiplataforma (Windows y Linux/Unix) alrededor de API específicas del sistema operativo. Su biblioteca de rendimiento tiene varias clases que harían lo que necesitas. (Para que sea multiplataforma, elija una clase como performance_counter
la que exista en los espacios de nombres winstl
y unixstl
, luego use el espacio de nombres que coincida con su plataforma).
Para C++11 y superior :
La std::chrono
biblioteca tiene esta funcionalidad incorporada. Consulte esta respuesta de @HowardHinnant para obtener más detalles.
Las bibliotecas STLSoft de Matthew Wilson proporcionan varios tipos de temporizadores, con interfaces congruentes para que pueda conectar y usar. Entre las ofertas se encuentran temporizadores de bajo costo pero de baja resolución, y otros de alta resolución pero de alto costo. También los hay para medir tiempos de preroscado y para medir tiempos por proceso, así como todos los que miden tiempos transcurridos.
Hay un artículo exhaustivo que lo cubre en Dr. Dobb's de hace algunos años, aunque solo cubre los de Windows, los definidos en el subproyecto WinSTL. STLSoft también proporciona temporizadores UNIX en el subproyecto UNIXSTL, y puede usar el "PlatformSTL", que incluye el de 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;
}
HT
La biblioteca de código abierto StlSoft proporciona un temporizador bastante bueno tanto en plataformas Windows como Linux. Si desea implementarlo por su cuenta, simplemente eche un vistazo a sus fuentes.
La biblioteca ACE también tiene temporizadores portátiles de alta resolución.
Doxygen para temporizador de alta resolución:
http://www.dre.vanderbilt.edu/Doxygen/5.7.2/html/ace/a00244.html