web-dev-qa-db-fra.com

Minuterie haute résolution multiplate-forme C++

Je cherche à implémenter un mécanisme de minuterie simple en C++. Le code devrait fonctionner sous Windows et Linux. La résolution doit être aussi précise que possible (au moins une milliseconde). Cela servira simplement à suivre le temps qui passe, et non à mettre en œuvre une conception événementielle Quel est le meilleur outil pour y parvenir?

68
Amish Programmer

Pour C++ 03:

Boost.Timer pourrait fonctionner, mais cela dépend de la fonction C clock et il est donc possible que la résolution ne soit pas satisfaisante.

Boost.Date_Time inclut une classe ptime qui a déjà été recommandée pour Stack Overflow. Voir sa documentation sur microsec_clock::local_time et microsec_clock::universal_time, mais notez sa mise en garde selon laquelle "les systèmes Win32 n'atteignent souvent pas la résolution en microsecondes via cette API".

STLsoft fournit, entre autres choses, des wrappers C++ inter-plateformes (Windows et Linux/Unix) minces autour d’API spécifiques à un système d’exploitation. Sa bibliothèque de performances a plusieurs classes qui feraient ce dont vous avez besoin. (Pour le rendre multi-plateforme, choisissez une classe telle que performance_counter qui existe à la fois dans les espaces de noms winstl et unixstl, puis utilisez celui qui correspond à votre plate-forme.)

Pour C++ 11 et supérieur:

Cette bibliothèque est intégrée à la bibliothèque std::chrono. Voir cette réponse par @HowardHinnant pour plus de détails.

38
Josh Kelley

Réponse mise à jour pour une ancienne question:

En C++ 11, vous pouvez accéder de manière portable à la minuterie avec la résolution la plus élevée avec:

#include <iostream>
#include <chrono>
#include "chrono_io"

int main()
{
    typedef std::chrono::high_resolution_clock Clock;
    auto t1 = Clock::now();
    auto t2 = Clock::now();
    std::cout << t2-t1 << '\n';
}

Exemple de sortie:

74 nanoseconds

"chrono_io" est une extension destinée à faciliter les problèmes d’entrée/sortie avec ces nouveaux types et est disponible gratuitement ici .

Il existe également une implémentation de <chrono> disponible dans Boost (il se peut qu’elle soit toujours sur le bout du coffre, mais elle n’est pas certaine qu’elle ait été publiée).

Mettre à jour

C'est en réponse au commentaire de Ben ci-dessous que les appels suivants à std::chrono::high_resolution_clock prennent plusieurs millisecondes dans VS11. Vous trouverez ci-dessous une solution de contournement compatible avec <chrono>. Cependant, cela ne fonctionne que sur du matériel Intel, vous devez plonger dans Assembly en ligne (la syntaxe varie en fonction du compilateur) et vous devez connecter la vitesse d'horloge de la machine à l'horloge:

#include <chrono>

struct clock
{
    typedef unsigned long long                 rep;
    typedef std::ratio<1, 2800000000>          period; // My machine is 2.8 GHz
    typedef std::chrono::duration<rep, period> duration;
    typedef std::chrono::time_point<clock>     time_point;
    static const bool is_steady =              true;

    static time_point now() noexcept
    {
        unsigned lo, hi;
        asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
        return time_point(duration(static_cast<rep>(hi) << 32 | lo));
    }

private:

    static
    unsigned
    get_clock_speed()
    {
        int mib[] = {CTL_HW, HW_CPU_FREQ};
        const std::size_t namelen = sizeof(mib)/sizeof(mib[0]);
        unsigned freq;
        size_t freq_len = sizeof(freq);
        if (sysctl(mib, namelen, &freq, &freq_len, nullptr, 0) != 0)
            return 0;
        return freq;
    }

    static
    bool
    check_invariants()
    {
        static_assert(1 == period::num, "period must be 1/freq");
        assert(get_clock_speed() == period::den);
        static_assert(std::is_same<rep, duration::rep>::value,
                      "rep and duration::rep must be the same type");
        static_assert(std::is_same<period, duration::period>::value,
                      "period and duration::period must be the same type");
        static_assert(std::is_same<duration, time_point::duration>::value,
                      "duration and time_point::duration must be the same type");
        return true;
    }

