Dlaczego czas zgłaszany przez time () bywa czasem 1 sekunda za sekundowym składnikiem timespec_get () w kodzie C?

12

Poniższy fragment kodu:

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

tworzy ten wynik:

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

Dlaczego rozbieżność między cTimei ts.tv_sec? Zauważ, że problem nie występuje, jeśli zmienna warunkowa zostanie zmieniona na ts.tv_nsec >= 3000000. Problem polega na tym, że nanosekundy są mniejsze niż 3000000.

Theo d'Or
źródło
Musisz bardziej szczegółowo określić używany system operacyjny, jego wersję, wersję używanej biblioteki C.
Jakiś programista koleś
2
@Someprogrammerdude Linux Debian 8, GCC 6.3.0.
Theo d'Or
Co jest timespec_get()? Czy to C czy C ++? Wygląda std::timespec_get. Proszę użyć odpowiedniego tagu.
Marco Bonelli
@MarcoBonelli: Został dodany do C w C11. Może się rozmnażać online .
ShadowRanger
@ShadowRanger dziękuję za odniesienie, nie widziałem manwpisu timespec_getw moim systemie, więc doszedłem do wniosków. Ma sens.
Marco Bonelli

Odpowiedzi:

11

Powodem jest to, że (domyślnie) używasz różnych zegarów systemowych. timespec_get()wykorzystuje wysokiej rozdzielczości zegara czasu rzeczywistego dla całego systemu, a time()Używa gruba zegar czasu rzeczywistego.

Spróbuj użyć

clock_gettime(CLOCK_REALTIME_COARSE, &ts);

zamiast twojego timespec_get(), różnica powinna zniknąć.

Edytować:

Można to zobaczyć w Linux Kernel Source, vclock_gettime.c

Rzeczywiście problem ten jest nieco subtelny. Część sekundowa elementów konstrukcji używana przez CLOCK_REALTIME_COARSEi CLOCK_REALTIMEzawiera identyczne wartości, ale część nanosekundowa jest inna; z CLOCK_REALTIMEtym może być większy niż 1000000000(co jest jedną sekundą). W takim przypadku jest ustalone na połączenie:

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

Ta korekta nie jest wykonywana ani za pomocą CLOCK_REALTIME_COARSE, ani za pomocą time(). To wyjaśnia różnicę między CLOCK_REALTIMEi time().

Ctx
źródło
Czy jest to udokumentowane w dowolnym miejscu, czy jest tylko artefaktem timeimplementacji z (przypuszczalnie) bardziej wydajnym, ale mniej dokładnym zegarem (zgodnie z teorią, że i tak ma tylko drugą ziarnistość, więc kto potrzebuje precyzji)? Opóźnianie w czasie rzeczywistym o około milisekundę (testy online wykazały sporadyczne opóźnienie powyżej ms, ale niewiele więcej), gdy tylko pytasz o drugą ziarnistość, nie wydaje się tak ważne.
ShadowRanger
@ShadowRanger Dodałem trochę więcej szczegółów
Ctx
Nie jest to jednoznaczna dokumentacja zamiaru, ale to wystarczająca ilość szczegółów, aby uzyskać głosowanie w górę. :-) Zabawne, że zegar może zgłaszać więcej niż sekundę dodatkowych nanosekund.
ShadowRanger
@ShadowRanger Nie mogłem znaleźć prawdziwej dokumentacji innej niż źródło, co oznacza również, że zachowanie może również ulec zmianie bez uprzedzenia
Ctx
@Ctx Dziękujemy za szczegółową odpowiedź! Użyję timespec_get () zamiast clock_gettime (), ponieważ radzisz, ponieważ timespec_get () to C11, a nie POSIX i nie wymaga ustawienia używanego zegara. Nie miałem pojęcia, że ​​użyto różnych zegarów, ale biorąc pod uwagę wybór, nie widzę sensu w używaniu zegara zgrubnego.
Theo d'Or