Mengapa waktu dilaporkan oleh waktu () terkadang 1 detik di belakang komponen detik dari timespec_get () dalam kode C?

12

Potongan kode berikut:

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

menghasilkan output ini:

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

Mengapa perbedaan antara cTimedan ts.tv_sec? Perhatikan bahwa masalah tidak terjadi jika kondisi diubah menjadi ts.tv_nsec >= 3000000. Masalahnya bergantung pada nanodetik yang lebih kecil dari 3000000.

Theo d'Or
sumber
Anda harus lebih spesifik tentang sistem operasi yang digunakan, versi itu, versi perpustakaan C yang digunakan.
Beberapa programmer Bung
2
@Someprogrammerdude Linux Debian 8, GCC 6.3.0.
Theo d'Or
Apa timespec_get()? Apakah ini C atau C ++? Sepertinya std::timespec_get. Silakan gunakan tag yang sesuai.
Marco Bonelli
@MarcoBonelli: Itu ditambahkan ke C di C11. Dapat mereproduksi online .
ShadowRanger
@ShadowRanger terima kasih atas referensi, saya tidak bisa melihat manentri untuk timespec_getdi sistem saya jadi saya langsung mengambil kesimpulan. Masuk akal.
Marco Bonelli

Jawaban:

11

Alasannya adalah, bahwa Anda (secara implisit) menggunakan jam sistem yang berbeda. timespec_get()menggunakan jam realtime seluruh sistem resolusi tinggi , sementara time()menggunakan jam realtime kasar .

Coba gunakan

clock_gettime(CLOCK_REALTIME_COARSE, &ts);

alih-alih Anda timespec_get(), maka perbedaannya harus menghilang.

Edit:

Ini dapat dilihat di Sumber Kernel Linux, vclock_gettime.c

Memang masalahnya agak halus untuk dilihat di sini. Bagian detik dari anggota struktur yang digunakan oleh CLOCK_REALTIME_COARSEdan CLOCK_REALTIMEmengandung nilai yang identik, tetapi bagian nanodetik berbeda; dengan CLOCK_REALTIMEitu bisa lebih besar dari 1000000000(yaitu satu detik). Dalam hal ini, telepon diperbaiki:

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

Koreksi ini tidak dilakukan dengan CLOCK_REALTIME_COARSE, atau dengan time(). Ini menjelaskan perbedaan antara CLOCK_REALTIMEdan time().

Ctx
sumber
Apakah ini didokumentasikan di mana saja, atau hanya sebuah artefak timeyang diimplementasikan dengan (mungkin) jam yang lebih berkinerja tetapi kurang akurat (pada teori itu hanya mendapat rincian kedua, jadi siapa yang butuh ketelitian)? Tertinggal real time sekitar satu milidetik (tes online sesekali menunjukkan keterlambatan lebih dari satu ms, tetapi tidak terlalu banyak) ketika Anda hanya meminta rincian kedua tidak terlalu penting menurut saya.
ShadowRanger
@ShadowRanger Saya menambahkan beberapa detail
Ctx
Bukan dokumentasi niat yang eksplisit, tapi itu cukup detail untuk pemungutan suara. :-) Lucu bahwa jam itu benar-benar dapat melaporkan lebih dari satu detik dari nanodetik tambahan.
ShadowRanger
@ShadowRanger Saya tidak dapat menemukan dokumentasi nyata selain sumber untuk itu, yang juga berarti, bahwa perilaku tersebut mungkin juga berubah secara detail tanpa pemberitahuan sebelumnya
Ctx
@ CTX Terima kasih atas jawaban terperinci! Saya akan menggunakan timespec_get () daripada clock_gettime () yang Anda sarankan, karena timespec_get () adalah C11 daripada POSIX dan tidak memerlukan pengaturan jam mana yang digunakan. Saya tidak tahu jam yang berbeda digunakan, tetapi diberi pilihan, saya tidak melihat banyak gunanya menggunakan jam kasar.
Theo d'Or