2010-07-02 19 views
8

¿Cuál es el sistema de temporización más rápido que puede usar un programador C/C++?Sistema de resolución de temporización más rápido

Por ejemplo:
time() dará los segundos desde el 1 de enero de 1970 00:00.
GetTickCount() en Windows dará el tiempo, en milisegundos, desde el momento de inicio del sistema, pero está limitado a 49.7 días (después de eso simplemente vuelve a cero).

Quiero obtener la hora actual, o tics desde el inicio del sistema o la aplicación, en milisegundos.

La mayor preocupación es la sobrecarga del método; necesito la más liviana, porque estoy a punto de llamarla muchas veces por segundo.

Mi caso es que tengo un hilo de trabajo, y para ese hilo de trabajo publico trabajos pendientes. Cada trabajo tiene un "tiempo de ejecución". Por lo tanto, no me importa si el tiempo es el tiempo "real" actual o el tiempo transcurrido desde el tiempo de actividad del sistema; solo debe ser lineal y ligero.

Editar:

unsigned __int64 GetTickCountEx() 
{ 
    static DWORD dwWraps = 0; 
    static DWORD dwLast = 0; 

    DWORD dwCurrent = 0; 

    timeMutex.lock(); 

    dwCurrent = GetTickCount(); 
    if(dwLast > dwCurrent) 
     dwWraps++; 

    dwLast = dwCurrent; 

    unsigned __int64 timeResult = ((unsigned __int64)0xFFFFFFFF * dwWraps) + dwCurrent; 

    timeMutex.unlock(); 

    return timeResult; 
} 
+0

¿Anticipas ejecutar esto en sistemas con tiempo de actividad> 49.7 días? –

+0

Sí. Definitivamente más. – Poni

+0

¿Preguntas específicamente sobre Windows? Las plataformas de Posix tienen 'gettimeofday()', que puede abarcar varias décadas con una precisión de microsegundos. –

Respuesta

15

Para la sincronización, la current Microsoft recommendation es utilizar QueryPerformanceCounter & QueryPerformanceFrequency.

Esto le dará un tiempo superior a milisegundos. Si el sistema no es compatible con un temporizador de alta resolución, se establecerá en milisegundos (lo mismo que GetTickCount).

Here is a short Microsoft article with examples of why you should use it :)

+0

En el primer enlace, en la cláusula n. ° 1, dice: "los desarrolladores deben intentar que sus juegos llamen a QueryPerformanceCounter lo menos posible para evitar cualquier penalización en el rendimiento". Entonces, ¿debo concluir que mejor uso RDTSC? Parece una llamada muy dependiente de la plataforma. – Poni

+0

Ok, lea un poco más y RDTSC no es la elección. – Poni

+0

Además, MSDN recomienda no llamar a QueryPerformanceCounter demasiadas veces. Entonces, ¿me quedé con GetTickCount? – Poni

0

En Linux te microsegundos:

struct timeval tv; 
int res = gettimeofday(&tv, NULL); 
double tmp = (double) tv.tv_sec + 1e-6 * (double) tv.tv_usec; 

En Windows, sólo millseconds están disponibles:

SYSTEMTIME st; 
GetSystemTime(&st); 
tmp += 1e-3 * st.wMilliseconds; 

return tmp; 

Esto vino de datetime.c R 's (y fue editado por brevedad).

Luego está, por supuesto, Boost's Date_Time que puede tener una resolución de nanosegundos en algunos sistemas (detalles here y here).

3

Si usted está preocupado por GetTickCount() desbordante, a continuación, sólo se puede envolver así:

DWORDLONG GetLongTickCount(void) 
{ 
    static DWORDLONG last_tick = 0; 
    DWORD tick = GetTickCount(); 

    if (tick < (last_tick & 0xffffffff)) 
     last_tick += 0x100000000; 

    last_tick = (last_tick & 0xffffffff00000000) | tick; 
    return last_tick; 
} 

Si desea llamar a este desde varios subprocesos que necesita para bloquear el acceso a la variable last_tick . Siempre que llame al GetLongTickCount() al menos una vez cada 49,7 días, detectará el desbordamiento.

