81 votes

Minuteur haute résolution C++ multiplateforme

Je cherche à mettre en œuvre un mécanisme simple de minuterie en C++. Le code doit fonctionner sous Windows et Linux. La résolution doit être aussi précise que possible (au moins une précision de l'ordre de la milliseconde). Ce mécanisme sera utilisé pour suivre simplement le passage du temps, et non pour mettre en œuvre une quelconque conception événementielle. Quel est le meilleur outil pour y parvenir ?

4 votes

Soyez plus précis. Chronométrez-vous l'appel d'une fonction ou voulez-vous recevoir un signal après une période de temps donnée ? Il s'agit là de deux applications de chronométrage "simples", mais elles sont mises en œuvre de manière très différente. Notez l'utilisation du mot "simple" entre guillemets : le chronométrage dans les ordinateurs à usage général n'est jamais "simple".

0 votes

161voto

Howard Hinnant Points 59526

Réponse actualisée à une vieille question :

En C++11, vous pouvez obtenir de manière portative la plus haute résolution du timer 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 pour faciliter les problèmes d'E/S avec ces nouveaux types et est disponible gratuitement. aquí .

Il existe également une implémentation de <chrono> disponible en boost (peut être encore sur tip-of-trunk, pas sûr qu'il ait été publié).

Mise à jour

Ceci est en réponse au commentaire de Ben ci-dessous, selon lequel les appels subséquents à std::chrono::high_resolution_clock prennent plusieurs millisecondes dans le VS11. Vous trouverez ci-dessous une <chrono> -compatible. Cependant, elle ne fonctionne que sur le matériel Intel, vous devez vous plonger dans l'assemblage en ligne (la syntaxe pour le faire varie selon le compilateur), et vous devez câbler la vitesse d'horloge de la machine dans 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();

Ce n'est donc pas portable. Mais si vous voulez expérimenter avec une horloge haute résolution sur votre propre matériel Intel, il n'y a rien de plus fin. Mais attention, les vitesses d'horloge actuelles peuvent changer dynamiquement (elles ne sont pas vraiment une constante de compilation). Et avec une machine multiprocesseur, vous pouvez même obtenir des horodatages de différents processeurs. Mais malgré tout, 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 rechange.

Cette horloge a une durée en termes de vitesse d'horloge de votre processeur (comme vous l'avez indiqué). En d'autres termes, pour moi, cette horloge tique une fois tous les 1/2,800,000,000 de seconde. Si vous le souhaitez, vous pouvez convertir cette durée 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 du processeur pour former la nanoseconde. D'autres modes d'arrondi sont possibles, mais c'est un autre sujet.

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

J'ai ajouté quelques "vérifications d'invariants" à l'horloge ci-dessus, dont la plus importante consiste à vérifier que la fonction clock::period est correct pour la machine. Encore une fois, ce n'est pas un code portable, mais si vous utilisez cette horloge, vous vous êtes déjà engagé à le faire. Le code privé get_clock_speed() montrée ici permet d'obtenir la fréquence maximale du processeur sous OS X, et cela devrait être le même nombre que le dénominateur constant de la fonction clock::period .

Cet ajout vous permettra de gagner un peu de temps de débogage lorsque vous porterez ce code sur votre nouvelle machine et que vous oublierez de mettre à jour l'adresse de l'utilisateur. clock::period à la vitesse de votre nouvelle machine. Toutes les vérifications sont effectuées soit au moment de la compilation, soit au moment du démarrage du programme. Il n'y aura donc pas d'impact sur les performances de clock::now() le moins du monde.

2 votes

Dans Visual Studio 11, l'intervalle non nul le plus court pour high_resolution_clock est de plusieurs millisecondes, malheureusement.

6 votes

Il m'a fallu quelques secondes pour m'en rendre compte... des millions de nanosecondes sur une plate-forme où la vitesse d'horloge est une fraction de nanoseconde. Wow !!! J'espérais voir des plateformes où des fractions de nanoseconde seraient mesurables. Je pensais que mes résultats de plusieurs dizaines de nanosecondes n'étaient pas si impressionnants.

0 votes

Oui, j'ai effectivement soumis ce problème en tant que bogue pour Visual Studio 11. Cependant, comme il n'est pas critique, je ne m'attends pas à ce qu'il soit corrigé dans cette version.

44voto

Josh Kelley Points 24438

Pour C++03 :

Boost.Timer pourrait fonctionner, mais cela dépend de la fonction C clock et peuvent donc ne pas avoir une résolution suffisante pour vous.

Boost.Date_Time comprend un ptime classe qui a déjà été recommandé sur Stack Overflow. Voir sa documentation sur microsec_clock::local_time y microsec_clock::universal_time mais notez l'avertissement suivant : "Les systèmes Win32 n'atteignent souvent pas une résolution de l'ordre de la microseconde via cette API".

STLsoft fournit, entre autres, des enveloppes fines multiplateformes (Windows et Linux/Unix) en C++ autour d'API spécifiques au système d'exploitation. Son site bibliothèque de performance a plusieurs classes qui feraient ce dont vous avez besoin. (Pour le rendre multiplateforme, choisissez une classe comme performance_counter qui existe à la fois dans le winstl y unixstl puis utilisez l'espace de noms correspondant à votre plate-forme).

Pour C++11 et plus :

El std::chrono a cette fonctionnalité intégrée. Voir cette réponse par @HowardHinnant pour plus de détails.

7 votes

Comme il s'agit d'une question/réponse célèbre, une mise à jour serait la bienvenue. Plus précisément, cela pourrait être réalisé d'une manière standard et portable en utilisant les fonctionnalités modernes du C++, telles que <chrono> y <thread> ? Si possible, comment ?

6voto

dcw Points 2181

Matthew Wilson 's Bibliothèques STLSoft proposent plusieurs types de minuteurs, avec des interfaces congruentes pour que vous puissiez les brancher et les utiliser. Parmi les offres, on trouve des minuteurs à faible coût mais à faible résolution, et d'autres à haute résolution mais à coût élevé. Il existe également des minuteurs pour mesurer les temps de pré-fil et les temps par processus, ainsi que tous ceux qui mesurent les temps écoulés.

Il y a une liste exhaustive article sur le sujet dans le Dr. Dobb's d'il y a quelques années, bien qu'il ne couvre que ceux de Windows, ceux définis dans le sous-projet WinSTL. STLSoft prévoit également des timers UNIX dans le sous-projet UNIXSTL, et vous pouvez utiliser celui de "PlatformSTL", qui inclut celui d'UNIX ou de Windows selon le cas, comme dans :

#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

5voto

Malte Clasen Points 3989

El StlSoft La bibliothèque open source fournit une bon timer sur les plateformes Windows et Linux. Si vous voulez l'implémenter vous-même, jetez un coup d'oeil à leurs sources.

5voto

La bibliothèque ACE dispose également de minuteries portables à haute résolution.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X