    static const bool invariants;
};

const bool clock::invariants = clock::check_invariants();

Donc, ce n'est pas portable. Mais si vous voulez expérimenter avec une horloge haute résolution sur votre propre matériel Intel, elle n’est pas plus fine que cela. Bien que cela soit prévenu, les vitesses d'horloge actuelles peuvent changer de façon dynamique (elles ne sont pas vraiment une constante de compilation). Et avec une machine multi-processeurs, vous pouvez même obtenir des horodatages de différents processeurs. Néanmoins, les expériences sur mon matériel fonctionnent assez bien. Si vous êtes coincé avec une résolution en millisecondes, cela pourrait être une solution de contournement.

Cette horloge a une durée en termes de vitesse d'horloge de votre processeur (comme vous l'avez signalé). C'est à dire. pour moi, cette horloge se déclenche une fois tous les 1/2 800 000 000 de secondes. Si vous le souhaitez, vous pouvez convertir cela en nanosecondes (par exemple) avec:

using std::chrono::nanoseconds;
using std::chrono::duration_cast;
auto t0 = clock::now();
auto t1 = clock::now();
nanoseconds ns = duration_cast<nanoseconds>(t1-t0);

La conversion tronquera les fractions d'un cycle de l'unité centrale pour former la nanoseconde. D'autres modes d'arrondi sont possibles, mais c'est un sujet différent.

Pour moi, cela retournera une durée aussi basse que 18 ticks d'horloge, ce qui tronque à 6 nanosecondes.

J'ai ajouté des "vérifications invariantes" à l'horloge ci-dessus, la plus importante étant de vérifier que le clock::period est correct pour la machine. Encore une fois, ce n'est pas du code portable, mais si vous utilisez cette horloge, vous vous êtes déjà engagé à le faire. La fonction privée get_clock_speed() présentée ici obtient la fréquence maximale de l'unité centrale sous OS X et doit correspondre au même nombre que le dénominateur constant de clock::period.

L'ajout de cela vous fera gagner un peu de temps de débogage lorsque vous portez ce code sur votre nouvelle machine et oubliez de mettre à jour le clock::period à la vitesse de votre nouvelle machine. Toutes les vérifications sont effectuées au moment de la compilation ou au démarrage du programme. Cela n'aura donc aucun impact sur les performances de clock::now().

137
Howard Hinnant

Matthew Wilson / Les bibliothèques STLSoft fournissent plusieurs types de minuterie, avec des interfaces congruentes pour vous permettre de brancher et de jouer. Parmi les offres, il y a des minuteries à faible coût mais à faible résolution, et celles à haute résolution mais très coûteuses. Il en existe aussi pour mesurer les temps de pré-filetage et pour mesurer les temps par processus, ainsi que pour tous les temps écoulés.

Il existe un article exhaustif le couvrant dans Dr. Dobb d'il y a quelques années, bien qu'il ne couvre que celles de Windows, celles définies dans le sous-projet WinSTL. STLSoft fournit également des timers UNIX dans le sous-projet UNIXSTL et vous pouvez utiliser celui "PlatformSTL", qui inclut le système UNIX ou Windows selon le cas, comme dans les cas suivants:

#include <platformstl/performance/performance_counter.hpp>
#include <iostream>

int main()
{
    platformstl::performance_counter c;

    c.start();
    for(int i = 0; i < 1000000000; ++i);
    c.stop();

    std::cout << "time (s): " << c.get_seconds() << std::endl;
    std::cout << "time (ms): " << c.get_milliseconds() << std::endl;
    std::cout << "time (us): " << c.get_microseconds() << std::endl;
}

HTH

6
dcw

La bibliothèque open source StlSoft fournit une minuterie plutôt bonne sur les plateformes Windows et Linux. Si vous souhaitez le mettre en œuvre vous-même, il suffit de regarder leurs sources.

5
Malte Clasen

La bibliothèque ACE a également des minuteries portables haute résolution.

Doxygen pour minuterie haute résolution:
http://www.dre.vanderbilt.edu/Doxygen/5.7.2/html/ace/a00244.html

5
Dan

