2010-12-31 11 views
8

Soy consciente de que para la precisión de tiempo, las funciones como timeGetTime, timeBeginPeriod, QueryPerformanceCounter etc. son excelentes, proporcionando una buena resolución & de precisión, pero solo en función del tiempo transcurrido desde arranque, sin enlace directo a la hora del reloj.Registro con marcas de tiempo que tienen exactitud y resolución de milisegundos en Windows C++

Sin embargo, no quiero cronometrar eventos como tales. Quiero poder mostrar una marca de tiempo exacta (hora local) para poder mostrarla en un archivo de registro, por ejemplo, 31-12-2010 12: 38: 35.345, por cada entrada realizada. (Necesito la precisión de milisegundo)

Las funciones de tiempo estándar de Windows, como GetLocalTime, aunque dan valores en milisegundos, no tienen una resolución de milisegundos, según el sistema operativo en ejecución. Estoy usando XP, así que no puedo esperar mucho mejor que una resolución de 15 ms.

Lo que necesito es una forma de obtener lo mejor de ambos mundos, sin crear una gran sobrecarga para obtener la salida requerida. Métodos/cálculos demasiado grandes significarían que el registrador comenzaría a consumir demasiado tiempo durante su operación.

¿Cuál sería la mejor/más simple forma de hacer esto?

Respuesta

5

primer lugar, algunas funciones:

// ========================================================================== 
#define NOMINMAX 
#define _AFXDLL 
#include "afxwin.h"    // TRACE 
#include "windows.h"    // ULARGE_INTEGER 
#include "mmSystem.h"    // timeGetTime 
#pragma comment(lib, "Winmm.lib") // timeGetTime 

// ========================================================================== 
// convert FILETIME to ULONGLONG 
// (casting won't work on 64-bit platforms, due to alignment of FILETIME members) 
inline void ToULL(const FILETIME& ft, ULONGLONG& uft) 
{ 
    ULARGE_INTEGER uli; 
    uli.LowPart = ft.dwLowDateTime ; 
    uli.HighPart= ft.dwHighDateTime; 
    uft= uli.QuadPart; 
} 

// -------------------------------------------------------------------------- 
// convert ULONGLONG to FILETIME 
// (casting won't work on 64-bit platforms, due to alignment of FILETIME members) 
inline void ToFILETIME(const ULONGLONG& uft, FILETIME& ft) 
{ 
    ULARGE_INTEGER uli; 
    uli.QuadPart= uft; 
    ft.dwLowDateTime = uli.LowPart ; 
    ft.dwHighDateTime= uli.HighPart; 
} 

// -------------------------------------------------------------------------- 
// ULONGLONG version for GetSystemTimeAsFileTime 
inline void GetSystemTimeAsULL(ULONGLONG& uft) 
{ 
    FILETIME ft; 
    ::GetSystemTimeAsFileTime(&ft); 
    ToULL(ft, uft); 
} 

// -------------------------------------------------------------------------- 
// convert ULONGLONG to time-components 
bool ULLToSystemTime(const ULONGLONG nTime  , // [i] 
        WORD&   nYear  , // [o] 1601 - 30827 
        WORD&   nMonth  , // [o] 1 - 12 
        WORD&   nDay   , // [o] 1 - 31 
        WORD&   nHour  , // [o] 0 - 23 
        WORD&   nMinute  , // [o] 0 - 59 
        WORD&   nSecond  , // [o] 0 - 59 
        WORD&   nMilliseconds) // [o] 0 - 999 
{ 
    SYSTEMTIME sysTime; 
    FILETIME ft  ; 
    ToFILETIME(nTime, ft); 

    // the wDayOfWeek member of the SYSTEMTIME structure is ignored 
    if (0 == ::FileTimeToSystemTime(&ft, &sysTime)) 
     return false; 

    nYear  = sysTime.wYear  ; 
    nMonth  = sysTime.wMonth  ; 
    nDay   = sysTime.wDay   ; 
    nHour  = sysTime.wHour  ; 
    nMinute  = sysTime.wMinute  ; 
    nSecond  = sysTime.wSecond  ; 
    nMilliseconds= sysTime.wMilliseconds; 
    return true; 
} 

// -------------------------------------------------------------------------- 
void TraceTime(const ULONGLONG nTime) // [i] 
{ 
    WORD nYear,nMonth,nDay,nHour,nMinute,nSecond,nMilliseconds; 
    ULLToSystemTime(nTime, nYear,nMonth,nDay,nHour,nMinute,nSecond,nMilliseconds); 
    TRACE("Time: %02u-%02u-%04u %02u:%02u:%02u.%03u\n", nDay,nMonth,nYear,nHour,nMinute,nSecond,nMilliseconds); 
} 

Ahora, cómo utilizar:

ULONGLONG u0,u1; 
::GetSystemTimeAsULL(u0); 

// wait for tick (each 14.4mS) 
do 
{ 
    ::GetSystemTimeAsULL(u1); 
} 
while (u0==u1); 

DWORD d1= ::timeGetTime(); 

// d1 and u1 are now synchronized 

// ... do some work 

// get current time: 
ULONGLONG u2= u1+(::timeGetTime() - d1)*10000; // mSec --> HectoNanoSec 

TraceTime(u2); 

en cuenta que debe resync d1 y u1 una vez cada 2-3 minutos para mantener la precisión. En realidad, puede medir la deriva entre los relojes para encontrar el intervalo de resincronización óptimo.

