Почему время, сообщаемое функцией time (), иногда составляет 1 секунду после секундного компонента timespec_get () в коде C?

12

Следующий фрагмент кода:

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

производит этот вывод:

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

Почему расхождение между cTimeи ts.tv_sec? Обратите внимание, что проблема не возникает, если условие изменяется на ts.tv_nsec >= 3000000. Проблема заключается в том, что наносекунды меньше 3000000.

Тео д'Ор
источник
Вы должны быть более конкретными об используемой операционной системе, ее версии, используемой версии библиотеки C.
Какой-то программист чувак
2
@Someprogrammerdude Linux Debian 8, GCC 6.3.0.
Тео д'Ор
Что timespec_get()? Это C или C ++? Похоже std::timespec_get. Пожалуйста, используйте соответствующий тег.
Марко Бонелли
@MarcoBonelli: он был добавлен в C в C11. Можно воспроизвести онлайн .
ShadowRanger
@ShadowRanger спасибо за ссылку, я не вижу manзаписи для timespec_getв моей системе, поэтому я сделал поспешные выводы. Имеет смысл.
Марко Бонелли

Ответы:

11

Причина в том, что вы (неявно) используете разные системные часы. timespec_get()использует высокое разрешение общесистемного часы реального времени, в то время как time()ИСПОЛЬЗУЕТ грубые часы реального времени.

Попробуй использовать

clock_gettime(CLOCK_REALTIME_COARSE, &ts);

вместо вашего timespec_get(), тогда разница должна исчезнуть.

Редактировать:

Это можно увидеть в Исходнике ядра Linux, vclock_gettime.c

На самом деле эту проблему немного уловить. Секундная часть элементов структуры, используемых CLOCK_REALTIME_COARSEи CLOCK_REALTIMEсодержащих идентичные значения, но наносекундная часть отличается; с CLOCK_REALTIMEним может быть больше, чем 1000000000(что составляет одну секунду). В этом случае это фиксируется на вызове:

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

Эта коррекция не выполняется ни с помощью CLOCK_REALTIME_COARSE, ни с time(). Это объясняет разницу между CLOCK_REALTIMEи time().

Ctx
источник
Это где-то задокументировано, или это просто артефакт timeреализации с (предположительно) более производительными, но менее точными часами (по теории, что в любом случае он имеет только вторую гранулярность, так кому нужна точность)? Отставание в реальном времени на миллисекунду или около того (онлайн-тестирование показало случайную задержку более чем на мс, но не намного больше), когда вы запрашиваете только вторую степень детализации, это не так важно, как мне кажется.
ShadowRanger
@ShadowRanger Я добавил еще несколько деталей
Ctx
Непонятная документация о намерениях, но этого достаточно для подробного голосования. :-) Забавно, что часы на самом деле могут сообщать о дополнительных наносекундах больше, чем на секунду.
ShadowRanger
@ShadowRanger Я не смог найти настоящую документацию, кроме источника для этого, что также означает, что поведение может также измениться в деталях без предварительного уведомления
Ctx
@Ctx Спасибо за подробный ответ! Я буду использовать timespec_get (), а не clock_gettime (), который вы советуете, так как timespec_get () - это C11, а не POSIX и не требует установки того, какие часы использовать. Я понятия не имел, что использовались разные часы, но, учитывая выбор, я не вижу особого смысла в использовании грубых часов.
Тео д'Ор