GNU/Linux >> Belajar Linux >  >> Linux

Mendapatkan siklus cpu menggunakan RDTSC - mengapa nilai RDTSC selalu meningkat?

Ada banyak informasi yang membingungkan dan/atau salah tentang TSC di luar sana, jadi saya pikir saya akan mencoba menjelaskannya.

Ketika Intel pertama kali memperkenalkan TSC (dalam CPU Pentium asli), didokumentasikan dengan jelas untuk menghitung siklus (dan bukan waktu). Namun, saat itu CPU sebagian besar berjalan pada frekuensi tetap, sehingga beberapa orang mengabaikan perilaku yang terdokumentasi dan menggunakannya untuk mengukur waktu (terutama, pengembang kernel Linux). Kode mereka rusak di CPU selanjutnya yang tidak berjalan pada frekuensi tetap (karena manajemen daya, dll). Sekitar waktu itu produsen CPU lain (AMD, Cyrix, Transmeta, dll) bingung dan beberapa menerapkan TSC untuk mengukur siklus dan beberapa menerapkannya sehingga mengukur waktu, dan beberapa membuatnya dapat dikonfigurasi (melalui MSR).

Kemudian sistem "multi-chip" menjadi lebih umum untuk server; dan bahkan kemudian multi-core diperkenalkan. Hal ini menyebabkan perbedaan kecil antara nilai TSC pada inti yang berbeda (karena waktu pengaktifan yang berbeda); tetapi yang lebih penting, ini juga menyebabkan perbedaan besar antara nilai TSC pada CPU yang berbeda yang disebabkan oleh CPU yang bekerja pada kecepatan yang berbeda (karena manajemen daya dan/atau faktor lainnya).

Orang-orang yang mencoba menggunakannya dengan salah sejak awal (orang yang menggunakannya untuk mengukur waktu dan bukan siklus) banyak mengeluh, dan akhirnya meyakinkan produsen CPU untuk membuat standar agar TSC mengukur waktu dan bukan siklus.

Tentu saja ini berantakan - mis. dibutuhkan banyak kode hanya untuk menentukan apa yang sebenarnya diukur oleh TSC jika Anda mendukung semua CPU 80x86; dan teknologi manajemen daya yang berbeda (termasuk hal-hal seperti SpeedStep, tetapi juga hal-hal seperti kondisi tidur) dapat mempengaruhi TSC dengan cara yang berbeda pada CPU yang berbeda; jadi AMD memperkenalkan flag "TSC invariant" di CPUID untuk memberi tahu OS bahwa TSC dapat digunakan untuk mengukur waktu dengan benar.

Semua CPU Intel dan AMD baru-baru ini sudah seperti ini untuk sementara waktu - TSC menghitung waktu dan tidak mengukur siklus sama sekali. Ini berarti jika Anda ingin mengukur siklus, Anda harus menggunakan penghitung pemantauan kinerja (khusus model). Sayangnya, penghitung pemantauan kinerja bahkan lebih berantakan (karena sifat spesifik modelnya dan konfigurasi yang berbelit-belit).


Selama utas Anda tetap pada inti CPU yang sama, instruksi RDTSC akan terus mengembalikan angka yang meningkat hingga selesai. Untuk CPU 2GHz, ini terjadi setelah 292 tahun, jadi ini bukan masalah sebenarnya. Anda mungkin tidak akan melihatnya terjadi. Jika Anda ingin hidup selama itu, pastikan komputer Anda dinyalakan ulang, katakanlah, setiap 50 tahun.

Masalah dengan RDTSC adalah bahwa Anda tidak memiliki jaminan bahwa itu dimulai pada titik waktu yang sama pada semua inti CPU multicore lama dan tidak ada jaminan bahwa itu dimulai pada titik waktu yang sama pada semua CPU pada papan multi-CPU tua .
Sistem modern biasanya tidak memiliki masalah seperti itu, tetapi masalahnya juga dapat diselesaikan pada sistem lama dengan mengatur afinitas thread sehingga hanya berjalan pada satu CPU. Ini tidak bagus untuk performa aplikasi, jadi sebaiknya tidak dilakukan secara umum, tetapi untuk mengukur kutu, tidak apa-apa.

("Masalah" lainnya adalah banyak orang menggunakan RDTSC untuk mengukur waktu, yang tidak apa fungsinya, tetapi Anda menulis bahwa Anda menginginkan siklus CPU, jadi tidak apa-apa. Jika Anda lakukan menggunakan RDTSC untuk mengukur waktu, Anda mungkin memiliki kejutan saat penghematan daya atau hyperboost atau apa pun teknik perubahan frekuensi yang banyak disebut. Untuk waktu aktual, clock_gettime syscall sangat bagus di Linux.)

