Apakah gettimeofday () dijamin dengan resolusi mikrodetik?

97

Saya mem-porting game, yang aslinya ditulis untuk Win32 API, ke Linux (baik, port OS X dari port Win32 ke Linux).

Saya telah menerapkan QueryPerformanceCounterdengan memberikan uSeconds sejak proses dimulai:

BOOL QueryPerformanceCounter(LARGE_INTEGER* performanceCount)
{
    gettimeofday(&currentTimeVal, NULL);
    performanceCount->QuadPart = (currentTimeVal.tv_sec - startTimeVal.tv_sec);
    performanceCount->QuadPart *= (1000 * 1000);
    performanceCount->QuadPart += (currentTimeVal.tv_usec - startTimeVal.tv_usec);

    return true;
}

Ini, ditambah dengan QueryPerformanceFrequency()memberikan 1000000 konstan sebagai frekuensi, bekerja dengan baik pada mesin saya , memberi saya variabel 64-bit yang berisi uSecondssejak program dimulai.

Jadi, apakah ini portabel? Saya tidak ingin menemukannya bekerja secara berbeda jika kernel dikompilasi dengan cara tertentu atau semacamnya. Saya baik-baik saja dengan itu menjadi non-portabel untuk sesuatu selain Linux.

Bernard
sumber

Jawaban:

57

Mungkin. Tetapi Anda memiliki masalah yang lebih besar. gettimeofday()dapat mengakibatkan pengaturan waktu yang salah jika ada proses di sistem Anda yang mengubah pengatur waktu (mis., ntpd). Pada linux "normal", saya yakin resolusinya gettimeofday()adalah 10us. Itu dapat melompat maju dan mundur dan waktu, akibatnya, berdasarkan proses yang berjalan di sistem Anda. Ini secara efektif membuat jawaban atas pertanyaan Anda no.

Anda harus melihat clock_gettime(CLOCK_MONOTONIC)interval waktu. Itu menderita beberapa masalah yang lebih sedikit karena hal-hal seperti sistem multi-core dan pengaturan jam eksternal.

Juga, lihat clock_getres()fungsinya.

Louis Brandy
sumber
1
clock_gettime hanya ada di Linux terbaru. sistem lain hanya memiliki gettimeofday ()
vitaly.v.ch
3
@ vitaly.v.ch itu POSIX jadi bukan hanya Linux dan 'newist'? bahkan distro 'Enterprise' seperti Red Hat Enterprise Linux didasarkan pada 2.6.18 yang memiliki clock_gettime jadi tidak, tidak terlalu baru .. (tanggal halaman manual di RHEL adalah 2004-Maret-12 jadi sudah ada untuk sementara waktu) kecuali Anda berbicara tentang BENAR-BENAR Kernel LAMA WTF maksud Anda?
Spudd86
clock_gettime dimasukkan ke dalam POSIX pada tahun 2001. sejauh yang saya tahu saat ini clock_gettime () diimplementasikan di Linux 2.6 dan qnx. tetapi linux 2.4 saat ini digunakan di banyak sistem produksi.
vitaly.v.ch
Ini diperkenalkan pada tahun 2001, tetapi tidak wajib sampai POSIX 2008.
R .. GitHub STOP HELPING ICE
2
Dari FAQ Linux untuk lock_gettime (lihat jawaban David Schlosnagle) "CLOCK_MONOTONIC ... adalah frekuensi yang disesuaikan oleh NTP melalui adjtimex (). Di masa mendatang (saya masih mencoba untuk mendapatkan patch) akan ada CLOCK_MONOTONIC_RAW yang tidak dimodifikasi sama sekali, dan akan memiliki korelasi linier dengan penghitung perangkat keras. " Menurut saya jam _RAW tidak pernah berhasil masuk ke kernel (kecuali jika namanya diganti _HR, tetapi penelitian saya menunjukkan bahwa upaya juga diabaikan).
Tony Delroy
41

Resolusi Tinggi, Waktu Overhead Rendah untuk Prosesor Intel

