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?
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.
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()
.
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
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
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:
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.
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++.
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.
STLSoft a une Performance Library , qui inclut un ensemble de classes de minuterie, dont certaines fonctionnent sous UNIX et Windows.
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.
Je ne suis pas sûr de vos besoins. Si vous souhaitez calculer un intervalle de temps, veuillez consulter le fil ci-dessous.
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
Si vous utilisez le framework Qt dans le projet, la meilleure solution consiste probablement à utiliser QElapsedTimer.