¿Por qué el tiempo informado por time () a veces 1 segundo detrás del componente de segundos de timespec_get () en el código C?

12

El siguiente fragmento de código:

struct timespec ts;
for (int x = 0; x < 100000000; x++) {
    timespec_get(&ts, TIME_UTC);
    long cTime = (long) time(NULL);
    if (cTime != ts.tv_sec && ts.tv_nsec < 3000000) {
        printf("cTime: %ld\n", cTime);
        printf("ts.tv_sec: %ld\n", ts.tv_sec);
        printf("ts.tv_nsec: %ld\n", ts.tv_nsec);
    }
}

produce esta salida:

...
cTime: 1579268059
ts.tv_sec: 1579268060
ts.tv_nsec: 2527419
cTime: 1579268059
ts.tv_sec: 1579268060
ts.tv_nsec: 2534036
cTime: 1579268059
ts.tv_sec: 1579268060
ts.tv_nsec: 2540359
cTime: 1579268059
ts.tv_sec: 1579268060
ts.tv_nsec: 2547039
...

¿Por qué la discrepancia entre cTimey ts.tv_sec? Tenga en cuenta que el problema no se produce si el condicional se cambia a ts.tv_nsec >= 3000000. El problema se basa en que los nanosegundos son más pequeños que 3000000.

Theo d'Or
fuente
Debe ser más específico sobre el sistema operativo utilizado, la versión del mismo, la versión de la biblioteca C utilizada.
Algún tipo programador el
2
@Someprogrammerdude Linux Debian 8, GCC 6.3.0.
Theo d'Or
¿Qué es timespec_get()? ¿Es esto C o C ++? Aspecto del producto std::timespec_get. Por favor use la etiqueta apropiada.
Marco Bonelli
@MarcoBonelli: Se agregó a C en C11. Puede reproducirse en línea .
ShadowRanger
@ShadowRanger gracias por la referencia, no pude ver una manentrada timespec_geten mi sistema, así que saqué conclusiones. Tiene sentido.
Marco Bonelli

Respuestas:

11

La razón es que usted (implícitamente) usa diferentes relojes del sistema. timespec_get()usa el reloj de tiempo real de todo el sistema de alta resolución , mientras time()usa el reloj de tiempo real grueso

Tratar de usar

clock_gettime(CLOCK_REALTIME_COARSE, &ts);

en lugar de tu timespec_get(), entonces la diferencia debería desaparecer.

Editar:

Esto se puede ver en Linux Kernel Source, vclock_gettime.c

De hecho, el tema es un poco sutil para ver aquí. La parte de segundos de los miembros de la estructura utilizada CLOCK_REALTIME_COARSEy que CLOCK_REALTIMEcontiene valores idénticos, pero la parte de nanosegundos es diferente; con CLOCK_REALTIMEella puede ser mayor que 1000000000(que es un segundo). En este caso, se arregla en la llamada:

ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
ts->tv_nsec = ns;

Esta corrección no se realiza CLOCK_REALTIME_COARSEni con time(). Esto explica la diferencia entre CLOCK_REALTIMEy time().

Ctx
fuente
¿Está documentado en alguna parte, o solo es un artefacto de timeser implementado con el reloj (presumiblemente) más eficiente pero menos preciso (en la teoría de que de todos modos solo tiene una segunda granularidad, entonces, ¿quién necesita precisión)? Retrasar el tiempo real en un milisegundo más o menos (las pruebas en línea mostraron un retraso ocasional de más de un ms, pero no mucho más) cuando solo se solicita una segunda granularidad, no es tan importante, supongo.
ShadowRanger
@ShadowRanger Agregué algunos detalles más
Ctx
No es una documentación explícita de intención, pero eso es suficiente detalle para un voto positivo. :-) Es curioso que el reloj realmente pueda informar más de un segundo de nanosegundos adicionales.
ShadowRanger
@ShadowRanger No pude encontrar una documentación real que no sea la fuente para eso, lo que también significa que el comportamiento también podría cambiar en detalle sin previo aviso
Ctx
@Ctx ¡Gracias por la respuesta detallada! Usaré timespec_get () en lugar de clock_gettime () que me aconsejan, ya que timespec_get () es C11 en lugar de POSIX y no requiere la configuración de qué reloj usar. No tenía idea de que se usaran diferentes relojes, pero dada la opción, no veo mucho sentido en usar el reloj grueso.
Theo d'Or