Jika Anda menggunakan perangkat keras Intel, berikut ini cara membaca penghitung instruksi waktu nyata CPU. Ini akan memberi tahu Anda jumlah siklus CPU yang dijalankan sejak prosesor di-boot. Ini mungkin counter terbaik yang bisa Anda dapatkan untuk pengukuran kinerja.

Perhatikan bahwa ini adalah jumlah siklus CPU. Di linux Anda bisa mendapatkan kecepatan CPU dari / proc / cpuinfo dan membagi untuk mendapatkan jumlah detik. Mengubah ini menjadi ganda cukup berguna.

Ketika saya menjalankan ini di kotak saya, saya mengerti

11867927879484732
11867927879692217
it took this long to call printf: 207485

Berikut panduan pengembang Intel yang memberikan banyak detail.

#include <stdio.h>
#include <stdint.h>

inline uint64_t rdtsc() {
    uint32_t lo, hi;
    __asm__ __volatile__ (
      "xorl %%eax, %%eax\n"
      "cpuid\n"
      "rdtsc\n"
      : "=a" (lo), "=d" (hi)
      :
      : "%ebx", "%ecx");
    return (uint64_t)hi << 32 | lo;
}

main()
{
    unsigned long long x;
    unsigned long long y;
    x = rdtsc();
    printf("%lld\n",x);
    y = rdtsc();
    printf("%lld\n",y);
    printf("it took this long to call printf: %lld\n",y-x);
}
Mark Harrison
sumber
11
Perhatikan bahwa TSC mungkin tidak selalu disinkronkan antar inti, mungkin berhenti atau mengubah frekuensinya saat prosesor memasuki mode daya yang lebih rendah (dan Anda tidak tahu cara melakukannya), dan secara umum tidak selalu dapat diandalkan. Kernel dapat mendeteksi jika dapat diandalkan, mendeteksi alternatif lain seperti penghitung waktu HPET dan ACPI PM, dan secara otomatis memilih yang terbaik. Sebaiknya selalu menggunakan kernel untuk penentuan waktu kecuali Anda benar-benar yakin TSC stabil dan monotonik.
CesarB
12
TSC pada platform Intel Core dan di atasnya disinkronkan di beberapa CPU dan bertambah pada frekuensi yang konstan terlepas dari status manajemen daya. Lihat Panduan Pengembang Perangkat Lunak Intel, Vol. 3 Bagian 18.10. Namun kecepatan kenaikan penghitung tidak sama dengan frekuensi CPU. TSC bertambah pada "frekuensi maksimum platform yang diselesaikan, yang sama dengan produk frekuensi bus yang dapat diskalakan dan rasio bus terselesaikan maksimum" Panduan Pengembang Perangkat Lunak Intel, Vol. 3 Bagian 18.18.5. Anda mendapatkan nilai-nilai tersebut dari register spesifik model (MSR) CPU.
sstock
7
Anda dapat memperoleh frekuensi bus yang dapat diskalakan dan rasio bus terselesaikan maksimum dengan menanyakan register khusus model (MSR) CPU sebagai berikut: Frekuensi bus yang dapat diskalakan == MSR_FSB_FREQ [2: 0] id 0xCD, Rasio bus maksimum yang diselesaikan == MSR_PLATFORM_ID [12: 8] id 0x17. Lihat Intel SDM Vol.3 Apendiks B.1 untuk menafsirkan nilai register. Anda dapat menggunakan alat-msr di Linux untuk meminta register. kernel.org/pub/linux/utils/cpu/msr-tools
sstock
1
Bukankah kode Anda harus digunakan CPUIDlagi setelah RDTSCinstruksi pertama dan sebelum menjalankan kode yang menjadi tolok ukur? Jika tidak, apa yang harus menghentikan kode benchmark yang dieksekusi sebelum / in-parallel-dengan yang pertama RDTSC, dan akibatnya kurang terwakili di RDTSCdelta?
Tony Delroy
18

@Bandung:

