我知道,对于计时准确性,timeGetTime,timeBeginPeriod,QueryPerformanceCounter等功能都很棒,同时提供了良好的分辨率和优点。准确性,但仅基于启动时间,没有与时钟时间的直接链接。
但是,我不想这样做时间事件。我希望能够生成一个精确的时间戳(本地时间),以便我可以在日志文件中显示它,例如31-12-2010 12:38:35.345,对于每个条目。 (我需要毫秒精度)
标准的Windows时间函数,如GetLocalTime,虽然它们提供毫秒值,但没有毫秒级的分辨率,具体取决于运行的操作系统。我正在使用XP,所以我不能指望比大约15ms的分辨率好多了。
我需要的是一种获得两全其美的方法,而不会产生大量开销来获得所需的输出。过大的方法/计算意味着记录器在操作过程中会开始耗费太多时间。
最好/最简单的方法是什么?
答案 0 :(得分:5)
您可以尝试GetSystemAsFileTime
,以100纳秒为单位表示时间。您可以使用Windows来确定它填充的实际分辨率。
替代方法只是查询本地时间并使用QueryPerformanceCounter
锁定应用程序启动时的偏移量与时间比率,并将其应用于后续计数器读数。
答案 1 :(得分:5)
首先,一些功能:
// ==========================================================================
#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);
请注意,您应该在2-3分钟内重新同步d1和u1一次,以保持准确性。 实际上,您可以测量时钟之间的漂移,以找到最佳的重新同步间隔。
答案 2 :(得分:0)
我曾为此编写过代码,但最终放弃了它,并通过操作系统时间分辨率实现了和平。该代码在紧密循环中调用了GetLocalTime和QueryPerformanceCounter 50次。当我检测到GetLocalTime的结果被一个分辨率滴答改变时,我假设相应的QueryPerformanceCounter的结果足够接近该滴答的开头。这样我得到了精确的时间偏移。从那时起,我调用QueryPerformanceCounter并使用时间偏移进行数学计算,以获得精确的本地时间。
最后我推断所有这些都不值得这么麻烦,而且我并不需要那么精确。
答案 3 :(得分:0)
我要做的是在启动时获取系统时间和queryperfcounter。你知道有一个相当准确的起点。
然后在您的日志系统中调用QueryPerformanceCounter(),减去start-QPC值,除以QPF得到秒(存储为double),将此值与启动时的系统时间相结合并打印。
答案 4 :(得分: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);
}
请注意,此代码使用两个未记录的内容,TIME_FILEDS结构和RtlTimeToTimeFields函数。我在我的代码中使用了类似的实现,它适用于所有当前的WIN NT风格。但是,使用它不能保证在下一个WIN NT版本中可移植