+0

Interesante el punto acerca del lanzamiento de 64 bits ya que no estaba al tanto de eso. Significa que mi solución actual está bloqueada de 32 bits mientras estoy usando (__int64 *) & ft – Dave

+0

Aunque esto no es lo que he terminado, está ciertamente más cerca de la lógica que he usado (aunque en un 32- versión bloqueada bit). ¡Esta respuesta es probablemente mejor! – Dave

5

Puede probar GetSystemAsFileTime que expresa el tiempo en unidades de 100 nanosegundos. Le corresponde a Windows determinar con qué resolución real lo llena.

El método alternativo consiste simplemente en consultar la hora local y usar QueryPerformanceCounter para bloquear una relación de compensación de tiempo al inicio de la aplicación y aplicarla a lecturas posteriores del contador.

+0

Sin embargo, mi dificultad radica en tratar con el valor en milisegundos de timeGetTime y la estructura SYSTEMTIME de GetLocalTime. Puedo obtener ambos al inicio, calculo una diferencia en timeGetTime en tiempo de registro, pero la forma de aplicar ese desplazamiento a la estructura GetLocalTime en un proceso limpio y rápido me está eludiendo. Al extender esto para usar QueryPerformanceCounter se agrega otra variable (QueryPerformanceFrequency) a la mezcla, lo que hace que se ralentice aún más. Necesito un algoritmo 'rápido', no uno que acapare un sistema. – Dave

+0

@Psychic Preocuparse por el rendimiento matemático básico en los procesadores de gigahercios es un poco tonto cuando está produciendo en un disco que es mucho más lento. –

+0

La salida es principalmente para pantalla. El archivo sería opcional, al igual que la salida de la base de datos. También habría muchas entradas por segundo, de ahí la necesidad de una precisión de milisegundos, por lo que tener un método optimizado es de gran necesidad en este caso. Podríamos tener computadoras de gigahertz, pero un sistema de registro no lo comprende todo. Tengo un buen número de otros módulos que consumen muchos más recursos que un rastreador ineficiente no puede ralentizar. – Dave

0

Una vez escribí el código para esto, pero finalmente lo abandoné y en paz con la resolución de tiempo del sistema operativo. El código llamó tanto a GetLocalTime como a QueryPerformanceCounter en un ciclo cerrado por 50 veces. Cuando detecto que el resultado de GetLocalTime fue cambiado por un tic de resolución, entonces supongo que el resultado del QueryPerformanceCounter correspondiente está lo suficientemente cerca del comienzo de ese tic. De esa forma obtengo una compensación de tiempo precisa. Desde ese momento en adelante, invoco QueryPerformanceCounter y hago cálculos matemáticos con la compensación de tiempo para obtener una hora local precisa.

Al final razoné que todo esto no vale tanto la pena, y que realmente no necesito toda esa precisión.

0

Lo que haría es obtener la hora del sistema y el queryperfcounter en el inicio. Usted sabe que tiene un punto de partida razonablemente preciso.

Luego llame a QueryPerformanceCounter() en su sistema de registro, restar el valor de inicio-QPC, divida por QPF para obtener segundos (almacene en un doble), combine este valor con la hora del sistema al inicio e imprima.

0
typedef struct _TIME_FIELDS { 

USHORT Year; USHORT Month; USHORT Day; USHORT Hour; USHORT Minute; USHORT Second; USHORT Milliseconds; USHORT Weekday; 

} TIME_FIELDS; 

typedef signed __int64  INT64; 
typedef unsigned __int64 UINT64; 


INT64 get_current_time() 
{ 
    ULONG secs; 
    LARGE_INTEGER tm; 

    KeQuerySystemTime (&tm); 

    RtlTimeToSecondsSince1970(&tm, &secs); 

    /* NOTE: tm is in 100s of nano seconds */ 
    return (secs * 1000 + (tm.QuadPart/10000)%1000); 
} 

LARGE_INTEGER get_system_time() 
{ 
    LARGE_INTEGER tm; 
    INT64 time = get_current_time(); 

    RtlSecondsSince1970ToTime (time, &tm); 

    /* NOTE: tm is in 100s of nano seconds */ 
    tm.QuadPart += (QTIME_MSECS(time)%1000)*10000; 
    return tm; 

} 

int log_current_time() 
{ 
    static char* month_names[] = 
    { 
    "Jan", 
    "Feb", 
    "Mar", 
    "Apr", 
    "May", 
    "Jun", 
    "Jul", 
    "Aug", 
    "Sep", 
    "Oct", 
    "Nov", 
    "Dec" 
    }; 

    LARGE_INTEGER tm, lcl_time; 
    TIME_FIELDS fields; 

    tm = get_system_time(); 

    ExSystemTimeToLocalTime (&tm, &lcl_time); 

    RtlTimeToTimeFields(&tm, &fields); 

    printf ("%s %02d %04d:%02d:%02d:%02d:%02d", month_names[fields.Month - 1], 
     fields.Day, fields.Year, fields.Hour, fields.Minute, fields.Second, 
     fileds.Milliseconds); 
} 

Tenga en cuenta que este código utiliza dos cosas indocumentados, estructura y función TIME_FILEDS RtlTimeToTimeFields. Estoy usando una implementación similar en mi código y funciona bien en todos los sabores actuales de WIN NT. Sin embargo, el uso de este no se garantiza que sea portátil entre la próxima versión de Windows NT

Cuestiones relacionadas