it-swarm.com.de

Wie kann ich die Windows-Systemzeit mit Millisekundenauflösung ermitteln?

Wie kann ich die Windows-Systemzeit mit Millisekundenauflösung ermitteln?

Wenn dies nicht möglich ist, wie kann ich dann die Startzeit des Betriebssystems ermitteln? Ich möchte diesen Wert zusammen mit timeGetTime () verwenden, um eine Systemzeit mit Millisekundenauflösung zu berechnen.

Danke im Voraus.

19
axilmar

GetTickCount wird es nicht für Sie erledigen. 

Schauen Sie in QueryPerformanceFrequency/QueryPerformanceCounter. Das einzige Problem ist hier die CPU-Skalierung.

6
Joel Clark

Versuchen Sie diesen Artikel aus dem MSDN Magazine. Es ist eigentlich ziemlich kompliziert.

Implementiere einen ständig aktualisierenden, hochauflösenden Zeitanbieter für Windows

21
Kevin Gale

Dies ist eine Ausarbeitung der obigen Kommentare, um einige der Gründe zu erklären.

Erstens sind die Aufrufe GetSystemTime * die einzigen Win32-APIs, die die Systemzeit bereitstellen. Diese Zeit hat eine ziemlich grobe Granularität, da die meisten Anwendungen nicht den zusätzlichen Aufwand benötigen, um eine höhere Auflösung aufrechtzuerhalten. Die Zeit wird (wahrscheinlich) intern als eine 64-Bit-Anzahl von Millisekunden gespeichert. Beim Aufruf von timeGetTime werden die niederwertigen 32 Bit abgerufen. Beim Aufruf von GetSystemTime etc fordert Windows die Angabe dieser Millisekundenzeit nach der Konvertierung in Tage usw. sowie die Startzeit des Systems an.

In einer Maschine gibt es zwei Zeitquellen: die Uhr der CPU und eine Borduhr (z. B. Echtzeituhr (RTC), programmierbare Intervallzeitgeber (PIT) und High Precision Event Timer (HPET)). Der erste hat eine Auflösung von ca. ~ 0,5 ns (2 GHz) und der zweite ist im Allgemeinen bis zu einer Periode von 1 ms programmierbar (obwohl neuere Chips (HPET) eine höhere Auflösung haben). Windows verwendet diese periodischen Häkchen, um bestimmte Vorgänge auszuführen, einschließlich der Aktualisierung der Systemzeit.

Anwendungen können diese Periode über timerBeginPeriod ändern. Dies betrifft jedoch das gesamte System. Das Betriebssystem prüft/aktualisiert regelmäßige Ereignisse in der angeforderten Häufigkeit. Bei niedrigen CPU-Lasten/-Frequenzen gibt es Leerlaufzeiten, um Energie zu sparen. Bei hohen Frequenzen bleibt keine Zeit, um den Prozessor in den Energiesparmodus zu versetzen. Siehe Timer Resolution für weitere Details. Schließlich hat jeder Tick einen gewissen Aufwand und die Erhöhung der Frequenz beansprucht mehr CPU-Zyklen.

Für eine höhere Auflösungszeit wird die Systemzeit nicht auf diese Genauigkeit eingehalten, nur der Big Ben verfügt über einen Sekundenzeiger. Die Verwendung von QueryPerformanceCounter (QPC) oder der Ticks der CPU (rdtsc) kann die Auflösung zwischen den Systemzeit-Ticks bereitstellen. Ein solcher Ansatz wurde in dem von Kevin zitierten Artikel des MSDN-Magazins verwendet. Diese Ansätze können zwar eine Drift haben (z. B. aufgrund von Frequenzskalierung) usw. und müssen daher mit der Systemzeit synchronisiert werden.

11
Brian

Ab Windows 8 hat Microsoft den neuen API-Befehl GetSystemTimePreciseAsFileTime eingeführt:

https://msdn.Microsoft.com/de-de/library/windows/desktop/hh706895%28v=vs.85%29.aspx

Leider können Sie dies nicht verwenden, wenn Sie Software erstellen, die auch auf älteren Betriebssystemen ausgeführt werden muss.

Meine derzeitige Lösung ist wie folgt, aber seien Sie sich bewusst: Die festgelegte Zeit ist nicht genau, sie ist nur nahe an der Echtzeit. Das Ergebnis sollte immer kleiner oder gleich der Echtzeit sein, jedoch mit einem festen Fehler (sofern der Computer nicht in den Standby-Modus ging). Das Ergebnis hat eine Millisekunde Auflösung. Für meinen Zweck ist es genau genug.

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);
    }
}
4
David Gausmann

In Windows ist die Basis aller Zeiten eine Funktion namens GetSystemTimeAsFiletimename__.

  • Es gibt eine Struktur zurück, die in der Lage ist, eine Zeit mit einer 100 ns-Wiederholung zu halten.
  • Es wird in UTC aufbewahrt

