Perché il tempo riportato da time () a volte 1 secondo dietro il componente secondi di timespec_get () nel codice C?


12

Il seguente frammento di codice:

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 questo output:

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

Perché la discrepanza tra cTimee ts.tv_sec? Si noti che il problema non si verifica se il condizionale viene modificato in ts.tv_nsec >= 3000000. Il problema si basa sul fatto che i nanosecondi sono inferiori a 3000000.


Devi essere più specifico sul sistema operativo utilizzato, versione di esso, versione della libreria C utilizzata.
Un programmatore amico

2
@Someprogrammerdude Linux Debian 8, GCC 6.3.0.
Theo d'Or,

Cosa timespec_get()? Questo è C o C ++? Assomiglia std::timespec_get. Si prega di utilizzare il tag appropriato.
Marco Bonelli,

@MarcoBonelli: è stato aggiunto a C in C11. Può riprodurre online .
ShadowRanger

@ShadowRanger grazie per il riferimento, non ho potuto vedere una manvoce timespec_getsul mio sistema, quindi sono saltato alle conclusioni. Ha senso.
Marco Bonelli,

Risposte:


11

Il motivo è che usi (implicitamente) diversi orologi di sistema. timespec_get()utilizza l' alta risoluzione a livello di sistema orologio in tempo reale, mentre time()usi il grosso orologio in tempo reale.

Prova ad usare

clock_gettime(CLOCK_REALTIME_COARSE, &ts);

invece del tuo timespec_get(), allora la differenza dovrebbe svanire.

Modificare:

Questo può essere visto nel Linux Kernel Source, vclock_gettime.c

In effetti il ​​problema è un po 'sottile da vedere qui. La parte dei secondi dei membri della struttura utilizzata CLOCK_REALTIME_COARSEe CLOCK_REALTIMEcontiene valori identici, ma la parte dei nanosecondi è diversa; con CLOCK_REALTIMEesso può essere maggiore di 1000000000(che è un secondo). In questo caso, viene risolto sulla chiamata:

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

Questa correzione non viene eseguita né con CLOCK_REALTIME_COARSE, né con time(). Questo spiega la differenza tra CLOCK_REALTIMEe time().


È documentato da qualche parte, o solo un artefatto di timeessere implementato con l'orologio (presumibilmente) più performante ma meno accurato (in teoria che ha comunque solo una seconda granularità, quindi chi ha bisogno di precisione)? Rimanere in ritardo di circa un millisecondo (i test online hanno mostrato un ritardo occasionale di oltre un ms, ma non molto oltre) quando si chiede solo una seconda granularità non è poi così importante, immagino.
ShadowRanger

@ShadowRanger Ho aggiunto alcuni dettagli
Ctx

Documentazione non esplicita dell'intenzione, ma sono sufficienti dettagli per un voto positivo. :-) Divertente che l'orologio possa effettivamente riportare più di un secondo di nanosecondi aggiuntivi.
ShadowRanger

@ShadowRanger Non sono riuscito a trovare una vera documentazione diversa dalla fonte per questo, il che significa anche che il comportamento potrebbe anche cambiare in dettaglio senza preavviso
Ctx

@Ctx Grazie per la risposta dettagliata! Userò timespec_get () piuttosto che clock_gettime () che tu consigli, poiché timespec_get () è C11 anziché POSIX e non richiede l'impostazione di quale orologio usare. Non avevo idea che fossero usati orologi diversi, ma data la scelta, non vedo molto senso usare l'orologio grossolano.
Theo d'Or,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.