Différence entre std :: system_clock et std :: stabil_clock?

97

Quelle est la différence entre std::system_clocket std::steady_clock? (Un exemple de cas illustrant différents résultats / comportements serait formidable).

Si mon objectif est de mesurer précisément le temps d'exécution des fonctions (comme un benchmark), quel serait le meilleur choix entre std::system_clock, std::steady_clocket std::high_resolution_clock?

Vincent
la source
9
Pour commencer, l'horloge_système peut ne pas être stable.
James McNellis
12
@CharlesSalvia Je ne peux pas parler pour d'autres plates-formes, mais le system_clockn'est pas stable sur Windows. Sous Windows, l'heure système peut être modifiée à n'importe quelle valeur arbitraire par tout utilisateur suffisamment privilégié. De plus, le service de synchronisation de l'heure peut ajuster l'heure du système en arrière si nécessaire. Je m'attends à ce que la plupart des autres plates-formes aient des fonctionnalités similaires permettant d'ajuster l'heure du système.
James McNellis
3
@Charles: La plupart des boîtes POSIX que je connais sont affectées de la même manière et verront leur heure changer si l'utilisateur change l'heure.
Billy ONeal
5
Réponse vidéo à cette question: youtube.com/watch?v=P32hvk8b13M&t=48m44s
Howard Hinnant
1
@CharlesSalvia. D'après ma propre expérience d'analyse de la sortie de synchronisation de dizaines de systèmes d'acquisition de données sur PC, le temps d'un ordinateur n'est pas constant. Linux, Windows et les appels système spécifiques utilisés sont inconnus, mais le point commun est des différences de temps négatives fréquentes entre les valeurs de temps suivantes. Le temps en ligne droite n'est pas la norme.
Tyson Hilmer

Réponses:

71

Depuis N3376:

20.11.7.1 [time.clock.system] / 1:

Les objets de classe system_clockreprésentent l'heure de l'horloge murale à partir de l'horloge temps réel du système.

20.11.7.2 [heure.horloge.steady] / 1:

Les objets de classe steady_clockreprésentent des horloges pour lesquelles les valeurs de time_pointne diminuent jamais à mesure que le temps physique avance et pour lesquelles des valeurs d' time_pointavance à un rythme constant par rapport au temps réel. Autrement dit, l'horloge peut ne pas être ajustée.

20.11.7.3 [time.clock.hires] / 1:

Les objets de classe high_resolution_clockreprésentent les horloges avec la période de graduation la plus courte. high_resolution_clockpeut être un synonyme de system_clockou steady_clock.

Par exemple, l'horloge à l'échelle du système peut être affectée par quelque chose comme l'heure d'été, auquel moment l'heure réelle indiquée à un moment donné dans le futur peut en fait être une heure du passé. (Par exemple aux États-Unis, à l'automne, le temps recule d'une heure, de sorte que la même heure est vécue "deux fois") Cependant, il steady_clockn'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 du 20.11.3 [time.clock.req] / 2:

Dans le tableau 59 C1, C2notez les types d'horloge. t1et t2sont des valeurs renvoyées par C1::now()où l'appel de retour t1se produit avant le retour de l'appel t2et ces deux appels se produisent avant C1::time_point::max(). [Remarque: cela signifie C1que ne s'est pas enroulé entre t1et t2. —End note]

Expression: C1::is_steady
Renvoie: const bool
Sémantique opérationnelle: truesi t1 <= t2est toujours vrai et le temps entre les tics d'horloge est constant, sinon false.

C'est tout ce que la norme a sur leurs différences.

Si vous voulez faire un benchmarking, votre meilleur pari sera probablement std::high_resolution_clock, car il est probable que votre plate-forme utilise une minuterie haute résolution (par exemple QueryPerformanceCountersous Windows) pour cette horloge. Cependant, si vous effectuez une analyse comparative, vous devriez vraiment envisager d'utiliser des minuteries spécifiques à la plate-forme pour votre référence, car différentes plates-formes gèrent cela différemment. Par exemple, certaines plates-formes peuvent vous donner des moyens de déterminer le nombre réel de cycles d'horloge requis par le programme (indépendamment des autres processus exécutés sur le même processeur). Mieux encore, mettez la main sur un véritable profileur et utilisez-le.