0

POSIX admite clock_gettime() que usa struct timespec que tiene una resolución de nanosegundos. Si su sistema realmente admite una resolución precisa es más debatible, pero creo que es la llamada estándar con la resolución más alta. No todos los sistemas lo admiten, y a veces está bien oculto (biblioteca '-lposix4' en Solaris, IIRC).


Actualización (09/20/2016):

  • Aunque Mac OS X 10.6.4 no apoyó clock_gettime(), y ni tampoco cualquier otra versión de Mac OS X hasta e incluyendo Mac OS X 10.11.6 El Capitan), macOS Sierra 10.12 tiene la función clock_gettime() y páginas de manual para ella por fin. La resolución real (en CLOCK_MONOTONIC) todavía es de microsegundos; las unidades más pequeñas son todos ceros. Esto se confirma con clock_getres(), que informa que la resolución es de 1000 nanosegundos, también conocido como 1 μs.

La página del manual para clock_gettime() en MacOS Sierra menciones mach_absolute_time() como una manera de obtener alta resolución de tiempo. Para obtener más información, entre otros lugares, consulte Technical Q&A QA1398: Mach Absolute Time Units y (en SO) What is mach_absolute_time() based on on iPhone?

5

Hace poco tuve esta pregunta e hice una investigación. La buena noticia es que los tres sistemas operativos principales proporcionan algún tipo de temporizador de alta resolución. La mala noticia es que se trata de una llamada API diferente en cada sistema. Para los sistemas operativos POSIX, usted quiere usar clock_gettime(). Sin embargo, si usa Mac OS X, esto no es compatible, debe usar mach_get_time(). Para Windows, use QueryPerformanceCounter. Alternativamente, con los compiladores que admiten OpenMP, puede usar omp_get_wtime(), pero es posible que no proporcione la resolución que está buscando.

También encontré cycle.h de fftw.org (www.fftw.org/cycle.h) para ser útil.

Aquí hay un código que llama a un temporizador en cada sistema operativo, utilizando algunas feables declaraciones #ifdef. El uso es muy simple: Temporizador t; t.tic(); SomeOperation(); t.toc ("Mensaje"); E imprimirá el tiempo transcurrido en segundos.

#ifndef TIMER_H 
#define TIMER_H 

#include <iostream> 
#include <string> 
#include <vector> 

# if (defined(__MACH__) && defined(__APPLE__)) 
# define _MAC 
# elif (defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(_WIN64)) 
# define _WINDOWS 
# ifndef WIN32_LEAN_AND_MEAN 
#  define WIN32_LEAN_AND_MEAN 
# endif 
#endif 

# if defined(_MAC) 
# include <mach/mach_time.h> 
# elif defined(_WINDOWS) 
# include <windows.h> 
# else 
# include <time.h> 
# endif 


#if defined(_MAC) 
    typedef uint64_t timer_t; 
    typedef double timer_c; 

#elif defined(_WINDOWS) 
    typedef LONGLONG  timer_t; 
    typedef LARGE_INTEGER timer_c; 

#else 
    typedef double timer_t; 
    typedef timespec timer_c; 
#endif 

    //============================================================================== 
    // Timer 
    // A quick class to do benchmarking. 
    // Example: Timer t; t.tic(); SomeSlowOp(); t.toc("Some Message"); 

    class Timer { 
    public: 
    Timer(); 

    inline void tic(); 
    inline void toc(); 
    inline void toc(const std::string &msg); 

    void print(const std::string &msg); 
    void print(); 
    void reset(); 
    double getTime(); 

    private: 
    timer_t start; 
    double duration; 
    timer_c ts; 
    double conv_factor; 
    double elapsed_time; 
    }; 



    Timer::Timer() { 

#if defined(_MAC) 
    mach_timebase_info_data_t info; 
    mach_timebase_info(&info); 

    conv_factor = (static_cast<double>(info.numer))/ 
        (static_cast<double>(info.denom)); 
    conv_factor = conv_factor*1.0e-9; 

#elif defined(_WINDOWS) 
    timer_c freq; 
    QueryPerformanceFrequency(&freq); 
    conv_factor = 1.0/(static_cast<double>freq.QuadPart); 

#else 
    conv_factor = 1.0; 
#endif 

    reset(); 
    } 

    inline void Timer::tic() { 

#if defined(_MAC) 
    start = mach_absolute_time(); 

#elif defined(_WINDOWS) 
    QueryPerformanceCounter(&ts); 
    start = ts.QuadPart; 

#else 
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); 
    start = static_cast<double>(ts.tv_sec) + 1.0e-9 * 
      static_cast<double>(ts.tv_nsec); 

