web-dev-qa-db-fra.com

Comment puis-je obtenir l'heure système Windows avec une résolution en millisecondes?

Comment puis-je obtenir l'heure système Windows avec une résolution en millisecondes?

Si ce qui précède n'est pas possible, comment puis-je obtenir l'heure de démarrage du système d'exploitation? Je voudrais utiliser cette valeur avec timeGetTime () afin de calculer une heure système avec une résolution de la milliseconde.

Merci d'avance.

19
axilmar

GetTickCount ne le fera pas pour vous. 

Regardez dans QueryPerformanceFrequency/QueryPerformanceCounter. Le seul truc à obtenir ici est la mise à l'échelle du processeur, alors faites vos recherches.

6
Joel Clark

Essayez cet article de MSDN Magazine. C'est en fait assez compliqué.

Implémentez un fournisseur de temps haute résolution et à mise à jour continue pour Windows

21
Kevin Gale

Ceci est une élaboration des commentaires ci-dessus pour expliquer certains des pourquoi.

Premièrement, les appels GetSystemTime * sont les seules API Win32 fournissant l’heure du système. Cette fois, la granularité est assez grossière, car la plupart des applications n'ont pas besoin de la surcharge nécessaire pour conserver une résolution plus élevée. Le temps est (probablement) stocké en interne sous la forme d'un nombre de millisecondes sur 64 bits. L'appel de timeGetTime obtient les 32 bits de poids faible. L'appel de GetSystemTime, etc. demande à Windows de renvoyer cette milliseconde après la conversion en jours, etc. et en indiquant l'heure de démarrage du système.

Il existe deux sources de temps dans une machine: l'horloge de la CPU et une horloge intégrée (par exemple, l'horloge en temps réel (RTC), les temporisations à intervalles programmables (PIT) et la minuterie d'événements haute précision (HPET)). Le premier a une résolution d'environ ~ 0,5 ns (2 GHz) et le second est généralement programmable jusqu'à une période de 1 ms (bien que les puces plus récentes (HPET) aient une résolution plus élevée). Windows utilise ces ticks périodiques pour effectuer certaines opérations, notamment la mise à jour de l'heure système.

Les applications peuvent modifier cette période via timerBeginPeriod ; Cependant, cela affecte tout le système. Le système d'exploitation vérifiera/mettra à jour les événements réguliers à la fréquence demandée. Sous faibles charges/fréquences du processeur, il existe des périodes d'inactivité pour les économies d'énergie. Aux hautes fréquences, il n’ya pas de temps pour placer le processeur dans des états de faible consommation. Voir Résolution du minuteur pour plus de détails. Enfin, chaque tick entraîne une surcharge et l’augmentation de la fréquence consomme plus de cycles de la CPU.

Pour un temps de résolution plus élevé, le temps système n'est pas maintenu avec cette précision, pas plus que Big Ben n'a une seconde main. L'utilisation de QueryPerformanceCounter (QPC) ou des ticks du processeur (rdtsc) peut fournir la résolution entre les ticks horaires du système. Une telle approche a été utilisée dans l'article du magazine MSDN cité par Kevin. Bien que ces approches puissent avoir une dérive (due, par exemple, à l’échelle de fréquence), etc., elles doivent donc être synchronisées avec l’heure du système.

11
Brian

À partir de Windows 8, Microsoft a introduit la nouvelle commande API GetSystemTimePreciseAsFileTime:

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

Malheureusement, vous ne pouvez pas l'utiliser si vous créez un logiciel qui doit également fonctionner sur des systèmes d'exploitation plus anciens.

Ma solution actuelle est la suivante, mais sachez que l'heure déterminée n'est pas exacte, elle est seulement proche de l'heure réelle. Le résultat doit toujours être inférieur ou égal au temps réel, mais avec une erreur corrigée (à moins que l'ordinateur ne se mette en veille). Le résultat a une résolution en millisecondes. Pour moi, c'est assez exact.

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

Sous Windows, la base de tous les temps est une fonction appelée GetSystemTimeAsFiletimename__.

  • Il retourne une structure capable de contenir une heure avec une résolution de 100ns.
  • Il est conservé en UTC

