Pourquoi le temps est-il rapporté par time () parfois 1 seconde derrière le composant secondes de timespec_get () en code C?

12

L'extrait de code suivant:

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

produit cette sortie:

...
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
...

Pourquoi l'écart entre cTimeet ts.tv_sec? Notez que le problème ne se produit pas si le conditionnel est modifié en ts.tv_nsec >= 3000000. Le problème repose sur des nanosecondes inférieures à 3000000.

Théo d'Or
la source
Vous devez être plus précis sur le système d'exploitation utilisé, sa version, la version de la bibliothèque C utilisée.
Un programmeur du
2
@Someprogrammerdude Linux Debian 8, GCC 6.3.0.
Theo d'Or
Quoi timespec_get()? Est-ce C ou C ++? On dirait std::timespec_get. Veuillez utiliser la balise appropriée.
Marco Bonelli
@MarcoBonelli: Il a été ajouté au C dans C11. Peut se reproduire en ligne .
ShadowRanger
@ShadowRanger merci pour la référence, je n'ai pas pu voir une manentrée pour timespec_getsur mon système alors j'ai sauté aux conclusions. Logique.
Marco Bonelli

Réponses:

11

La raison en est que vous utilisez (implicitement) différentes horloges système. timespec_get()utilise l' horloge en temps réel à l'échelle du système à haute résolution , tout en time()utilisant l' horloge en temps réel grossière .

Essayez d'utiliser

clock_gettime(CLOCK_REALTIME_COARSE, &ts);

au lieu du vôtre timespec_get(), alors la différence devrait disparaître.

Éditer:

Cela peut être vu dans la source du noyau Linux, vclock_gettime.c

En effet, le problème est un peu subtil à voir ici. La partie seconde des membres de structure utilisée par CLOCK_REALTIME_COARSEet CLOCK_REALTIMEcontient des valeurs identiques, mais la partie nanosecondes est différente; avec CLOCK_REALTIMEelle peut être plus grande que 1000000000(ce qui correspond à une seconde). Dans ce cas, il est fixé sur l'appel:

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

Cette correction n'est effectuée ni avec CLOCK_REALTIME_COARSE, ni avec time(). Cela explique la différence entre CLOCK_REALTIMEet time().

Ctx
la source
Est-ce documenté n'importe où, ou simplement un artefact de timemise en œuvre avec l'horloge (probablement) plus performante mais moins précise (selon la théorie selon laquelle elle n'a qu'une seconde granularité de toute façon, alors qui a besoin de précision)? Retarder le temps réel d'une milliseconde environ (les tests en ligne ont montré un décalage occasionnel de plus d'un ms, mais pas beaucoup plus) lorsque vous ne demandez qu'une deuxième granularité n'est pas si important que ça.
ShadowRanger
@ShadowRanger J'ai ajouté plus de détails
Ctx
Pas une documentation explicite de l'intention, mais c'est assez de détails pour un vote positif. :-) C'est drôle que l'horloge puisse rapporter plus d'une seconde de nanosecondes supplémentaires.
ShadowRanger
@ShadowRanger Je n'ai pas pu trouver une vraie documentation autre que la source pour cela, ce qui signifie également que le comportement pourrait également changer en détail sans préavis
Ctx
@Ctx Merci pour la réponse détaillée! J'utiliserai timespec_get () plutôt que clock_gettime () que vous conseillez, car timespec_get () est C11 plutôt que POSIX et ne nécessite pas le réglage de l'horloge à utiliser. Je n'avais aucune idée que différentes horloges étaient utilisées, mais étant donné le choix, je ne vois pas grand intérêt à utiliser l'horloge grossière.
Theo d'Or