Чому час, про який повідомляється часом (), іноді на 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.


Ви повинні бути більш конкретними щодо використовуваної операційної системи, її версії та версії використовуваної бібліотеки С.
Якийсь програміст чувак

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


Це документоване будь-де, або просто артефакт timeреалізації з (імовірно) ефективнішими, але менш точними годинниками (за теорією, що у будь-якому випадку вона має лише другу деталізацію, тому кому потрібна точність)? Відставання в реальному часі на мільйонні секунди (онлайнові тести показували випадкові відставання понад мс, але не набагато більше), коли ви тільки просите про другу деталізацію, не все так важливо, напевно.
ShadowRanger

@ShadowRanger Я додав ще кілька деталей
Ctx

Не явна документація про наміри, але це є достатньою деталлю для повторного голосування. :-) Смішно, що годинник насправді може повідомити про додаткові наносекунди більше ніж секунди.
ShadowRanger

@ShadowRanger Я не зміг знайти реальну документацію, крім джерела для цього, що також означає, що поведінка може також детально змінюватися без попереднього повідомлення
Ctx

@Ctx Дякую за детальну відповідь! Я буду використовувати timespec_get (), а не clock_gettime (), який ви радите, так як timespec_get () - це C11, а не POSIX, і не потрібно встановити, який годинник використовувати. Я не мав уявлення, що використовуються різні годинники, але, враховуючи вибір, я не бачу особливого сенсу використовувати грубі годинники.
Тео д'Ор
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.