2010-12-31 7 views
8

Sono consapevole del fatto che per la precisione temporale, funzioni come timeGetTime, timeBeginPeriod, QueryPerformanceCounter ecc. Sono eccezionali, offrendo sia una buona precisione di precisione &, ma solo in base al tempo-poiché- avvio, senza collegamento diretto all'orologio.Registro con data e ora con precisione e risoluzione in Windows C++

Tuttavia, non desidero cronometrare gli eventi in quanto tali. Voglio essere in grado di produrre un timestamp esatto (ora locale) in modo che possa visualizzarlo in un file di registro, ad esempio 31-12-2010 12: 38: 35.345, per ogni voce effettuata. (Ho bisogno della precisione millisecondo)

Le funzioni standard di ora di Windows, come GetLocalTime, mentre forniscono valori in millisecondi, non hanno una risoluzione di millisecondo, a seconda del sistema operativo in esecuzione. Sto usando XP, quindi non posso aspettarmi molto meglio di una risoluzione di 15ms.

Quello che mi serve è un modo per ottenere il meglio da entrambi i mondi, senza creare un grande sovraccarico per ottenere l'output richiesto. Metodi/calcoli troppo grandi significherebbero che il registratore inizierebbe a consumare troppo tempo durante il suo funzionamento.

Quale sarebbe il modo migliore/più semplice per farlo?

risposta

5

primo luogo, alcuni funzioni:

// ========================================================================== 
#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); 
} 

Ora, come utilizzare:

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

nota che si dovrebbe risincronizzazione d1 e u1 una volta ogni 2-3 minuti per mantenere la precisione. In realtà, è possibile misurare la deriva tra gli orologi per trovare l'intervallo di risincronizzazione ottimale.

+0

Interessante il punto sul casting a 64 bit perché non ne ero a conoscenza. Significa che la mia soluzione corrente è bloccata a 32 bit mentre sto usando (__int64 *) e ft – Dave

+0

Anche se questo non è quello che ho trovato, è sicuramente più vicino alla logica che ho usato (anche se in un 32- versione bit bloccata). Questa risposta è probabilmente meglio però! – Dave

5

Si può provare GetSystemAsFileTime che esprime il tempo in unità di 100 nanosecondi. Sei in grado di determinare con quale risoluzione effettiva viene popolato.

Il metodo alternativo è solo per interrogare l'ora locale e utilizzare QueryPerformanceCounter per bloccare un offset al rapporto temporale all'avvio dell'applicazione e applicarlo alle successive letture del contatore.

+0

Tuttavia, la mia difficoltà sta nel gestire il valore in millisecondi da timeGetTime e la struttura SYSTEMTIME di GetLocalTime. Posso ottenere entrambi all'avvio, calulare una differenza in timeGetTime al momento del log, ma come applicare tale offset alla struttura GetLocalTime in un processo pulito e rapido mi sfugge. Estendendo questo per usare QueryPerformanceCounter aggiunge un'altra variabile (QueryPerformanceFrequency) al mix, rallentandolo ulteriormente. Ho bisogno di un algoritmo 'veloce', non di uno che farà affogare un sistema. – Dave

+0

@Psychic preoccuparsi delle prestazioni matematiche di base sui processori gigahertz è un po 'sciocco quando si esegue l'output su un disco che è molto più lento. –

+0

L'output è principalmente sullo schermo. Il file sarebbe facoltativo, così come l'output del database. Ci sarebbero anche molte voci al secondo, quindi la necessità di accuratezza al millisecondo, quindi avere un metodo ottimizzato è di grande bisogno in questo caso. Potremmo avere computer gigahertz, ma un sistema di registrazione non capisce tutto. Ho un buon numero di altri moduli che assorbono molte più risorse che non possono essere rallentate da un logger inefficiente. – Dave

0

Una volta ho scritto il codice per questo, ma alla fine l'ho abbandonato e reso sereno con la risoluzione temporale del sistema operativo. Il codice ha chiamato GetLocalTime e QueryPerformanceCounter in un ciclo stretto per 50 volte. Quando rilevo che il risultato di GetLocalTime è stato modificato con una tacca di risoluzione, suppongo che il risultato del corrispondente QueryPerformanceCounter sia abbastanza vicino all'inizio di tale spunta. In questo modo ottengo un offset di tempo preciso. Da quel momento in poi chiamo QueryPerformanceCounter e faccio matematica con l'offset dell'ora per ottenere una precisa ora locale.

Alla fine ho ragionato che tutto questo non vale la pena così tanto, e che non ho davvero bisogno di tutta quella precisione.

0

Quello che farei è ottenere l'ora di sistema e il queryperfcounter all'avvio. Sai avere un punto di partenza ragionevolmente preciso.

Quindi chiamare QueryPerformanceCounter() nel sistema di log, sottrarre il valore di avvio-QPC, dividere per QPF per ottenere secondi (memorizzare in un doppio), combinare questo valore con l'ora di sistema all'avvio e stampare.

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); 
} 

Si prega di notare che questo codice utilizza due cose senza documenti, la struttura e la funzione TIME_FILEDS RtlTimeToTimeFields. Sto usando un'implementazione simile nel mio codice e funziona bene su tutti gli attuali sapori WIN NT. Tuttavia, utilizzando questo non è garantito per essere portabile tra la prossima release WIN NT