¿Diferencia entre std :: system_clock y std :: stable_clock?

97

¿Cuál es la diferencia entre std::system_clocky std::steady_clock? (Un caso de ejemplo que ilustre diferentes resultados / comportamientos sería genial).

Si mi objetivo es medir con precisión el tiempo de ejecución de las funciones (como un punto de referencia), ¿cuál sería la mejor elección entre std::system_clock, std::steady_clocky std::high_resolution_clock?

Vincent
fuente
9
Para empezar, es posible que system_clock no sea estable.
James McNellis
12
@CharlesSalvia No puedo hablar por otras plataformas, pero system_clockno es estable en Windows. En Windows, la hora del sistema puede ser alterada a cualquier valor arbitrario por cualquier usuario con suficientes privilegios. Además, el servicio de sincronización horaria puede ajustar la hora del sistema hacia atrás si es necesario. Espero que la mayoría de las otras plataformas tengan características similares que permitan ajustar la hora del sistema.
James McNellis
3
@Charles: La mayoría de las cajas POSIX que conozco se ven afectadas de manera similar y su hora cambiará si el usuario cambia la hora.
Billy ONeal
5
Respuesta en video a esta pregunta: youtube.com/watch?v=P32hvk8b13M&t=48m44s
Howard Hinnant
1
@CharlesSalvia. Según mi propia experiencia analizando la salida de tiempo de docenas de sistemas de adquisición de datos de PC, el tiempo desde una computadora no es constante. Se desconocen Linux, Windows y las llamadas al sistema específicas utilizadas, pero lo común son las frecuentes diferencias de tiempo negativas entre los valores de tiempo posteriores. El tiempo en línea recta no es la norma.
Tyson Hilmer

Respuestas:

71

Desde N3376:

20.11.7.1 [time.clock.system] / 1:

Los objetos de clase system_clockrepresentan la hora del reloj de pared del reloj en tiempo real de todo el sistema.

20.11.7.2 [time.clock.staady] / 1:

Los objetos de clase steady_clockrepresentan relojes cuyos valores de time_pointnunca disminuyen a medida que avanza el tiempo físico y cuyos valores de time_pointavance a una tasa constante en relación con el tiempo real. Es decir, es posible que el reloj no se ajuste.

20.11.7.3 [time.clock.hires] / 1:

Los objetos de clase high_resolution_clockrepresentan relojes con el período de tic más corto. high_resolution_clockpuede ser sinónimo de system_clocko steady_clock.

Por ejemplo, el reloj de todo el sistema puede verse afectado por algo como el horario de verano, momento en el que la hora real que aparece en algún momento en el futuro puede ser en realidad una hora del pasado. (Por ejemplo, en los EE. UU., En el otoño el tiempo retrocede una hora, por lo que la misma hora se experimenta "dos veces") Sin embargo, steady_clockno se permite verse afectado por tales cosas.

Otra forma de pensar en "estable" en este caso es en los requisitos definidos en la tabla de 20.11.3 [time.clock.req] / 2:

En la Tabla 59 C1y C2denote los tipos de reloj. t1y t2son valores devueltos por C1::now()donde t1ocurre la devolución de la llamada antes de la devolución de la llamada t2y ambas llamadas ocurren antes C1::time_point::max(). [Nota: esto significa que C1no pasó entre t1y t2. —Nota final]

Expresión: C1::is_steady
Devuelve: const bool
Semántica operativa: truesi t1 <= t2siempre es verdadero y el tiempo entre tics del reloj es constante, en caso contrario false.

Eso es todo lo que tiene el estándar sobre sus diferencias.

Si desea realizar evaluaciones comparativas, su mejor opción probablemente será std::high_resolution_clock, porque es probable que su plataforma utilice un temporizador de alta resolución (por ejemplo, QueryPerformanceCounteren Windows) para este reloj. Sin embargo, si está realizando una evaluación comparativa, debería considerar el uso de temporizadores específicos de la plataforma para su evaluación comparativa, porque las diferentes plataformas manejan esto de manera diferente. Por ejemplo, algunas plataformas pueden brindarle algún medio para determinar el número real de tics de reloj que requiere el programa (independientemente de otros procesos que se ejecutan en la misma CPU). Mejor aún, consigue un generador de perfiles real y úsalo.

