web-dev-qa-db-fra.com

Mesure du temps de haute précision C ++ dans Windows

Je souhaite mesurer un point précis dans le temps jusqu'à la nanoseconde à l'aide de C++ dans Windows. Est-ce possible? Si ce n'est pas le cas, est-il possible d'obtenir l'heure spécifique en microsecondes au moins?. N'importe quelle bibliothèque devrait le faire, sauf si je suppose que c'est possible avec du code managé. Merci

30
Kelly Elton

Si une application filetée s'exécute sur un ordinateur multicœur, QueryPerformanceCounter peut (et va) retourner des valeurs différentes selon le noyau sur lequel le code s'exécute. Voir cet article MSDN. (rdtsc a le même problème)

Ce n'est pas seulement un problème théorique; nous l'avons rencontré avec notre application et avons dû conclure que la seule source de temps fiable est timeGetTime qui n'a qu'une précision ms (ce qui était heureusement suffisant dans notre cas). Nous avons également essayé de fixer l'affinité des threads pour nos threads afin de garantir que chaque thread reçoive toujours une valeur cohérente de QueryPerformanceCounter, cela a fonctionné, mais cela a absolument tué les performances de l'application.

Pour résumer, il n'y a pas de minuterie fiable sur les fenêtres qui peut être utilisée pour chronométrer la chose avec une précision de micro seconde (du moins pas lors de l'exécution sur un ordinateur multicœur).

34
Andreas Brinck

Windows a une API de compteur haute performance .

Vous devez obtenir la forme des ticks QueryPerformanceCounter et diviser par la fréquence du processeur, fournie par QueryPerformanceFrequency.

LARGE_INTEGER frequency;
if (::QueryPerformanceFrequency(&frequency) == FALSE)
    throw "foo";

LARGE_INTEGER start;
if (::QueryPerformanceCounter(&start) == FALSE)
    throw "foo";

// Calculation.


LARGE_INTEGER end;
if (::QueryPerformanceCounter(&end) == FALSE)
    throw "foo";

double interval = static_cast<double>(end.QuadPart - start.QuadPart) / frequency.QuadPart;

Ce interval devrait être en quelques secondes.

14
Konrad Rudolph

Pour référence future, avec Windows Vista, 2008 et supérieur, Windows nécessite le support matériel "HPET". Cela fonctionne indépendamment du CPU, de son horloge et de sa fréquence. Il est possible d'obtenir des temps avec des précisions à la sous-microseconde.

Pour mettre en œuvre cela, vous avez besoin d'utiliser QPC/QPF. Le problème est que QPF (fréquence) est une valeur NOMINALE, donc l'utilisation des appels bruts entraînera des dérives temporelles pouvant dépasser les minutes par jour. Pour expliquer cela, vous devez mesurer la fréquence réelle et vérifier sa dérive dans le temps car la chaleur et d'autres conditions physiques de fonctionnement l'affecteront.

Un article qui décrit cela peut être trouvé sur MSDN (vers 2004!) À ce lien. http://msdn.Microsoft.com/en-us/magazine/cc163996.aspx

J'ai moi-même implémenté quelque chose de similaire (et j'ai trouvé le lien ci-dessus aujourd'hui!) Mais je préfère ne pas utiliser le "temps microseconde" parce que l'appel QPC lui-même est assez long par rapport à d'autres appels Windows tels que GetSystemTimeAsFileTime, et la synchronisation ajoute plus de surcharge. Je préfère donc utiliser des horodatages en millisecondes (environ 70% de temps d'appel en moins qu'en utilisant QPC), surtout lorsque j'essaie d'obtenir le temps des centaines de milliers de fois par seconde.

6
Frank LaPiana

Le meilleur choix est les fonctions QueryPerformanceCounter et QueryPerformanceFrequency.

Microsoft a récemment (2014) publié des informations plus détaillées sur QueryPerformanceCounter:

Voir Acquisition d'horodatages haute résolution (MSDN 2014) pour les détails.

Il s'agit d'un article complet avec de nombreux exemples et une description détaillée. Une lecture incontournable pour les utilisateurs de QPC.

5
Arno

Je pense que les microsecondes sont un peu déraisonnables (sans assistance matérielle). Les millisecondes sont réalisables, mais même alors pas si précises en raison de divers problèmes de résolution de compteur néfastes. Quoi qu'il en soit, j'inclus ma propre classe de minuterie (basée sur std :: chrono) pour votre considération:

#include <type_traits>
#include <chrono>


class Stopwatch final
{
public:

    using elapsed_resolution = std::chrono::milliseconds;

    Stopwatch()
    {
        Reset();
    }

    void Reset()
    {
        reset_time = clock.now();
    }

    elapsed_resolution Elapsed()
    {
        return std::chrono::duration_cast<elapsed_resolution>(clock.now() - reset_time);
    }

private:

    std::chrono::high_resolution_clock clock;
    std::chrono::high_resolution_clock::time_point reset_time;
};

Notez que sous le capot sous Windows std :: chrono :: high_resolution_clock utilise QueryPerformanceCounter, c'est donc la même chose mais portable.

4
Robinson

MSDN affirme que -