La structure FILETIMEenregistre le nombre d'intervalles de 100ns depuis le 1er janvier 1600; ce qui signifie que sa résolution est limitée à 100ns.

Ceci forme notre première fonction:

 enter image description here

Un nombre de ticks de 100 ns sur 64 bits depuis le 1er janvier 1600 est un peu difficile à manier. Windows fournit une fonction d'assistance très utile, FileTimeToSystemTimename__, qui permet de décoder cet entier 64 bits en parties utiles:

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

Notez que SYSTEMTIMEa une limitation de résolution intégrée de 1ms

Nous avons maintenant un moyen de passer de FILETIMEà SYSTEMTIMEname__:

 enter image description here

Nous pourrions écrire la fonction pour obtenir l'heure système actuelle sous la forme d'une structure SYSTEIMTIMEname__:

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

Sauf que Windows a déjà écrit une telle fonction pour vous: GetSystemTimeNAME_

 enter image description here

Local plutôt que UTC

Maintenant, que se passe-t-il si vous ne voulez pas l'heure actuelle en UTC. Et si vous le voulez dans votre heure locale? Windows fournit une fonction permettant de convertir un FILETIMEau format UTC en votre heure locale: FileTimeToLocalFileTimename__ 

 enter image description here

Vous pourriez écrire une fonction qui vous renvoie déjà un FILETIMEdans local time:

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

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

   return ftNowLocal;
}

 enter image description here

Et disons que vous voulez décoder le local FILETIME en SYSTEMTIME. Ce n'est pas un problème, vous pouvez à nouveau utiliser FileTimeToSystemTimename__:

 enter image description here

Heureusement, Windows vous fournit déjà une fonction qui vous renvoie la valeur:

 enter image description here

Précis

Il y a une autre considération. Avant Windows 8, l'horloge avait une résolution d'environ 15 ms. Sous Windows 8, l’horloge a été améliorée à 100 ns (ce qui correspond à la résolution de FILETIMEname__).

  • GetSystemTimeAsFileTimename__ (hérité, résolution 15ms)
  • GetSystemTimeAsPreciseFileTimename__ (Windows 8, résolution 100ns)

Cela signifie que nous devrions toujours préférer la nouvelle valeur:

 enter image description here

Vous avez demandé l'heure

Vous avez demandé l'heure; mais vous avez des choix.

Le fuseau horaire:

  • UTC (système natif)
  • Fuseau horaire local

Le format:

  • FILETIMEname__ (système natif, résolution 100ns)
  • SYTEMTIMEname__ (décodé, résolution 1ms)

Résumé

  • Résolution 100ns: FILETIMEname __
    • UTC: GetSytemTimeAsPreciseFileTimename__ (ou GetSystemTimeAsFileTimename__)
    • Local: (lancez le vôtre)
  • Résolution 1ms: SYSTEMTIMEname __
    • UTC: GetSystemTimename__
    • Local: GetLocalTimename__
3
Ian Boyd

GetSystemTimeAsFileTime donne la meilleure précision de toute fonction Win32 pour le temps absolu. QPF/QPC, comme le suggérait Joel Clark, donnera un meilleur temps relatif.

1
Ben Voigt

Puisque nous venons tous ici pour des extraits rapides au lieu d'explications ennuyeuses, je vais en écrire un:

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

Aucune idée de la façon dont les réponses basées sur les compteurs de performance ont évolué, ne faites pas de bugs de glissement, les gars.

0
user3125367

QueryPerformanceCounter () est conçu pour une résolution de minuterie fine.

Il s'agit du minuteur de résolution le plus élevé du système que vous pouvez utiliser dans le code de votre application pour identifier les goulots d'étranglement des performances.

Voici une implémentation simple pour les développeurs 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());
    }

Si vous êtes un développeur C/C++, consultez la page suivante: http://support.Microsoft.com/kb/815668

0
Bruno

Celle-ci est très ancienne, mais il existe une autre fonction utile dans la bibliothèque Windows C _ftime , qui renvoie une structure avec l'heure locale time_t, millisecondes, fuseau horaire et indicateur d'heure avancée.

0
Mikhail Edoshin