使用在 Windows C++ 中具有毫秒精度和分辨率的时间戳记录

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4570194/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 15:53:04  来源:igfitidea点击:

Log with timestamps that have millisecond accuracy & resolution in Windows C++

c++windowstimestampresolution

提问by Dave

I'm aware that for timing accuracy, functions like timeGetTime, timeBeginPeriod, QueryPerformanceCounter etc are great, giving both good resolution & accuracy, but only based on time-since-boot, with no direct link to clock time.

我知道为了计时准确性,像 timeGetTime、timeBeginPeriod、QueryPerformanceCounter 等函数都很棒,提供了良好的分辨率和准确性,但仅基于启动后的时间,没有与时钟时间的直接链接。

However, I don't want to time events as such. I want to be able to produce an exact timestamp (local time) so that I can display it in a log file, eg 31-12-2010 12:38:35.345, for each entry made. (I need the millisecond accuracy)

但是,我不想为这样的事件计时。我希望能够为所做的每个条目生成一个确切的时间戳(本地时间),以便我可以在日志文件中显示它,例如 31-12-2010 12:38:35.345。(我需要毫秒精度)

The standard Windows time functions, like GetLocalTime, whilst they give millisecond values, don't have millisecond resolution, depending on the OS running. I'm using XP, so I can't expect much better than about a 15ms resolution.

标准的 Windows 时间函数,如 GetLocalTime,虽然它们给出毫秒值,但没有毫秒分辨率,具体取决于运行的操作系统。我使用的是 XP,所以我不能期望比大约 15 毫秒的分辨率更好。

What I need is a way to get the best of both worlds, without creating a large overhead to get the required output. Overly large methods/calculations would mean that the logger would start to eat up too much time during its operation.

我需要的是一种两全其美的方法,而不会产生大量开销来获得所需的输出。过大的方法/计算将意味着记录器在其操作期间会开始消耗太多时间。

What would be the best/simplest way to do this?

什么是最好/最简单的方法来做到这一点?

采纳答案by Lior Kogan

First, some functions:

先说一些功能:

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

Now, how to use:

现在,如何使用:

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

Note that you should resync d1 and u1 once in 2-3 minutes to keep the accuracy. Actually, you can measure the drift between the clocks to find the optimal resync interval.

请注意,您应该在 2-3 分钟内重新同步 d1 和 u1 以保持准确性。实际上,您可以测量时钟之间的漂移以找到最佳的重新同步间隔。

回答by Steve-o

You can try GetSystemAsFileTimewhich expresses time in units of 100-nanoseconds. You are up to Windows to determine what actual resolution it populates it with.

您可以尝试GetSystemAsFileTime以 100 纳秒为单位表示时间。您由 Windows 来确定它填充的实际分辨率。

Alternative method is just to query the local time and use QueryPerformanceCounterto lock in an offset to time ratio at application startup and apply that to subsequent counter readings.

另一种方法是查询本地时间并使用QueryPerformanceCounter在应用程序启动时锁定偏移时间比并将其应用于后续计数器读数。

回答by Dialecticus

I once wrote the code for this, but eventually abandoned it and made peace with OS time resolution. The code called both GetLocalTime and QueryPerformanceCounter in a tight loop for like 50 times. When I detect that result of GetLocalTime was changed by one resolution tick then I assume that result from corresponding QueryPerformanceCounter is close enough to the beginning of that tick. That way I get a precise time offset. From that point onward I call QueryPerformanceCounter and do math with time offset to get me a precise local time.

我曾经为此编写了代码,但最终放弃了它并与操作系统时间分辨率保持和平。该代码在紧密循环中调用了 GetLocalTime 和 QueryPerformanceCounter 大约 50 次。当我检测到 GetLocalTime 的结果被一个分辨率刻度更改时,我假设相应 QueryPerformanceCounter 的结果足够接近该刻度的开头。这样我就得到了一个精确的时间偏移。从那时起,我调用 QueryPerformanceCounter 并使用时间偏移进行数学运算,以获得精确的本地时间。

In the end I reasoned that all this is not worth so much trouble, and that I don't really need all that precision.

最后我认为这一切都不值得这么麻烦,而且我真的不需要那么精确。

回答by Macke

What I'd do is to get the system time and the queryperfcounter at startup. You know have a reasonably accurate starting point.

我要做的是在启动时获取系统时间和 queryperfcounter。你知道有一个相当准确的起点。

Then call QueryPerformanceCounter() in your log system, subtract the start-QPC-value, divide by QPF to get seconds (store in a double), combine this value with the system time at startup and print.

然后在您的日志系统中调用 QueryPerformanceCounter(),减去 start-QPC-value,除以 QPF 得到秒数(以双精度形式存储),将此值与启动时的系统时间相结合并打印。

回答by Vikram.exe

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

Please note that this code uses two undocumented things, TIME_FILEDS structure and RtlTimeToTimeFields function. I am using a similar implementation in my code and it works fine on all the current WIN NT flavors. However, using this is not guaranteed to be portable among the next WIN NT release

请注意,这段代码使用了两个未公开的东西,TIME_FILEDS 结构和 RtlTimeToTimeFields 函数。我在我的代码中使用了类似的实现,它在所有当前的 WIN NT 风格上都能正常工作。但是,使用它并不能保证在下一个 WIN NT 版本中可移植