Harus saya akui, sebagian besar contoh Anda terlintas di benak saya. Itu mengkompilasi, dan tampaknya berhasil. Apakah ini aman untuk sistem SMP atau SpeedStep?

Itu pertanyaan yang bagus ... Saya pikir kodenya oke. Dari sudut pandang praktis, kami menggunakannya di perusahaan saya setiap hari, dan kami menjalankannya pada beragam kotak, semuanya dari 2-8 core. Tentu saja, YMMV, dll, tetapi tampaknya menjadi metode waktu yang andal dan overhead rendah (karena tidak membuat pengalihan konteks ke ruang sistem).

Secara umum cara kerjanya adalah:

  • mendeklarasikan blok kode menjadi assembler (dan volatile, sehingga pengoptimal akan membiarkannya).
  • jalankan instruksi CPUID. Selain mendapatkan beberapa informasi CPU (yang tidak kami lakukan apa-apa), ia menyinkronkan buffer eksekusi CPU sehingga pengaturan waktu tidak terpengaruh oleh eksekusi yang tidak sesuai pesanan.
  • jalankan eksekusi rdtsc (read timestamp). Ini mengambil jumlah siklus mesin yang dijalankan sejak prosesor disetel ulang. Ini adalah nilai 64-bit, jadi dengan kecepatan CPU saat ini, nilainya akan bertambah setiap 194 tahun atau lebih. Menariknya, dalam referensi Pentium asli, mereka mencatat itu membungkus setiap 5800 tahun atau lebih.
  • beberapa baris terakhir menyimpan nilai dari register ke dalam variabel hi dan lo, dan memasukkannya ke dalam nilai pengembalian 64-bit.

Catatan khusus:

  • Eksekusi out-of-order dapat menyebabkan hasil yang salah, jadi kami mengeksekusi instruksi "cpuid" yang selain memberi Anda beberapa informasi tentang cpu juga menyinkronkan eksekusi instruksi out-of-order.

  • Sebagian besar OS menyinkronkan penghitung pada CPU ketika mereka mulai, jadi jawabannya bagus dalam beberapa nano-detik.

  • Komentar yang berhibernasi mungkin benar, tetapi dalam praktiknya Anda mungkin tidak peduli dengan pengaturan waktu melintasi batas hibernasi.

  • mengenai speedstep: CPU Intel yang lebih baru mengimbangi perubahan kecepatan dan mengembalikan hitungan yang disesuaikan. Saya melakukan pemindaian cepat pada beberapa kotak di jaringan kami dan hanya menemukan satu kotak yang tidak memilikinya: Pentium 3 menjalankan beberapa server database lama. (ini adalah kotak linux, jadi saya memeriksanya dengan: grep constant_tsc / proc / cpuinfo)

  • Saya tidak yakin tentang CPU AMD, kami pada dasarnya adalah toko Intel, meskipun saya tahu beberapa ahli sistem tingkat rendah kami melakukan evaluasi AMD.

Semoga ini memuaskan rasa ingin tahu Anda, ini adalah bidang pemrograman yang menarik dan (IMHO) kurang dipelajari. Anda tahu ketika Jeff dan Joel membicarakan tentang apakah seorang programmer harus tahu C atau tidak? Saya meneriaki mereka, "hei, lupakan hal C tingkat tinggi ... assembler adalah yang harus Anda pelajari jika Anda ingin tahu apa yang dilakukan komputer!"

Mark Harrison
sumber
1
... Kernel telah mencoba untuk membuat orang berhenti menggunakan rdtsc untuk sementara waktu ... dan umumnya menghindari menggunakannya di kernel karena hanya itu tidak dapat diandalkan.
Spudd86
1
Sebagai referensi, pertanyaan yang saya ajukan (Dalam balasan terpisah - sebelum komentar) adalah: "Harus saya akui, sebagian besar contoh Anda langsung terlintas di benak saya. Ini dapat dikompilasi, dan tampaknya berhasil. Apakah ini aman untuk Sistem SMP atau SpeedStep? "
Bernard
9

