Solusi lengkap yang tidak pernah gagal untuk penjadwalan utas, yang harus menghasilkan waktu yang persis sama untuk setiap pengujian, adalah dengan mengkompilasi program Anda menjadi OS independen dan mem-boot komputer Anda untuk menjalankan program dalam lingkungan bebas OS. Namun, ini sebagian besar tidak praktis dan paling-paling sulit.
Pengganti yang baik untuk bebas OS adalah dengan mengatur afinitas utas saat ini ke 1 inti dan prioritas ke yang tertinggi. Alternatif ini harus memberikan hasil yang cukup konsisten.
Anda juga harus menonaktifkan pengoptimalan yang akan mengganggu proses debug, yang untuk g ++ atau gcc berarti menambahkan -Og
ke baris perintah , untuk mencegah kode yang sedang diuji dioptimalkan. The -O0
bendera tidak boleh digunakan karena memperkenalkan overhead tambahan tidak dibutuhkan yang akan dimasukkan dalam hasil waktu, sehingga skewing kecepatan waktunya kode.
Sebaliknya, keduanya mengasumsikan bahwa Anda menggunakan -Ofast
(atau, paling tidak, -O3
) pada pembuatan produksi akhir dan mengabaikan masalah penghapusan kode "mati", -Og
melakukan pengoptimalan yang sangat sedikit dibandingkan dengan -Ofast
; jadi-Og
dapat salah menggambarkan kecepatan sebenarnya dari kode dalam produk akhir.
Selanjutnya, semua uji kecepatan (sampai batas tertentu) sumpah palsu: dalam produk produksi akhir yang dikompilasi dengan -Ofast
, setiap potongan / bagian / fungsi kode tidak diisolasi; sebaliknya, setiap potongan kode terus mengalir ke yang berikutnya, sehingga memungkinkan kompiler untuk berpotensi bergabung, menggabungkan, dan mengoptimalkan bersama potongan kode dari semua tempat.
Pada saat yang sama, jika Anda membuat tolok ukur potongan kode yang sangat sering digunakan realloc()
, cuplikan kode tersebut mungkin berjalan lebih lambat dalam produk produksi dengan fragmentasi memori yang cukup tinggi. Oleh karena itu, ekspresi "keseluruhan lebih dari jumlah bagiannya" berlaku untuk situasi ini karena kode dalam pembuatan produksi akhir mungkin berjalan jauh lebih cepat atau lebih lambat daripada cuplikan individu yang Anda uji kecepatannya.
Sebuah solusi parsial yang dapat mengurangi ketidaksesuaian digunakan -Ofast
untuk pengujian kecepatan DENGAN penambahan asm volatile("" :: "r"(var))
variabel yang terlibat dalam pengujian untuk mencegah penghapusan kode / loop mati.
Berikut adalah contoh cara mengukur fungsi akar kuadrat di komputer Windows.
// set USE_ASM_TO_PREVENT_ELIMINATION to 0 to prevent `asm volatile("" :: "r"(var))`
// set USE_ASM_TO_PREVENT_ELIMINATION to 1 to enforce `asm volatile("" :: "r"(var))`
#define USE_ASM_TO_PREVENT_ELIMINATION 1
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <chrono>
#include <cmath>
#include <windows.h>
#include <intrin.h>
#pragma intrinsic(__rdtsc)
#include <cstdint>
class Timer {
public:
Timer() : beg_(clock_::now()) {}
void reset() { beg_ = clock_::now(); }
double elapsed() const {
return std::chrono::duration_cast<second_>
(clock_::now() - beg_).count(); }
private:
typedef std::chrono::high_resolution_clock clock_;
typedef std::chrono::duration<double, std::ratio<1> > second_;
std::chrono::time_point<clock_> beg_;
};
unsigned int guess_sqrt32(register unsigned int n) {
register unsigned int g = 0x8000;
if(g*g > n) {
g ^= 0x8000;
}
g |= 0x4000;
if(g*g > n) {
g ^= 0x4000;
}
g |= 0x2000;
if(g*g > n) {
g ^= 0x2000;
}
g |= 0x1000;
if(g*g > n) {
g ^= 0x1000;
}
g |= 0x0800;
if(g*g > n) {
g ^= 0x0800;
}
g |= 0x0400;
if(g*g > n) {
g ^= 0x0400;
}
g |= 0x0200;
if(g*g > n) {
g ^= 0x0200;
}
g |= 0x0100;
if(g*g > n) {
g ^= 0x0100;
}
g |= 0x0080;
if(g*g > n) {
g ^= 0x0080;
}
g |= 0x0040;
if(g*g > n) {
g ^= 0x0040;
}
g |= 0x0020;
if(g*g > n) {
g ^= 0x0020;
}
g |= 0x0010;
if(g*g > n) {
g ^= 0x0010;
}
g |= 0x0008;
if(g*g > n) {
g ^= 0x0008;
}
g |= 0x0004;
if(g*g > n) {
g ^= 0x0004;
}
g |= 0x0002;
if(g*g > n) {
g ^= 0x0002;
}
g |= 0x0001;
if(g*g > n) {
g ^= 0x0001;
}
return g;
}
unsigned int empty_function( unsigned int _input ) {
return _input;
}
unsigned long long empty_ticks=0;
double empty_seconds=0;
Timer my_time;
template<unsigned int benchmark_repetitions>
void benchmark( char* function_name, auto (*function_to_do)( auto ) ) {
register unsigned int i=benchmark_repetitions;
register unsigned long long start=0;
my_time.reset();
start=__rdtsc();
while ( i-- ) {
auto result = (*function_to_do)( i << 7 );
#if USE_ASM_TO_PREVENT_ELIMINATION == 1
asm volatile("" :: "r"(
// There is no data type in C++ that is smaller than a char, so it will
// not throw a segmentation fault error to reinterpret any arbitrary
// data type as a char. Although, the compiler might not like it.
result
));
#endif
}
if ( function_name == nullptr ) {
empty_ticks = (__rdtsc()-start);
empty_seconds = my_time.elapsed();
std::cout<< "Empty:\n" << empty_ticks
<< " ticks\n" << benchmark_repetitions << " repetitions\n"
<< std::setprecision(15) << empty_seconds
<< " seconds\n\n";
} else {
std::cout<< function_name<<":\n" << (__rdtsc()-start-empty_ticks)
<< " ticks\n" << benchmark_repetitions << " repetitions\n"
<< std::setprecision(15) << (my_time.elapsed()-empty_seconds)
<< " seconds\n\n";
}
}
int main( void ) {
void* Cur_Thread= GetCurrentThread();
void* Cur_Process= GetCurrentProcess();
unsigned long long Current_Affinity;
unsigned long long System_Affinity;
unsigned long long furthest_affinity;
unsigned long long nearest_affinity;
if( ! SetThreadPriority(Cur_Thread,THREAD_PRIORITY_TIME_CRITICAL) ) {
SetThreadPriority( Cur_Thread, THREAD_PRIORITY_HIGHEST );
}
if( ! SetPriorityClass(Cur_Process,REALTIME_PRIORITY_CLASS) ) {
SetPriorityClass( Cur_Process, HIGH_PRIORITY_CLASS );
}
GetProcessAffinityMask( Cur_Process, &Current_Affinity, &System_Affinity );
furthest_affinity = 0x8000000000000000ULL>>__builtin_clzll(Current_Affinity);
nearest_affinity = 0x0000000000000001ULL<<__builtin_ctzll(Current_Affinity);
SetProcessAffinityMask( Cur_Process, furthest_affinity );
SetThreadAffinityMask( Cur_Thread, furthest_affinity );
const int repetitions=524288;
benchmark<repetitions>( nullptr, empty_function );
benchmark<repetitions>( "Standard Square Root", standard_sqrt );
benchmark<repetitions>( "Original Guess Square Root", original_guess_sqrt32 );
benchmark<repetitions>( "New Guess Square Root", new_guess_sqrt32 );
SetThreadPriority( Cur_Thread, THREAD_PRIORITY_IDLE );
SetPriorityClass( Cur_Process, IDLE_PRIORITY_CLASS );
SetProcessAffinityMask( Cur_Process, nearest_affinity );
SetThreadAffinityMask( Cur_Thread, nearest_affinity );
for (;;) { getchar(); }
return 0;
}
Juga, penghargaan untuk Mike Jarvis atas Timernya.
Harap dicatat (ini sangat penting) bahwa jika Anda akan menjalankan cuplikan kode yang lebih besar, maka Anda benar-benar harus menolak jumlah iterasi untuk mencegah komputer Anda membeku.