Jawaban baru ini menggunakan fasilitas C ++ 11 <chrono>
. Meskipun ada jawaban lain yang menunjukkan cara menggunakan <chrono>
, tidak satupun yang menunjukkan cara menggunakan <chrono>
dengan RDTSC
fasilitas yang disebutkan dalam beberapa jawaban lain di sini. Jadi saya pikir saya akan menunjukkan cara menggunakan RDTSC
dengan <chrono>
. Selain itu, saya akan mendemonstrasikan bagaimana Anda dapat membuat template kode pengujian pada jam sehingga Anda dapat dengan cepat beralih di antara RDTSC
dan fasilitas jam bawaan sistem Anda (yang kemungkinan akan didasarkan pada clock()
, clock_gettime()
dan / atau QueryPerformanceCounter
.
Perhatikan bahwa RDTSC
instruksinya khusus x86. QueryPerformanceCounter
hanya untuk Windows. Dan clock_gettime()
hanya POSIX. Di bawah ini saya perkenalkan dua jam baru: std::chrono::high_resolution_clock
dan std::chrono::system_clock
, yang, jika Anda dapat mengasumsikan C ++ 11, sekarang bersifat lintas platform.
Pertama, berikut adalah cara Anda membuat jam yang kompatibel dengan C ++ 11 dari rdtsc
instruksi perakitan Intel . Saya akan menyebutnya x::clock
:
#include <chrono>
namespace x
{
struct clock
{
typedef unsigned long long rep;
typedef std::ratio<1, 2'800'000'000> period; // My machine is 2.8 GHz
typedef std::chrono::duration<rep, period> duration;
typedef std::chrono::time_point<clock> time_point;
static const bool is_steady = true;
static time_point now() noexcept
{
unsigned lo, hi;
asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
return time_point(duration(static_cast<rep>(hi) << 32 | lo));
}
};
} // x
Semua jam ini menghitung siklus CPU dan menyimpannya dalam integer 64-bit unsigned. Anda mungkin perlu mengubah sintaks bahasa assembly untuk kompiler Anda. Atau kompilator Anda mungkin menawarkan intrinsik yang dapat Anda gunakan sebagai gantinya (mis now() {return __rdtsc();}
.).
Untuk membuat jam, Anda harus memberikan representasi (tipe penyimpanan). Anda juga harus menyediakan periode jam, yang harus berupa konstanta waktu kompilasi, meskipun mesin Anda mungkin mengubah kecepatan jam dalam mode daya yang berbeda. Dan dari sana, Anda dapat dengan mudah menentukan durasi waktu "asli" jam dan titik waktu dalam kaitannya dengan dasar-dasar ini.
Jika yang ingin Anda lakukan hanyalah menampilkan jumlah detak jam, tidak masalah angka apa yang Anda berikan untuk periode jam tersebut. Konstanta ini hanya berperan jika Anda ingin mengubah jumlah jam menjadi beberapa unit waktu nyata seperti nanodetik. Dan dalam hal ini, semakin akurat Anda dapat memberikan kecepatan clock, semakin akurat konversi ke nanodetik, (milidetik, apa pun).
Di bawah ini adalah contoh kode yang menunjukkan cara menggunakan x::clock
. Sebenarnya saya telah membuat template kode pada jam karena saya ingin menunjukkan bagaimana Anda dapat menggunakan banyak jam berbeda dengan sintaks yang sama persis. Tes khusus ini menunjukkan apa overhead perulangan saat menjalankan apa yang Anda inginkan untuk waktu di bawah satu loop:
#include <iostream>
template <class clock>
void
test_empty_loop()
{
// Define real time units
typedef std::chrono::duration<unsigned long long, std::pico> picoseconds;
// or:
// typedef std::chrono::nanoseconds nanoseconds;
// Define double-based unit of clock tick
typedef std::chrono::duration<double, typename clock::period> Cycle;
using std::chrono::duration_cast;
const int N = 100000000;
// Do it
auto t0 = clock::now();
for (int j = 0; j < N; ++j)
asm volatile("");
auto t1 = clock::now();
// Get the clock ticks per iteration
auto ticks_per_iter = Cycle(t1-t0)/N;
std::cout << ticks_per_iter.count() << " clock ticks per iteration\n";
// Convert to real time units
std::cout << duration_cast<picoseconds>(ticks_per_iter).count()
<< "ps per iteration\n";
}
Hal pertama yang dilakukan kode ini adalah membuat unit "waktu nyata" untuk menampilkan hasilnya. Saya telah memilih picoseconds, tetapi Anda dapat memilih unit apa pun yang Anda suka, baik integral atau berbasis floating point. Sebagai contoh ada std::chrono::nanoseconds
unit yang sudah jadi yang bisa saya gunakan.
Sebagai contoh lain, saya ingin mencetak jumlah rata-rata siklus jam per iterasi sebagai floating point, jadi saya membuat durasi lain, berdasarkan ganda, yang memiliki unit yang sama dengan tick jam (disebut Cycle
dalam kode).
Loop diatur dengan panggilan ke clock::now()
salah satu sisi. Jika Anda ingin memberi nama tipe yang dikembalikan dari fungsi ini:
typename clock::time_point t0 = clock::now();
(seperti yang ditunjukkan dengan jelas dalam x::clock
contoh, dan juga berlaku untuk jam yang dipasok sistem).
Untuk mendapatkan durasi dalam hal jam floating point, seseorang hanya mengurangi dua titik waktu, dan untuk mendapatkan nilai per iterasi, bagi durasi itu dengan jumlah iterasi.
Anda bisa mendapatkan hitungan dalam durasi berapa pun dengan menggunakan count()
fungsi anggota. Ini mengembalikan representasi internal. Akhirnya saya gunakan std::chrono::duration_cast
untuk mengubah durasi Cycle
menjadi durasi picoseconds
dan mencetaknya.
Untuk menggunakan kode ini sederhana:
int main()
{
std::cout << "\nUsing rdtsc:\n";
test_empty_loop<x::clock>();
std::cout << "\nUsing std::chrono::high_resolution_clock:\n";
test_empty_loop<std::chrono::high_resolution_clock>();
std::cout << "\nUsing std::chrono::system_clock:\n";
test_empty_loop<std::chrono::system_clock>();
}
Di atas saya melakukan pengujian menggunakan buatan kami x::clock
, dan membandingkan hasil tersebut dengan menggunakan dua jam yang dipasok sistem: std::chrono::high_resolution_clock
dan std::chrono::system_clock
. Bagi saya ini cetakannya:
Using rdtsc:
1.72632 clock ticks per iteration
616ps per iteration
Using std::chrono::high_resolution_clock:
0.620105 clock ticks per iteration
620ps per iteration
Using std::chrono::system_clock:
0.00062457 clock ticks per iteration
624ps per iteration
Ini menunjukkan bahwa setiap jam ini memiliki periode detak yang berbeda, karena detak per iterasi sangat berbeda untuk setiap jam. Namun, ketika dikonversi ke satuan waktu yang diketahui (misalnya pikodetik), saya mendapatkan hasil yang kira-kira sama untuk setiap jam (jarak tempuh Anda mungkin berbeda-beda).
Perhatikan bagaimana kode saya benar-benar bebas dari "konstanta konversi ajaib". Memang, hanya ada dua angka ajaib di seluruh contoh:
- Kecepatan clock mesin saya untuk ditentukan
x::clock
.
- Jumlah iterasi yang akan diuji. Jika mengubah nomor ini membuat hasil Anda sangat bervariasi, maka Anda mungkin harus membuat jumlah iterasi lebih tinggi, atau mengosongkan komputer Anda dari proses yang bersaing saat pengujian.