Jadi dikatakan mikrodetik secara eksplisit, tetapi resolusi jam sistem tidak ditentukan. Saya kira resolusi dalam konteks ini berarti berapa jumlah terkecil yang pernah ditingkatkan?

Struktur data didefinisikan sebagai memiliki mikrodetik sebagai unit pengukuran, tetapi itu tidak berarti bahwa jam atau sistem operasi sebenarnya mampu mengukurnya dengan cermat.

Seperti yang disarankan orang lain, gettimeofday()itu buruk karena pengaturan waktu dapat menyebabkan jam miring dan membuang perhitungan Anda. clock_gettime(CLOCK_MONOTONIC)adalah yang Anda inginkan, dan clock_getres()akan memberi tahu Anda ketepatan jam Anda.

Joe Shaw
sumber
Jadi apa yang terjadi dalam kode Anda ketika gettimeofday () melompat maju atau mundur dengan penghematan siang hari?
mpez0
3
clock_gettime hanya ada di Linux terbaru. sistem lain hanya memiliki gettimeofday ()
vitaly.v.ch
8

Resolusi gettimeofday () yang sebenarnya bergantung pada arsitektur perangkat keras. Prosesor Intel serta mesin SPARC menawarkan pengatur waktu resolusi tinggi yang mengukur mikrodetik. Arsitektur perangkat keras lainnya kembali ke pengatur waktu sistem, yang biasanya disetel ke 100 Hz. Dalam kasus seperti itu, resolusi waktu menjadi kurang akurat.

Saya memperoleh jawaban ini dari Pengukuran Waktu dan Pengatur Waktu Resolusi Tinggi, Bagian I

CodingWithoutComments
sumber
6

Jawaban ini menyebutkan masalah dengan jam yang sedang disesuaikan. Masalah Anda dalam menjamin unit centang dan masalah dengan waktu yang disesuaikan diselesaikan di C ++ 11 dengan <chrono>perpustakaan.

Jam std::chrono::steady_clockdijamin tidak akan disesuaikan, dan selanjutnya akan maju dengan kecepatan konstan relatif terhadap waktu nyata, jadi teknologi seperti SpeedStep tidak boleh memengaruhinya.

Anda bisa mendapatkan unit yang aman dengan mengonversinya ke salah satu std::chrono::durationspesialisasi, seperti std::chrono::microseconds. Dengan tipe ini tidak ada ambiguitas tentang unit yang digunakan oleh nilai tick. Namun, perlu diingat bahwa jam tidak selalu memiliki resolusi ini. Anda dapat mengubah durasi menjadi attoseconds tanpa benar-benar memiliki jam yang akurat.

bames53
sumber
4

Dari pengalaman saya, dan dari apa yang saya baca di internet, jawabannya adalah "Tidak", itu tidak dijamin. Itu tergantung pada kecepatan CPU, sistem operasi, rasa Linux, dll.

CodingWithoutComments
sumber
3

Membaca RDTSC tidak dapat diandalkan dalam sistem SMP, karena setiap CPU memelihara penghitungnya sendiri dan setiap penghitung tidak dijamin oleh sinkronisasi sehubungan dengan CPU lain.

Saya mungkin menyarankan untuk mencoba clock_gettime(CLOCK_REALTIME). Manual posix menunjukkan bahwa ini harus diterapkan pada semua sistem yang sesuai. Ini dapat memberikan hitungan nanodetik, tetapi Anda mungkin ingin memeriksa clock_getres(CLOCK_REALTIME)sistem Anda untuk melihat apa resolusi sebenarnya.

Doug
sumber
clock_getres(CLOCK_REALTIME)tidak akan memberikan resolusi yang sebenarnya. Itu selalu mengembalikan "1 ns" (satu nanodetik) saat jam pengatur waktu tersedia, periksa include/linux/hrtimer.hfile untuk define HIGH_RES_NSEC 1(selengkapnya di stackoverflow.com/a/23044075/196561 )
osgx