簡體   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