Ce n’est pas la meilleure des réponses, mais voici quelques conversations sur un site de développement de jeux concernant les minuteries haute résolution:

  1. http://www.gamedev.net/topic/374327-timing-is-everything/
  2. http://www.gamedev.net/topic/471804-high-resolution-timer/
  3. http://www.gamedev.net/topic/40600-high-resolution-timing-in-games/
5

Je recommande fortement la bibliothèque boost :: posix_time pour cela. Il prend en charge les minuteries dans diverses résolutions jusqu’à quelques microsecondes, je crois.

4
Maciek

SDL2 dispose d’un excellent minuteur haute résolution multiplate-forme. Si toutefois vous avez besoin d'une précision inférieure à une milliseconde, j'ai écrit une très petite bibliothèque de minuteries multiplateformes ici . Elle est compatible avec les versions C++ 03 et C++ 11/supérieures du C++.

3
metamorphosis

J'ai déjà vu cela implémenté à quelques reprises sous forme de solutions internes à sources fermées .... qui utilisaient toutes des solutions #ifdef autour des timers haute résolution Windows natifs et des timers du noyau Linux utilisant struct timeval (voir man timeradd) de l'autre main.

Vous pouvez résumer ceci et quelques projets Open Source l'ont fait - le dernier que j'ai examiné était la classe CoinOR CoinTimer mais il y en a sûrement plus.

3
Dirk Eddelbuettel

STLSoft a une Performance Library , qui inclut un ensemble de classes de minuterie, dont certaines fonctionnent sous UNIX et Windows.

2
JamieH

La première réponse aux questions de la bibliothèque C++ est généralement BOOST: http://www.boost.org/doc/libs/1_40_0/libs/timer/timer.htm . Est-ce que ça fait ce que tu veux? Probablement pas mais c'est un début.

Le problème est que vous voulez des fonctions portables et les minuteries ne sont pas universelles dans les systèmes d'exploitation.

2
jmucchiello

Je ne suis pas sûr de vos besoins. Si vous souhaitez calculer un intervalle de temps, veuillez consulter le fil ci-dessous. 

Calcul du temps écoulé dans un programme C en millisecondes

1
Satbir

Je suis en retard pour la fête, mais je travaille dans une base de code héritée qui ne peut pas encore être mise à niveau vers c ++ 11. Aucun membre de notre équipe n’est très compétent en c ++. Il est donc difficile d’ajouter une bibliothèque telle que STL (outre les préoccupations potentielles soulevées par d’autres sur les problèmes de déploiement). J'avais vraiment besoin d'un minuteur multiplateforme extrêmement simple, capable de fonctionner de manière autonome sans autre bibliothèque que les bibliothèques système standard. Voici ce que j'ai trouvé:

http://www.songho.ca/misc/timer/timer.html

Republiez la source entière ici juste pour qu'elle ne soit pas perdue si le site meurt un jour:

    //////////////////////////////////////////////////////////////////////////////
// Timer.cpp
// =========
// High Resolution Timer.
// This timer is able to measure the elapsed time with 1 micro-second accuracy
// in both Windows, Linux and Unix system 
//
//  AUTHOR: Song Ho Ahn ([email protected]) - http://www.songho.ca/misc/timer/timer.html
// CREATED: 2003-01-13
// UPDATED: 2017-03-30
//
// Copyright (c) 2003 Song Ho Ahn
//////////////////////////////////////////////////////////////////////////////

#include "Timer.h"
#include <stdlib.h>

///////////////////////////////////////////////////////////////////////////////
// constructor
///////////////////////////////////////////////////////////////////////////////
Timer::Timer()
{
#if defined(WIN32) || defined(_WIN32)
    QueryPerformanceFrequency(&frequency);
    startCount.QuadPart = 0;
    endCount.QuadPart = 0;
#else
    startCount.tv_sec = startCount.tv_usec = 0;
    endCount.tv_sec = endCount.tv_usec = 0;
#endif

    stopped = 0;
    startTimeInMicroSec = 0;
    endTimeInMicroSec = 0;
}



///////////////////////////////////////////////////////////////////////////////
// distructor
///////////////////////////////////////////////////////////////////////////////
Timer::~Timer()
{
}



///////////////////////////////////////////////////////////////////////////////
// start timer.
// startCount will be set at this point.
///////////////////////////////////////////////////////////////////////////////
void Timer::start()
{
    stopped = 0; // reset stop flag
#if defined(WIN32) || defined(_WIN32)
    QueryPerformanceCounter(&startCount);
#else
    gettimeofday(&startCount, NULL);
#endif
}



///////////////////////////////////////////////////////////////////////////////
// stop the timer.
// endCount will be set at this point.
///////////////////////////////////////////////////////////////////////////////
void Timer::stop()
{
    stopped = 1; // set timer stopped flag

#if defined(WIN32) || defined(_WIN32)
    QueryPerformanceCounter(&endCount);
#else
    gettimeofday(&endCount, NULL);
#endif
}



///////////////////////////////////////////////////////////////////////////////
// compute elapsed time in micro-second resolution.
// other getElapsedTime will call this first, then convert to correspond resolution.
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTimeInMicroSec()
{
#if defined(WIN32) || defined(_WIN32)
    if(!stopped)
        QueryPerformanceCounter(&endCount);

    startTimeInMicroSec = startCount.QuadPart * (1000000.0 / frequency.QuadPart);
    endTimeInMicroSec = endCount.QuadPart * (1000000.0 / frequency.QuadPart);
#else
    if(!stopped)
        gettimeofday(&endCount, NULL);

    startTimeInMicroSec = (startCount.tv_sec * 1000000.0) + startCount.tv_usec;
    endTimeInMicroSec = (endCount.tv_sec * 1000000.0) + endCount.tv_usec;
#endif

    return endTimeInMicroSec - startTimeInMicroSec;
}



///////////////////////////////////////////////////////////////////////////////
// divide elapsedTimeInMicroSec by 1000
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTimeInMilliSec()
{
    return this->getElapsedTimeInMicroSec() * 0.001;
}



///////////////////////////////////////////////////////////////////////////////
// divide elapsedTimeInMicroSec by 1000000
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTimeInSec()
{
    return this->getElapsedTimeInMicroSec() * 0.000001;
}



///////////////////////////////////////////////////////////////////////////////
// same as getElapsedTimeInSec()
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTime()
{
    return this->getElapsedTimeInSec();
}

et le fichier d'en-tête:

//////////////////////////////////////////////////////////////////////////////
// Timer.h
// =======
// High Resolution Timer.
// This timer is able to measure the elapsed time with 1 micro-second accuracy
// in both Windows, Linux and Unix system 
//
//  AUTHOR: Song Ho Ahn ([email protected]) - http://www.songho.ca/misc/timer/timer.html
// CREATED: 2003-01-13
// UPDATED: 2017-03-30
//
// Copyright (c) 2003 Song Ho Ahn
//////////////////////////////////////////////////////////////////////////////

#ifndef TIMER_H_DEF
#define TIMER_H_DEF

#if defined(WIN32) || defined(_WIN32)   // Windows system specific
#include <windows.h>
#else          // Unix based system specific
#include <sys/time.h>
#endif


class Timer
{
public:
    Timer();                                    // default constructor
    ~Timer();                                   // default destructor

    void   start();                             // start timer
    void   stop();                              // stop the timer
    double getElapsedTime();                    // get elapsed time in second
    double getElapsedTimeInSec();               // get elapsed time in second (same as getElapsedTime)
    double getElapsedTimeInMilliSec();          // get elapsed time in milli-second
    double getElapsedTimeInMicroSec();          // get elapsed time in micro-second


protected:


private:
    double startTimeInMicroSec;                 // starting time in micro-second
    double endTimeInMicroSec;                   // ending time in micro-second
    int    stopped;                             // stop flag 
#if defined(WIN32) || defined(_WIN32)
    LARGE_INTEGER frequency;                    // ticks per second
    LARGE_INTEGER startCount;                   //
    LARGE_INTEGER endCount;                     //
#else
    timeval startCount;                         //
    timeval endCount;                           //
#endif
};

#endif // TIMER_H_DEF
0
Patrick

Si vous utilisez le framework Qt dans le projet, la meilleure solution consiste probablement à utiliser QElapsedTimer.

0
lpapp