繁体   English   中英

如何获得毫秒级的Windows系统时间?

[英]How can I get the Windows system time with millisecond resolution?

如何获得毫秒级的Windows系统时间?

如果上述方法不可行,那么如何获得操作系统启动时间? 我想将此值与timeGetTime()一起使用,以便以毫秒为单位计算系统时间。

先感谢您。

试试《 MSDN杂志》中的这篇文章。 实际上非常复杂。

为Windows实施持续更新的高分辨率时间提供程序

这是对以上评论的详尽解释,以解释一些原因。

首先, GetSystemTime *调用是唯一提供系统时间的Win32 API。 这段时间的粒度相当粗糙,因为大多数应用程序不需要维持较高分辨率所需的开销。 时间(可能)在内部存储为64位毫秒数。 调用timeGetTime将获得低阶32位。 调用GetSystemTime等要求Windows将转换为天等后(包括系统启动时间)返回此毫秒时间。

机器中有两个时间源:CPU时钟和板载时钟(例如,实时时钟(RTC),可编程间隔计时器(PIT)和高精度事件计时器(HPET))。 第一个具有约0.5ns(2GHz)的分辨率,第二个通常可编程至1ms的周期(尽管较新的芯片(HPET)具有更高的分辨率)。 Windows使用这些定期的滴答声来执行某些操作,包括更新系统时间。

应用程序可以通过timerBeginPeriod更改此时间 但是,这会影响整个系统。 操作系统将以请求的频率检查/更新常规事件。 在低CPU负载/频率下,有一些空闲时间可以节省功率。 在高频下,没有时间将处理器置于低功耗状态。 有关更多详细信息,请参见计时器分辨率 最后,每个刻度都有一些开销,增加频率会消耗更多的CPU周期。

对于更高的分辨率时间,系统时间不会保持这种精度,仅比Big Ben有用。 使用QueryPerformanceCounter(QPC)或CPU的刻度(rdtsc)可以提供系统时间刻度之间的分辨率。 MSDN杂志文章Kevin引用了这种方法。 尽管这些方法可能会有漂移(例如,由于频率缩放),等等,因此需要与系统时间同步。

GetTickCount不会为您完成它。

查看QueryPerformanceFrequency / QueryPerformanceCounter 不过,唯一的陷阱是CPU扩展,因此您的研究也是如此。

在Windows中,所有时间的基础是一个称为GetSystemTimeAsFiletime的函数。

  • 它返回一个结构,该结构能够保留100ns重新设定时间。
  • 它保存在UTC中

FILETIME结构记录自1600年1月1日以来的100ns间隔数; 表示其分辨率限制为100ns。

这形成了我们的第一个功能:

在此处输入图片说明

自1600年1月1日以来,100ns滴答声的64位数字有些笨拙。 Windows提供了一个方便的辅助函数FileTimeToSystemTime ,可以将这个64位整数解码为有用的部分:

record SYSTEMTIME {
   wYear: Word;
   wMonth: Word;
   wDayOfWeek: Word;
   wDay: Word;
   wHour: Word;
   wMinute: Word;
   wSecond: Word;
   wMilliseconds: Word;
}

请注意, SYSTEMTIME具有1ms的内置分辨率限制

现在,我们有了从FILETIMESYSTEMTIME

在此处输入图片说明

我们可以编写函数以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

现在,如果您不希望使用UTC的当前时间怎么办。 如果您想在当地时间怎么办? Windows提供了将UTC中的FILETIME转换为本地时间的功能: 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,100ns分辨率)

这意味着我们应该始终偏爱新值:

在此处输入图片说明

你问时间

您要求时间; 但是你有一些选择。

时区:

  • UTC (系统本机)
  • 当地时区

格式:

  • FILETIME (系统本机,100ns分辨率)
  • SYTEMTIME (已解码,分辨率为1ms)

摘要

  • 100ns分辨率: FILETIME
    • UTC: GetSytemTimeAsPreciseFileTime (或GetSystemTimeAsFileTime
    • 本地:( 自行滚动)
  • 1ms分辨率: SYSTEMTIME
    • UTC: GetSystemTime
    • 本地: GetLocalTime

从Windows 8开始,Microsoft引入了新的API命令GetSystemTimePreciseAsFileTime:

https://msdn.microsoft.com/zh-CN/library/windows/desktop/hh706895%28v=vs.85%29.aspx

不幸的是,如果您创建的软件也必须在较旧的操作系统上运行,则无法使用它。

我当前的解决方案如下,但请注意:确定的时间并不精确,仅接近实时时间。 结果应始终小于或等于实时,但有固定错误(除非计算机进入待机状态)。 结果具有毫秒分辨率。 就我的目的而言,这已经足够了。

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将提供更好的相对时间。

由于我们所有人都是来这里快速摘录而不是无聊的解释,所以我将写一个:

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

如果您是C / C ++开发人员,请在此处查看: http : //support.microsoft.com/kb/815668

好的,这是很老的,但是Windows C库_ftime还有另一个有用的函数,该函数返回一个结构,其本地时间为time_t ,毫秒,时区和夏令时标志。

在回答“ Windows上的微秒分辨率时间戳”问题时,我已经写了一些有关如何以适合大多数目的的方式快速,轻松地实现此目的的信息。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM