Temporizador de alta resolución multiplataforma de C++

Resuelto Amish Programmer asked hace 15 años • 14 respuestas

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?

Amish Programmer avatar Sep 28 '09 22:09 Amish Programmer
Aceptado

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_clockvarios 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::periodcorrecto 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::periodla 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.

Howard Hinnant avatar Apr 02 '2011 15:04 Howard Hinnant

Para C++03 :

Boost.Timer puede funcionar, pero depende de la función C clocky, por lo tanto, es posible que no tenga una resolución lo suficientemente buena para usted.

Boost.Date_Time incluye una ptimeclase que se recomendó anteriormente en Stack Overflow. Consulte sus documentos en microsec_clock::local_timey 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_counterla que exista en los espacios de nombres winstly unixstl, luego use el espacio de nombres que coincida con su plataforma).

Para C++11 y superior :

La std::chronobiblioteca tiene esta funcionalidad incorporada. Consulte esta respuesta de @HowardHinnant para obtener más detalles.

Josh Kelley avatar Sep 28 '2009 15:09 Josh Kelley

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

dcw avatar Sep 29 '2009 20:09 dcw

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.

Malte Clasen avatar Sep 28 '2009 15:09 Malte Clasen

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

 avatar Sep 28 '2009 15:09