#endif 
    } 

    inline void Timer::toc() { 
#if defined(_MAC) 
    duration = static_cast<double>(mach_absolute_time() - start); 

#elif defined(_WINDOWS) 
    QueryPerformanceCounter(&qpc_t); 
    duration = static_cast<double>(qpc_t.QuadPart - start); 

#else 
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); 
    duration = (static_cast<double>(ts.tv_sec) + 1.0e-9 * 
       static_cast<double>(ts.tv_nsec)) - start; 

#endif 

    elapsed_time = duration*conv_factor; 
    } 

    inline void Timer::toc(const std::string &msg) { toc(); print(msg); }; 

    void Timer::print(const std::string &msg) { 
    std::cout << msg << " "; print(); 
    } 

    void Timer::print() { 
    if(elapsed_time) { 
     std::cout << "elapsed time: " << elapsed_time << " seconds\n"; 
    } 
    } 

    void Timer::reset() { start = 0; duration = 0; elapsed_time = 0; } 
    double Timer::getTime() { return elapsed_time; } 


#if defined(_WINDOWS) 
# undef WIN32_LEAN_AND_MEAN 
#endif 

#endif // TIMER_H 
+0

Agradable, muy útil para probar! En cuanto a su uso en un proyecto, estoy tratando de evitar QPC porque no es 100% confiable, como algunos dicen aquí. – Poni

+0

Encuentro más información acerca de mach_absolute_time, declarado en mach/mach_time.h, vea este http técnico : //developer.apple.com/library/mac/#q a/qa1398/_index.html –

1

me gustaría sugerir que utilice la API GetSystemTimeAsFileTime si está dirigido específicamente de Windows. Generalmente es más rápido que GetSystemTime y tiene la misma precisión (que es de unos 10-15 milisegundos, no mire la resolución); cuando hice un benchmark hace algunos años bajo Windows XP, estaba en el rango de 50-100 veces más rápido.

La única desventaja es que es posible que tenga que convertir las estructuras FILETIME devueltas a una hora de reloj usando, por ejemplo, FileTimeToSystemTime si necesita acceder a las horas devueltas en un formato más amigable para los humanos. Por otro lado, siempre que no necesite esos tiempos convertidos en tiempo real, siempre puede hacerlo fuera de línea o de una manera "floja" (por ejemplo, solo convierta las marcas de tiempo que necesita mostrar/procesar, y solo cuando realmente los necesitas).

QueryPerformanceCounter puede ser una buena opción como otros han mencionado, pero la sobrecarga puede ser bastante grande dependiendo del soporte de hardware subyacente. En mi punto de referencia que menciono anteriormente, las llamadas QueryPerformanceCounter fueron 25-200 veces más lentas que las llamadas a GetSystemTimeAsFileTime. Además, hay algunos problemas de confiabilidad, como p. informó here.

Por lo tanto, en resumen: si puede hacer frente a una precisión de 10-15 milisegundos, le recomiendo que utilice GetSystemTimeAsFileTime. Si necesitas algo mejor que eso, me gustaría ir a QueryPerformanceCounter.

Pequeña exención de responsabilidad: No realicé ninguna evaluación comparativa en versiones de Windows posteriores a XP SP3. Te recomiendo que hagas algunos benchmarking por tu cuenta.

+0