Billy ONeal
la source
1
@Charles: Voulez-vous préciser dans la norme où c'est le cas? Cela semble indiquer clairement le contraire.
Billy ONeal
9
@Charles: De plus, l'heure POSIX n'est pas "stable" - si l'utilisateur modifie le réglage de l'heure sur son ordinateur, l'heure POSIX changera. Si vous faites cuire un œuf et que vous avez besoin d'une minuterie qui dure 4 minutes, vous en avez besoin pour durer 4 minutes même si l'heure actuelle est modifiée. Si vous avez défini un minuteur pour une réunion le 5 à 3, vous avez absolument besoin que ce minuteur change si l'heure locale change. D'où la différence entre steady_clocket system_clockici.
Billy ONeal
1
@ 5gon: Rien n'exige que ce system_clocksoit UTC.
Billy ONeal
1
@CharlesSalvia Veuillez également noter que puisque le temps POSIX est lié à UTC, et que UTC a des secondes intercalaires (cf. en.wikipedia.org/wiki/Unix_time#Leap_seconds ). Cela signifie que même si l'heure sur une machine n'est jamais ajustée, l'heure C / POSIX peut être non monotone.
Michael Schlottke-Lakemper
3
UPDATE (Visual Studio 2015) L'implémentation de stabil_clock a changé [.....] stabil_clock est maintenant basé sur QueryPerformanceCounter () et high_resolution_clock est maintenant un typedef pour stabil_clock. Cité de msdn.microsoft.com/en-us/library/hh874757.aspx
felix-b
47

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 vraie vie. Il semble que pour le moment, il n'y ait vraiment aucune différence entre ces horloges dans la mise en œuvre des 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 gérez une horloge régulière simplement en vérifiant is_steadyet en vous comportant en conséquence. Cependant VS2012 semble tricher un peu ici :-)

Si vous avez besoin d'une horloge haute précision, je vous recommande pour l'instant d'écrire votre propre horloge conforme à l'interface d'horloge officielle C ++ 11 et d'attendre que les implémentations rattrapent leur retard. Ce sera une bien meilleure approche que d'utiliser une 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. Lisez simplement la page de manuel clock_gettimeet modifiez le code ci-dessus.

Mateusz Pusz
la source
19
L'implémentation de VC ++ 2012 a été reconnue comme un bogue par le responsable de la bibliothèque standard de MS.
ildjarn
5
Pour ceux qui sont intéressés, c'est un lien vers ce bug
Ben Voigt
1
Boost utilise QueryPerformanceCounter, donc utiliser boost :: chrono est une bonne solution de contournement à ce bogue jusqu'à la sortie de Visual Studio 14
Mohamed El-Nakib
Et voici les appels POSIX à ceux qui sont transférés sur GCC 5.3.0: stackoverflow.com/a/36700301/895245
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
19

Implémentation de GCC 5.3.0

C ++ stdlib est à l'intérieur de la source GCC:

  • high_resolution_clock est un alias pour system_clock
  • system_clock transfère au premier des éléments suivants disponibles:
    • clock_gettime(CLOCK_REALTIME, ...)
    • gettimeofday
    • time
  • steady_clock transfère au premier des éléments suivants disponibles:
    • clock_gettime(CLOCK_MONOTONIC, ...)
    • system_clock

Alors CLOCK_REALTIMEvs CLOCK_MONOTONICest expliqué à: Différence entre CLOCK_REALTIME et CLOCK_MONOTONIC?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
2

Peut-être, la différence la plus significative est le fait que le point de départ de std::chrono:system_clockest le 1.1.1970, soi-disant époque UNIX. De l'autre côté, pour std::chrono::steady_clockgénéralement le temps de démarrage de votre PC et il est le plus approprié pour mesurer les intervalles.

Silviu
la source