Die FILETIMEname__-Struktur zeichnet die Anzahl der 100ns-Intervalle seit dem 1. Januar 1600 auf. Das bedeutet, seine Auflösung ist auf 100ns begrenzt.

Dies bildet unsere erste Funktion:

 enter image description here

Eine 64-Bit-Anzahl von 100ns-Ticks seit dem 1. Januar 1600 ist etwas unhandlich. Windows bietet eine praktische Hilfsfunktion, FileTimeToSystemTimename__, die diese 64-Bit-Ganzzahl in nützliche Teile dekodieren kann:

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

Beachten Sie, dass SYSTEMTIMEeine integrierte Auflösungsbeschränkung von 1ms hat.

Nun haben wir eine Möglichkeit, von FILETIMEzu SYSTEMTIMEzu gehen:

 enter image description here

Wir könnten die Funktion schreiben, um die aktuelle Systemzeit als SYSTEIMTIMEname__-Struktur abzurufen:

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

Außer dass Windows bereits eine solche Funktion für Sie geschrieben hat: GetSystemTimeNAME_

 enter image description here

Lokal statt UTC

Was ist, wenn Sie die aktuelle Uhrzeit nicht in UTC anzeigen möchten. Was ist, wenn Sie es in Ihrer Ortszeit wünschen? Windows bietet eine Funktion zum Konvertieren eines FILETIMEin UTC in Ihre Ortszeit: FileTimeToLocalFileTimename__ 

 enter image description here

Sie könnten eine Funktion schreiben, die Ihnen FILETIMEbereits in local time zurückgibt:

FILETIME GetLocalTimeAsFileTime()
{
   FILETIME ftNow;
   GetSystemTimeAsFileTime(ref ftNow);

   //convert to local
   FILETIME ftNowLocal
   FileTimeToLocalFileTime(ftNow, ref ftNowLocal);

   return ftNowLocal;
}

 enter image description here

Angenommen, Sie möchten das local FILETIME in ein SYSTEMTIME decodieren. Das ist kein Problem, Sie können FileTimeToSystemTimeerneut verwenden:

 enter image description here

Glücklicherweise stellt Windows Ihnen bereits eine Funktion zur Verfügung, die Ihnen den Wert zurückgibt:

 enter image description here

Präzise

Es gibt noch eine andere Überlegung. Vor Windows 8 hatte die Uhr eine Auflösung von ca. 15ms. In Windows 8 wurde die Uhr auf 100 ns erhöht (entsprechend der Auflösung von FILETIMEname__).

  • GetSystemTimeAsFileTimename__ (ältere Version, 15ms Auflösung)
  • GetSystemTimeAsPreciseFileTimename__ (Windows 8, Auflösung 100ns)

Das heißt, wir sollten immer den neuen Wert bevorzugen:

 enter image description here

Du hast nach der Zeit gefragt

Sie haben nach der Zeit gefragt. aber Sie haben einige Möglichkeiten.

Die Zeitzone:

  • UTC (systemeigen)
  • Lokale Zeitzone

Das Format:

  • FILETIMEname__ (systemeigen, 100ns Auflösung)
  • SYTEMTIMEname__ (decodiert, 1ms Auflösung)

Zusammenfassung

  • 100ns-Auflösung: FILETIMEname __
    • UTC: GetSytemTimeAsPreciseFileTimename__ (oder GetSystemTimeAsFileTimename__)
    • Local: (würfeln Sie Ihre eigenen)
  • Auflösung von 1ms: SYSTEMTIMEname __
    • UTC: GetSystemTimename__
    • Local: GetLocalTimename__
3
Ian Boyd

GetSystemTimeAsFileTime bietet die beste Genauigkeit aller Win32-Funktionen für die absolute Zeit. QPF/QPC, wie Joel Clark vorschlug, ergibt eine bessere relative Zeit.

1
Ben Voigt

Da wir alle hierher kommen, um kurze Auszüge statt langweiliger Erklärungen zu erhalten, schreibe ich eine:

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

Keine Ahnung, wie driftende Antworten auf der Basis von Leistungsindikatoren zugenommen haben, mache keine Slippage-Fehler, Leute.

0
user3125367

QueryPerformanceCounter () ist für eine feinkörnige Timerauflösung ausgelegt.

Es ist der Timer mit der höchsten Auflösung, den das System zu bieten hat und den Sie in Ihrem Anwendungscode verwenden können, um Leistungsengpässe zu identifizieren

Hier ist eine einfache Implementierung für C # devs:

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

Wenn Sie ein C/C++ - Entwickler sind, schauen Sie hier: http://support.Microsoft.com/kb/815668

0
Bruno

Nun, dieses ist sehr alt, aber es gibt noch eine weitere nützliche Funktion in der Windows C-Bibliothek _ftime , die eine Struktur mit Ortszeit als time_t, Millisekunden, Zeitzone und Sommerzeit-Flag zurückgibt.

0
Mikhail Edoshin