¡Gracias por una excelente respuesta! El primer enlace lleva a otro enlace muy útil, para su información: Implementación de un proveedor de tiempo de alta resolución para Windows http://msdn.microsoft.com/en-us/magazine/cc163996.aspx – Poni

0

Si se dirige a una versión lo suficientemente tardía del sistema operativo, entonces puede usar GetTickCount64() que tiene un punto de ajuste mucho más alto que GetTickCount(). También podría simplemente crear una versión de GetTickCount64() en la parte superior de GetTickCount().

+0

Hizo eso, sin embargo, me obliga para envolver el código con un mutex, que no parece exactamente correcto. ¿Por qué? Porque eventualmente, creo, estoy usando dos mutexes: uno de los contenedores y uno de GetTickCount(). Ver mi edición Dime lo que piensas, ¡gracias Len! – Poni

+0

No lo haría así. Tendría un temporizador para incrementar la parte alta de un ULONGLONG en el momento apropiado, tal vez usando un incremento entrelazado sin bloqueo, y luego simplemente usarlo en la llamada a GetTickCountEx(). En este momento estás haciendo un trabajo innecesario por 49.7 días menos, además de sincronizar todos los hilos que lo usan (¿y eso es un crítico y no un mutex?) Probablemente puedas evitar el bloqueo, o usar un bloqueo de lector/escritor y solo bloquear cuando estés a punto de actualizar la parte alta ... Podrías usar mi herramienta de interceptación API "tick shifter" para probar el código http://www.lenholgate.com/archives/000647.html –

+0

Buen punto Len. Tendré que evitar el bloqueo (y sí, es una sección crítica). El temporizador para aumentar la parte alta es una buena idea. – Poni

0

¿Ha revisado el código en este artículo de MSDN?

http://msdn.microsoft.com/en-us/magazine/cc163996.aspx

tengo este código compilar en una máquina de 64 bits de Windows 7 usando tanto VC2005 y C++ Builder XE, pero cuando se ejecuta, que encierra mi máquina; no me he depurado lo suficiente para averiguar por qué todavía. Parece demasiado complicado. Plantillas de plantillas de plantillas de UG ...

4

GetSystemTimeAsFileTime es el recurso más rápido. Su granularidad se puede obtener mediante una llamada al GetSystemTimeAdjustment que rellena lpTimeIncrement. La hora del sistema como tiempo de archivo tiene 100 unidades e incrementos en TimeIncrement. TimeIncrement puede variar y depende de la configuración de la interfaz del temporizador multimedia.

Una llamada al timeGetDevCaps revelará las capacidades de los servicios horarios. Devuelve un valor wPeriodMin para el período mínimo de interrupción admitida. Una llamada a timeBeginPeriod con wPeriodMin como argumento configurará el sistema para operar a la frecuencia de interrupción más alta posible (típicamente ~ 1ms). Esto hará que también obliguen a que el incremento de tiempo del archivo filetime devuelto por GetSystemTimeAsFileTime sea más pequeño. Su granularidad estará en el rango de 1 ms (10000 100ns unidades).

Para su propósito, le sugiero que elija este enfoque.

La elección QueryPerformanceCounter es cuestionable ya que su frecuencia no es exacta por dos medios: En primer lugar se aparta del valor dado por QueryPerformanceFrequency por un hardware específico offset. Este desplazamiento puede ser fácilmente varios cientos de ppm, lo que significa que una conversión en el tiempo contendrá un error de varios cientos de microsegundos por segundo. En segundo lugar tiene deriva térmica. La deriva de tales dispositivos puede ser fácilmente de varias ppm. De esta forma, se agrega otro/error de varios us/s.

Por lo tanto, siempre que una resolución de ~ 1ms sea suficiente y la pregunta principal sea la sobrecarga, GetSystemTimeAsFileTime es, de lejos, la mejor solución.

Cuando los microsegundos importan, tiene que recorrer un largo camino y ver más detalles. Los servicios de tiempo de milisegundos se describen en Windows Timestamp Project

0

En Mac OS X, puede usar UInt32 TickCount (void) para obtener los tics.