c++ cross-platform timer

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:

  1. http://www.gamedev.net/topic/374327-timing-is-everything/
  2. http://www.gamedev.net/topic/471804-high-resolution-timer/
  3. http://www.gamedev.net/topic/40600-high-resolution-timing-in-games/

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 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.



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


SDL2 tiene un excelente temporizador de alta resolución multiplataforma. Sin embargo, si necesita una precisión de menos de milisegundos, escribí una pequeña biblioteca de temporizadores multiplataforma here . Es compatible con C ++ 03 y C ++ 11 / versiones superiores de C ++.


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.