Quelle est la différence entre std::system_clock
Et std::steady_clock
? (Un exemple de cas illustrant différents résultats/comportements serait formidable).
Si mon objectif est de mesurer avec précision le temps d'exécution des fonctions (comme un point de repère), quel serait le meilleur choix entre std::system_clock
, std::steady_clock
Et std::high_resolution_clock
?
À partir de N3376:
20.11.7.1 [time.clock.system]/1:
Les objets de classe
system_clock
Représentent l'heure de l'horloge murale à partir de l'horloge en temps réel du système.
20.11.7.2 [time.clock.steady]/1:
Les objets de classe
steady_clock
Représentent des horloges pour lesquelles les valeurs detime_point
Ne diminuent jamais à mesure que le temps physique avance et pour lesquelles les valeurs detime_point
Avancent à un taux constant par rapport au temps réel. C'est-à-dire que l'horloge ne peut pas être ajustée.
20.11.7.3 [time.clock.hires]/1:
Les objets de classe
high_resolution_clock
Représentent des horloges avec la période de tick la plus courte.high_resolution_clock
Peut être un synonyme desystem_clock
Ousteady_clock
.
Par exemple, l'horloge à l'échelle du système peut être affectée par quelque chose comme l'heure d'été, l'heure à laquelle l'heure réelle indiquée à un moment donné dans le futur peut en réalité être une heure passée. (Aux États-Unis, par exemple, à l’automne, le temps recule d’une heure et la même heure est vécue "deux fois"). Cependant, steady_clock
N’est pas autorisé à être affecté par de telles choses.
Une autre façon de penser à "stable" dans ce cas est dans les exigences définies dans le tableau de 20.11.3 [time.clock.req]/2:
Dans le tableau 59,
C1
EtC2
Désignent des types d'horloge.t1
Ett2
Sont des valeurs renvoyées parC1::now()
où l'appel renvoyantt1
A lieu avant l'appel renvoyantt2
Et ces deux appels. se produire avantC1::time_point::max()
. [Remarque: cela signifie queC1
Ne s'est pas écoulé entret1
Ett2
. —Fin note]Expression:
C1::is_steady
Retourne:const bool
Sémantique opérationnelle:true
sit1 <= t2
Est toujours vrai et le temps entre les impulsions d'horloge est constant, sinonfalse
.
C'est tout ce que la norme a sur leurs différences.
Si vous souhaitez effectuer une analyse comparative, votre meilleur choix sera probablement std::high_resolution_clock
, Car il est probable que votre plate-forme utilise un minuteur haute résolution (par exemple, QueryPerformanceCounter
sur Windows) pour cette horloge. Toutefois, si vous effectuez des analyses comparatives, vous devriez vraiment envisager d’utiliser des minuteries spécifiques à votre plate-forme, car différentes plates-formes gèrent cela différemment. Par exemple, certaines plates-formes peuvent vous donner un moyen de déterminer le nombre réel de ticks d'horloge requis par le programme (indépendamment des autres processus s'exécutant sur le même processeur). Mieux encore, mettez la main sur un véritable profileur et utilisez-le.
Billy a fourni une excellente réponse basée sur la norme ISO C++ avec laquelle je suis entièrement d'accord. Cependant, il y a un autre aspect de l'histoire: la vie réelle. Il semble qu’à l’heure actuelle il n’existe vraiment aucune différence entre ces horloges lors de l’implémentation de compilateurs populaires:
gcc 4.8:
#ifdef _GLIBCXX_USE_CLOCK_MONOTONIC
...
#else
typedef system_clock steady_clock;
#endif
typedef system_clock high_resolution_clock;
Visual Studio 2012:
class steady_clock : public system_clock
{ // wraps monotonic clock
public:
static const bool is_monotonic = true; // retained
static const bool is_steady = true;
};
typedef system_clock high_resolution_clock;
Dans le cas de gcc, vous pouvez vérifier si vous utilisez une horloge fixe simplement en vérifiant is_steady
et se comporter en conséquence. Cependant VS2012 semble tricher un peu ici :-)
Si vous avez besoin d'une horloge de haute précision, je vous recommande d'écrire votre propre horloge conforme à l'interface d'horloge officielle C++ 11 et d'attendre que les implémentations se mettent à jour. Ce sera une bien meilleure approche que d’utiliser l’API spécifique au système d’exploitation directement dans votre code. Pour Windows, vous pouvez le faire comme ça:
// Self-made Windows QueryPerformanceCounter based C++11 API compatible clock
struct qpc_clock {
typedef std::chrono::nanoseconds duration; // nanoseconds resolution
typedef duration::rep rep;
typedef duration::period period;
typedef std::chrono::time_point<qpc_clock, duration> time_point;
static bool is_steady; // = true
static time_point now()
{
if(!is_inited) {
init();
is_inited = true;
}
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return time_point(duration(static_cast<rep>((double)counter.QuadPart / frequency.QuadPart *
period::den / period::num)));
}
private:
static bool is_inited; // = false
static LARGE_INTEGER frequency;
static void init()
{
if(QueryPerformanceFrequency(&frequency) == 0)
throw std::logic_error("QueryPerformanceCounter not supported: " + std::to_string(GetLastError()));
}
};
Pour Linux, c'est encore plus facile. Il suffit de lire la page de manuel de clock_gettime
et modifiez le code ci-dessus.
Implémentation de GCC 5.3.0
C++ stdlib est dans la source GCC:
high_resolution_clock
est un alias pour system_clock
system_clock
transmet au premier des éléments suivants qui sont disponibles: clock_gettime(CLOCK_REALTIME, ...)
gettimeofday
time
steady_clock
transmet au premier des éléments suivants qui sont disponibles: clock_gettime(CLOCK_MONOTONIC, ...)
system_clock
Alors CLOCK_REALTIME
Vs CLOCK_MONOTONIC
Est expliqué à: Différence entre CLOCK_REALTIME et CLOCK_MONOTONIC?