Un objet de scénario est un minuteur très précis qui enregistre les événements ETW (suivi d'événements pour Windows) lorsque vous le démarrez et l'arrêtez. Il est conçu pour être utilisé pour l'instrumentation des performances et l'analyse comparative, et est disponible en versions C # et C++. ... En règle générale sur le matériel moderne, un appel à Begin () ou End () prend de l'ordre de la microseconde, et les horodatages résultants sont précis à 100 ns (soit 0,1 microsecondes). ... Les versions sont disponibles pour .NET 3.5 (écrit en C #) et C++ natif, et s'exécutent sur les plates-formes x86 et x64. La classe Scenario a été initialement développée à l'aide de Visual Studio 2008, mais est désormais destinée aux développeurs utilisant Visual Studio 2010.]

De Page d'accueil du scénario . Pour autant que je sache, il a été fourni par les mêmes personnes que PPL.

De plus, vous pouvez lire ceci Horloges et minuteries haute résolution pour la mesure des performances dans Windows .

3
SChepurin

Dans les versions plus récentes de Windows vous voulez probablement GetSystemTimePreciseAsFileTime . Voir Acquisition d'horodatages haute résolution .

Beaucoup de cela varie d'une quantité plutôt malheureuse en fonction du matériel et de la version du système d'exploitation.

2
Craig Ringer

Si vous pouvez utiliser le compilateur Visual Studio 2012 ou supérieur, vous pouvez bien utiliser la bibliothèque standard std :: chrono .

#include <chrono>

::std::chrono::steady_clock::time_point time = std::chrono::steady_clock::now();

Notez que la la version MSVC 2012 peut être précise à seulement 1 ms . Les versions plus récentes doivent être précises jusqu'à une microseconde.

1
SpamBot

Vous pouvez utilisez l'API Performance Counter comme l'a proposé Konrad Rudolf, mais vous devez être averti qu'elle est basée sur la fréquence du processeur. Cette fréquence n'est pas stable lorsque par ex. un mode d'économie d'énergie est activé. Si vous souhaitez utiliser cette API, assurez-vous que le CPU est à une fréquence constante.

Sinon, vous pouvez créer une sorte de système "statistique", corrélant les ticks du CPU à l'horloge du BIOS du PC. Ce dernier est beaucoup moins précis, mais constant.

1
xtofl

En ce qui concerne la réponse de Konrad Rudolph, notez que d'après mon expérience, la fréquence du compteur de performances est d'environ 3,7 MHz, donc d'une précision inférieure à la microseconde, mais certainement pas à la nanoseconde. La fréquence réelle dépend du matériel (et du mode d'économie d'énergie). La précision en nanosecondes est quelque peu déraisonnable dans tous les cas, car les latences d'interruption et les temps de commutation de contexte de processus/thread sont bien plus longs que cela, et c'est également l'ordre de grandeur des instructions machine individuelles.

0
Clifford

rdtsc instruction est la plus précise.

0
Alexey Malistov

Merci pour l'entrée ... même si je n'ai pas pu obtenir une résolution en nano ou microseconde qui aurait été agréable, j'ai cependant pu trouver cela ... peut-être que quelqu'un d'autre le trouvera utile.

    class N_Script_Timer
{
    public:
        N_Script_Timer()
        {
            running = false;
            milliseconds = 0;
            seconds = 0;
            start_t = 0;
            end_t = 0;
        }
        void Start()
        {
            if(running)return;
            running = true;
            start_t = timeGetTime();
        }
        void End()
        {
            if(!running)return;
            running = false;
            end_t = timeGetTime();
            milliseconds = end_t - start_t;
            seconds = milliseconds / (float)1000;
        }
        float milliseconds;
        float seconds;

    private:
        unsigned long start_t;
        unsigned long end_t;
        bool running;
};
0
Kelly Elton

en utilisant QueryPerformanceCounter (pour Windows)

0
aJ.

Voici une classe Timer qui fonctionnera à la fois pour Windows et Linux:

#ifndef INCLUDE_CTIMER_HPP_
#define INCLUDE_CTIMER_HPP_

#if defined(_MSC_VER)
#  define NOMINMAX // workaround a bug in windows.h
#  include <windows.h>
#else
#  include <sys/time.h>
#endif

namespace Utils
{
   class CTimer
   {
   private:
#     if defined(_MSC_VER)
         LARGE_INTEGER m_depart;
#     else
         timeval m_depart;
#     endif

   public:
      inline void start()
      {
#        if defined(_MSC_VER)
            QueryPerformanceCounter(&m_depart);
#        else
            gettimeofday(&m_depart, 0);
#        endif
      };

      inline float GetSecondes() const
      {
#        if defined(_MSC_VER)
            LARGE_INTEGER now;
            LARGE_INTEGER freq;

            QueryPerformanceCounter(&now);
            QueryPerformanceFrequency(&freq);

            return (now.QuadPart - m_depart.QuadPart) / static_cast<float>(freq.QuadPart);
#        else
            timeval now;
            gettimeofday(&now, 0);

            return now.tv_sec - m_depart.tv_sec + (now.tv_usec - m_depart.tv_usec) / 1000000.0f;
#        endif
      };
   };
}
#endif // INCLUDE_CTIMER_HPP_
0
Mathieu Pagé