Billy ONeal
fuente
1
@Charles: ¿Le importaría señalar en el estándar dónde es ese el caso? Parece indicar claramente lo contrario.
Billy ONeal
9
@Charles: Además, el tiempo POSIX no es "estable"; si el usuario cambia la configuración de tiempo en su computadora, el tiempo POSIX cambiará. Si está cocinando un huevo y necesita un temporizador que dure 4 minutos, entonces necesita que dure 4 minutos incluso si se cambia la hora actual. Si tiene un temporizador configurado para una reunión el día 5 a las 3, entonces es absolutamente necesario que ese temporizador cambie si cambia la hora local. De ahí la diferencia entre steady_clocky system_clockaquí.
Billy ONeal
1
@ 5gon: Nada requiere que system_clocksea ​​UTC.
Billy ONeal
1
@CharlesSalvia Tenga en cuenta también que dado que el tiempo POSIX está vinculado a UTC, y UTC tiene segundos intercalares (cf. en.wikipedia.org/wiki/Unix_time#Leap_seconds ). Eso significa que incluso si el tiempo en una máquina nunca se ajusta, el tiempo de C / POSIX puede no ser monótono.
Michael Schlottke-Lakemper
3
ACTUALIZACIÓN (Visual Studio 2015) La implementación de Steady_clock ha cambiado [.....] Stein_clock ahora se basa en QueryPerformanceCounter () y high_resolution_clock ahora es un typedef para Stein_clock. Citado de msdn.microsoft.com/en-us/library/hh874757.aspx
felix-b
47

Billy proporcionó una gran respuesta basada en el estándar ISO C ++ con el que estoy totalmente de acuerdo. Sin embargo, hay otro lado de la historia: la vida real. Parece que en este momento realmente no hay diferencia entre esos relojes en la implementación de compiladores populares:

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;

En el caso de gcc, puede verificar si maneja el reloj estable simplemente verificando is_steadyy comportándose en consecuencia. Sin embargo, VS2012 parece hacer un poco de trampa aquí :-)

Si necesita un reloj de alta precisión, le recomiendo que por ahora escriba su propio reloj que se ajuste a la interfaz de reloj oficial de C ++ 11 y espere a que las implementaciones se pongan al día. Será un enfoque mucho mejor que usar la API específica del sistema operativo directamente en su código. Para Windows puedes hacerlo así:

// 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()));
  }
};

Para Linux es aún más fácil. Simplemente lea la página de manual de clock_gettimey modifique el código anterior.

Mateusz Pusz
fuente
19
La implementación de VC ++ 2012 ha sido reconocida como un error por el responsable de mantenimiento de la biblioteca estándar de MS.
ildjarn
5
Para los interesados, este es un enlace a ese error
Ben Voigt
1
Boost usa QueryPerformanceCounter, por lo que usar boost :: chrono es una buena solución a este error hasta que se
lance
Y aquí están las llamadas POSIX a las que se reenvían en GCC 5.3.0: stackoverflow.com/a/36700301/895245
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
19

Implementación de GCC 5.3.0

C ++ stdlib está dentro de la fuente GCC:

  • high_resolution_clock es un alias para system_clock
  • system_clock reenvía al primero de los siguientes que está disponible:
    • clock_gettime(CLOCK_REALTIME, ...)
    • gettimeofday
    • time
  • steady_clock reenvía al primero de los siguientes que está disponible:
    • clock_gettime(CLOCK_MONOTONIC, ...)
    • system_clock

Entonces CLOCK_REALTIMEvs CLOCK_MONOTONICse explica en: ¿ Diferencia entre CLOCK_REALTIME y CLOCK_MONOTONIC?

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

Quizás, la diferencia más significativa es el hecho de que el punto de partida de std::chrono:system_clockes el 1.1.1970, la llamada época UNIX. Por otro lado, por lo std::chrono::steady_clockgeneral, es el tiempo de arranque de su PC y es más adecuado para medir intervalos.

Silviu
fuente