如何以毫秒分辨率获取Windows系统时间?
如果上述不可行,则如何获取操作系统启动时间?我想将此值与timeGetTime()
一起使用,以计算具有毫秒分辨率的系统时间。
如何以毫秒分辨率获取Windows系统时间?
如果上述不可行,则如何获取操作系统启动时间?我想将此值与timeGetTime()
一起使用,以计算具有毫秒分辨率的系统时间。
可以阅读MSDN Magazine上的这篇文章。实际上它非常复杂。
在Windows中,所有时间的基础是名为GetSystemTimeAsFiletime
的函数。
FILETIME
结构记录了自1600年1月1日以来的100纳秒间隔数;这意味着其分辨率受到100纳秒的限制。
这就形成了我们的第一个函数:
一个自1600年1月1日以来的100纳秒滴答声的64位数字有点不方便。 Windows提供了一个方便的帮助函数FileTimeToSystemTime
,可以将这个64位整数解码成有用的部分:
record SYSTEMTIME {
wYear: Word;
wMonth: Word;
wDayOfWeek: Word;
wDay: Word;
wHour: Word;
wMinute: Word;
wSecond: Word;
wMilliseconds: Word;
}
请注意,SYSTEMTIME
具有内置的分辨率限制,为1ms
。
现在我们有一种方法可以从FILETIME
转换为SYSTEMTIME
:
我们可以编写函数以将当前系统时间作为SYSTEIMTIME
结构获取:
SYSTEMTIME GetSystemTime()
{
//Get the current system time utc in it's native 100ns FILETIME structure
FILETIME ftNow;
GetSytemTimeAsFileTime(ref ft);
//Decode the 100ns intervals into a 1ms resolution SYSTEMTIME for us
SYSTEMTIME stNow;
FileTimeToSystemTime(ref stNow);
return stNow;
}
除非你想自己编写,否则Windows已经为你编写了这样一个函数:GetSystemTime
如果你不想要当前的UTC时间,而是想要本地时间,那怎么办?Windows提供了一种将FILETIME
(UTC时间)转换为本地时间的方法:FileTimeToLocalFileTime
你可以编写一个返回本地时间的FILETIME
的函数:
FILETIME GetLocalTimeAsFileTime()
{
FILETIME ftNow;
GetSystemTimeAsFileTime(ref ftNow);
//convert to local
FILETIME ftNowLocal
FileTimeToLocalFileTime(ftNow, ref ftNowLocal);
return ftNowLocal;
}
假设你想把本地的FILETIME解码成SYSTEMTIME。那不是问题,你可以再次使用FileTimeToSystemTime
:
幸运的是,Windows已经提供了一个函数可以返回这个值:
还有另一种考虑。在Windows 8之前,时钟的分辨率大约为15ms。在Windows 8中,他们将时钟改进到100ns(与FILETIME
的分辨率相匹配)。
GetSystemTimeAsFileTime
(旧版, 15ms 分辨率)GetSystemTimeAsPreciseFileTime
(Windows 8, 100 ns 分辨率)这意味着我们应该始终优先选择新值:
你要求时间;但你有一些选择。
时区:
格式:
FILETIME
(系统本地, 100ns 分辨率)SYTEMTIME
(已解码, 1ms 分辨率)FILETIME
GetSytemTimeAsPreciseFileTime
(或 GetSystemTimeAsFileTime
)SYSTEMTIME
GetSystemTime
GetLocalTime
这是对上述评论的详细阐述,解释了一些原因。
首先,GetSystemTime* 调用是提供系统时间的唯一 Win32 API。由于大多数应用程序不需要维护更高分辨率所需的开销,该时间具有相当粗糙的精度。时间(可能)存储为毫秒的 64 位计数。调用 timeGetTime
获取低 32 位。调用 GetSystemTime 等请求 Windows 返回此毫秒时间,经过转换为天等并包括系统启动时间。
机器中有两个时间源:CPU 的时钟和板载时钟(例如,实时时钟(RTC)、可编程间隔定时器(PIT)和高精度事件定时器(HPET))。前者的分辨率约为 ~0.5 ns(2 GHz),后者通常可编程到 1 ms 的周期(但新型芯片(HPET)具有更高的分辨率)。Windows 使用这些周期性滴答声执行某些操作,包括更新系统时间。
应用程序可以通过 timerBeginPeriod 更改此周期;但是,这会影响整个系统。操作系统将在所请求的频率上检查/更新常规事件。在低 CPU 负载/频率下,存在用于节能的空闲时间。在高频率下,没有时间将处理器置于低功耗状态。详见 计时器分辨率 以获取更多详细信息。最后,每个滴答声都有一些开销,增加频率会消耗更多的 CPU 周期。
针对需要更高时间分辨率的情况,系统时间并不会保持如同大本钟秒针一般的精确度。可以使用 QueryPerformanceCounter
(QPC) 或者CPU的时钟周期数 (rdtsc
) 来提供系统时间间隔的分辨率。在Kevin引用的MSDN杂志文章中采用了这种方式。尽管这些方法可能存在漂移 (例如由于频率缩放等原因),因此需要将它们与系统时间同步。
GetTickCount
不足以满足您的需求。
请查看 QueryPerformanceFrequency
/ QueryPerformanceCounter
。唯一需要注意的是 CPU 频率变化,因此请做好研究。
自Windows 8起,Microsoft引入了新的API命令GetSystemTimePreciseAsFileTime
不幸的是,如果您创建的软件还必须在旧操作系统上运行,则无法使用该命令。
我的当前解决方案如下,但请注意:确定的时间不是精确的,它只接近于实际时间。结果应始终小于或等于实际时间,但具有固定误差(除非计算机进入待机状态)。结果具有毫秒分辨率。对于我的目的来说,这已经足够精确。
void GetHighResolutionSystemTime(SYSTEMTIME* pst)
{
static LARGE_INTEGER uFrequency = { 0 };
static LARGE_INTEGER uInitialCount;
static LARGE_INTEGER uInitialTime;
static bool bNoHighResolution = false;
if(!bNoHighResolution && uFrequency.QuadPart == 0)
{
// Initialize performance counter to system time mapping
bNoHighResolution = !QueryPerformanceFrequency(&uFrequency);
if(!bNoHighResolution)
{
FILETIME ftOld, ftInitial;
GetSystemTimeAsFileTime(&ftOld);
do
{
GetSystemTimeAsFileTime(&ftInitial);
QueryPerformanceCounter(&uInitialCount);
} while(ftOld.dwHighDateTime == ftInitial.dwHighDateTime && ftOld.dwLowDateTime == ftInitial.dwLowDateTime);
uInitialTime.LowPart = ftInitial.dwLowDateTime;
uInitialTime.HighPart = ftInitial.dwHighDateTime;
}
}
if(bNoHighResolution)
{
GetSystemTime(pst);
}
else
{
LARGE_INTEGER uNow, uSystemTime;
{
FILETIME ftTemp;
GetSystemTimeAsFileTime(&ftTemp);
uSystemTime.LowPart = ftTemp.dwLowDateTime;
uSystemTime.HighPart = ftTemp.dwHighDateTime;
}
QueryPerformanceCounter(&uNow);
LARGE_INTEGER uCurrentTime;
uCurrentTime.QuadPart = uInitialTime.QuadPart + (uNow.QuadPart - uInitialCount.QuadPart) * 10000000 / uFrequency.QuadPart;
if(uCurrentTime.QuadPart < uSystemTime.QuadPart || abs(uSystemTime.QuadPart - uCurrentTime.QuadPart) > 1000000)
{
// The performance counter has been frozen (e. g. after standby on laptops)
// -> Use current system time and determine the high performance time the next time we need it
uFrequency.QuadPart = 0;
uCurrentTime = uSystemTime;
}
FILETIME ftCurrent;
ftCurrent.dwLowDateTime = uCurrentTime.LowPart;
ftCurrent.dwHighDateTime = uCurrentTime.HighPart;
FileTimeToSystemTime(&ftCurrent, pst);
}
}
GetSystemTimeAsFileTime 是Win32中用于绝对时间的精度最高的函数。如Joel Clark所建议的,QPF/QPC将提供更好的相对时间。
timeBeginPeriod(1);
将把准确性设置为1毫秒。 - Ben Voigt既然我们都来这里寻找快速代码片段而不是无聊的解释,那我就写一个:
FILETIME t;
GetSystemTimeAsFileTime(&t); // unusable as is
ULARGE_INTEGER i;
i.LowPart = t.dwLowDateTime;
i.HighPart = t.dwHighDateTime;
int64_t ticks_since_1601 = i.QuadPart; // now usable
int64_t us_since_1601 = (i.QuadPart * 1e-1);
int64_t ms_since_1601 = (i.QuadPart * 1e-4);
int64_t sec_since_1601 = (i.QuadPart * 1e-7);
// unix epoch
int64_t unix_us = (i.QuadPart * 1e-1) - 11644473600LL * 1000000;
int64_t unix_ms = (i.QuadPart * 1e-4) - 11644473600LL * 1000;
double unix_sec = (i.QuadPart * 1e-7) - 11644473600LL;
// i.QuadPart is # of 100ns ticks since 1601-01-01T00:00:00Z
// difference to Unix Epoch is 11644473600 seconds (attention to units!)
不知道为什么漂移性能计数器的答案提高了,大家不要出现滑动错误。
QueryPerformanceCounter()
是为了精细的计时器分辨率而建立的。
它是系统提供的最高分辨率计时器,您可以在应用程序代码中使用它来识别性能瓶颈。
这里是C#开发人员的一个简单实现:
[DllImport("kernel32.dll")]
extern static short QueryPerformanceCounter(ref long x);
[DllImport("kernel32.dll")]
extern static short QueryPerformanceFrequency(ref long x);
private long m_endTime;
private long m_startTime;
private long m_frequency;
public Form1()
{
InitializeComponent();
}
public void Begin()
{
QueryPerformanceCounter(ref m_startTime);
}
public void End()
{
QueryPerformanceCounter(ref m_endTime);
}
private void button1_Click(object sender, EventArgs e)
{
QueryPerformanceFrequency(ref m_frequency);
Begin();
for (long i = 0; i < 1000; i++) ;
End();
MessageBox.Show((m_endTime - m_startTime).ToString());
}
QueryPerformanceCounter
函数在Visual C++中计时代码}}。timespec_get()
}}以便携的方式获取更高精度的时间。#include <stdio.h>
#include <time.h>
int main(void)
{
struct timespec ts;
timespec_get(&ts, TIME_UTC);
char buff[100];
strftime(buff, sizeof buff, "%D %T", gmtime(&ts.tv_sec));
printf("Current time: %s.%09ld UTC\n", buff, ts.tv_nsec);
}
<chrono>
头文件中使用 std::chrono::high_resolution_clock
、std::chrono::system_clock
(挂钟)或 std::chrono::steady_clock
(单调时钟),不再需要使用特定于 Windows 的 API。auto start1 = std::chrono::high_resolution_clock::now();
auto start2 = std::chrono::system_clock::now();
auto start3 = std::chrono::steady_clock::now();
// do some work
auto end1 = std::chrono::high_resolution_clock::now();
auto end2 = std::chrono::system_clock::now();
auto end3 = std::chrono::steady_clock::now();
std::chrono::duration<long long, std::milli> diff1 = end1 - start1;
std::chrono::duration<double, std::milli> diff2 = end2 - start2;
auto diff3 = std::chrono::duration_cast<std::chrono::milliseconds>(end3 - start3);
std::cout << diff.count() << ' ' << diff2.count() << ' ' << diff3.count() << '\n';
GetSystemTime
吗? - user1196549