Saya hanya akan menulis rdtsc di dalam asm pernyataan, yang berfungsi dengan baik untuk saya dan lebih mudah dibaca daripada beberapa kode hex yang tidak jelas. Dengan asumsi itu adalah kode hex yang benar (dan karena tidak mogok dan mengembalikan angka yang terus meningkat, sepertinya begitu), kode Anda bagus.

Jika Anda ingin mengukur jumlah centang yang diambil oleh sebuah kode, Anda ingin tanda centang perbedaan , Anda hanya perlu mengurangi dua nilai penghitung yang terus meningkat. Sesuatu seperti uint64_t t0 = rdtsc(); ... uint64_t t1 = rdtsc() - t0;
Perhatikan bahwa jika diperlukan pengukuran yang sangat akurat yang diisolasi dari kode di sekitarnya, Anda perlu membuat cerita bersambung, yaitu menghentikan jalur pipa, sebelum memanggil rdtsc (atau gunakan rdtscp yang hanya didukung pada prosesor yang lebih baru). Satu instruksi serialisasi yang dapat digunakan pada setiap tingkat hak istimewa adalah cpuid .

Sebagai balasan untuk pertanyaan selanjutnya di komentar:

TSC dimulai dari nol saat Anda menyalakan komputer (dan BIOS menyetel ulang semua penghitung pada semua CPU ke nilai yang sama, meskipun beberapa BIOS beberapa tahun lalu tidak melakukannya dengan andal).

Jadi, dari sudut pandang program Anda, penghitung memulai "beberapa waktu yang tidak diketahui di masa lalu", dan selalu bertambah dengan setiap detak jam yang dilihat CPU. Oleh karena itu jika Anda menjalankan instruksi yang mengembalikan penghitung itu sekarang dan nanti dalam proses yang berbeda, itu akan mengembalikan nilai yang lebih besar (kecuali CPU ditangguhkan atau dimatikan di antaranya). Proses yang berbeda dari program yang sama mendapatkan angka yang lebih besar, karena penghitungnya terus bertambah. Selalu.

Sekarang, clock_gettime(CLOCK_PROCESS_CPUTIME_ID) adalah hal yang berbeda. Ini adalah waktu CPU yang diberikan OS untuk proses tersebut. Ini dimulai dari nol saat proses Anda dimulai. Proses baru juga dimulai dari nol. Dengan demikian, dua proses yang berjalan setelah satu sama lain akan mendapatkan angka yang sangat mirip atau identik, tidak pernah bertambah.

clock_gettime(CLOCK_MONOTONIC_RAW) lebih dekat dengan cara kerja RDTSC (dan pada beberapa sistem lama diimplementasikan dengannya). Ini mengembalikan nilai yang pernah meningkat. Saat ini, ini biasanya HPET. Namun, ini benar-benar waktu , dan bukan centang . Jika komputer Anda dalam kondisi daya rendah (mis. berjalan pada 1/2 frekuensi normal), komputer akan tetap maju dengan kecepatan yang sama.


jawaban yang bagus, dan Damon sudah menyebutkan ini dalam jawabannya, tetapi saya akan menambahkan ini dari entri manual x86 yang sebenarnya (volume 2, 4-301) untuk RDTSC:

Memuat nilai saat ini dari penghitung stempel waktu prosesor (MSR 64-bit) ke dalam register EDX:EAX. Register EDX dimuat dengan 32 bit orde tinggi dari MSR dan register EAX dimuat dengan 32 bit orde rendah. (Pada prosesor yang mendukung arsitektur Intel 64, 32 bit orde tinggi dari masing-masing RAX dan RDX dihapus.)

Prosesor secara monoton menaikkan MSR penghitung stempel waktu setiap siklus clock dan menyetel ulang ke 0 setiap kali prosesor disetel ulang. Lihat "Penghitung Stempel Waktu" di Bab 17 dari Panduan Pengembang Perangkat Lunak Arsitektur Intel® 64 dan IA-32, Volume 3B , untuk detail spesifik tentang perilaku penghitung stempel waktu.


Linux
  1. Kapan terakhir kali Anda menggunakan Windows?

  2. Mengapa Windows 10 VM menunjukkan pemanfaatan CPU 100% selalu di QEMU-KVM?

  3. Mengapa Pengguna Root Membutuhkan Izin Sudo?

  1. Mengapa Bashrc Memeriksa Apakah Shell Saat Ini Interaktif?

  2. Linux – Nilai Vm.swappiness yang Tepat Saat Menggunakan Zram?

  3. Mengapa fungsi utama tanpa pernyataan pengembalian mengembalikan nilai 12?

  1. Mengapa Halaman Man Apt-key Menyarankan Untuk Tidak Menggunakan Perintah Tambahnya?

  2. kdevtmpfsi menggunakan seluruh CPU

  3. Mengapa output dari beberapa program Linux tidak menuju ke STDOUT atau STDERR?