Журнал с отметками времени, которые имеют точность и разрешение в миллисекундах в Windows C ++ - PullRequest
8 голосов
/ 31 декабря 2010

Мне известно, что для обеспечения точности синхронизации отлично подходят такие функции, как timeGetTime, timeBeginPeriod, QueryPerformanceCounter и т. Д., Которые обеспечивают как хорошее разрешение, так и точность, но только на основе времени с момента загрузки, без прямой ссылки на время часов.

Однако я не хочу оценивать события как таковые.Я хочу иметь возможность создавать точную временную метку (местное время), чтобы я мог отображать ее в файле журнала, например, 31-12-2010 12: 38: 35.345, для каждой сделанной записи.(Мне нужна точность в миллисекундах)

Стандартные функции времени Windows, такие как GetLocalTime, хотя они дают значения в миллисекундах, не имеют разрешения в миллисекундах, в зависимости от работающей ОС.Я использую XP, поэтому не могу ожидать гораздо лучшего, чем разрешение около 15 мс.

Мне нужен способ получить лучшее из обоих миров, не создавая больших накладных расходов для получения требуемого результата.,Чрезмерно большие методы / расчеты означают, что регистратор начнет слишком много времени потреблять во время своей работы.

Каков наилучший / самый простой способ сделать это?

Ответы [ 5 ]

5 голосов
/ 31 декабря 2010

Во-первых, некоторые функции:

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

Теперь, как использовать:

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

Обратите внимание, что вы должны повторно синхронизировать d1 и u1 раз в 2-3 минуты, чтобы сохранить точность,На самом деле, вы можете измерить дрейф между часами, чтобы найти оптимальный интервал повторной синхронизации.

5 голосов
/ 31 декабря 2010

Вы можете попробовать GetSystemAsFileTime, который выражает время в единицах по 100 наносекунд. Вы до Windows, чтобы определить, с каким фактическим разрешением он заполняет ее.

Альтернативный метод - просто запросить местное время и использовать QueryPerformanceCounter, чтобы зафиксировать отношение смещения к времени при запуске приложения и применить его к последующим показаниям счетчика.

0 голосов
/ 31 декабря 2010
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);
}

Обратите внимание, что в этом коде используются две недокументированные вещи: структура TIME_FILEDS и функция RtlTimeToTimeFields. Я использую подобную реализацию в своем коде, и она отлично работает на всех текущих версиях WIN NT. Тем не менее, использование этого не гарантируется переносимость в следующем выпуске WIN NT

0 голосов
/ 31 декабря 2010

Что бы я сделал, чтобы получить системное время и запросperfcounter при запуске.Вы знаете, есть достаточно точная отправная точка.

Затем вызовите QueryPerformanceCounter () в вашей системе журналов, вычтите значение start-QPC, разделите на QPF, чтобы получить секунды (запишите в двойное число), объедините это значение с системным временем при запуске и распечатайте.

0 голосов
/ 31 декабря 2010

Я однажды написал код для этого, но в итоге отказался от него и помирился с временным разрешением ОС. Код вызывал как GetLocalTime, так и QueryPerformanceCounter в тесном цикле примерно 50 раз. Когда я обнаруживаю, что результат GetLocalTime был изменен одним тиковым разрешением, я предполагаю, что результат соответствующего QueryPerformanceCounter достаточно близок к началу этого тика. Таким образом, я получаю точное смещение времени. С этого момента я вызываю QueryPerformanceCounter и делаю математику со смещением времени, чтобы получить точное местное время.

В конце концов, я рассудил, что все это не стоит особых хлопот и что мне не нужна вся эта точность.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...