Her test için tam olarak aynı süreleri vermesi gereken iş parçacığı planlamasına yönelik eksiksiz bir çözüm, programınızı işletim sisteminden bağımsız olacak şekilde derlemek ve programı işletim sistemi içermeyen bir ortamda çalıştırmak için bilgisayarınızı başlatmaktır. Yine de, bu büyük ölçüde pratik değildir ve en iyi ihtimalle zor olacaktır.
İşletim sisteminden kurtulmanın iyi bir alternatifi, mevcut iş parçacığının yakınlığını 1 çekirdeğe ve önceliği en yüksek değere ayarlamaktır. Bu alternatif, yeterince tutarlı sonuçlar sağlamalıdır.
Ayrıca, g ++ veya gcc için test edilen kodun optimize edilmesini önlemek için komut satırına ekleme-Og
anlamına gelen hata ayıklamayı engelleyecek optimizasyonları da kapatmalısınız . -O0
Böylelikle kod zamanlanmış hızını skewing zamanlama sonuçlarına dahil olacağını ekstra gereksiz yükü tanıtır çünkü bayrak kullanılmamalıdır.
Aksine, hem son üretim yapısında kullandığınızı -Ofast
(veya en azından -O3
) varsaymak hem de "ölü" kod eleme konusunu görmezden gelmek, aşağıdakilere -Og
kıyasla çok az optimizasyon gerçekleştirir -Ofast
; bu nedenle -Og
nihai üründe kodun gerçek hızını yanlış gösterebilir.
Ayrıca, tüm hız testleri (bir dereceye kadar) şerefine: ile derlenen nihai üretim ürününde -Ofast
, kodun her bir pasajı / bölümü / işlevi izole edilmemiştir; daha ziyade, her kod parçacığı sürekli olarak bir sonrakine akar, böylece derleyicinin potansiyel olarak her yerden kod parçalarını birleştirmesine, birleştirmesine ve bir araya getirmesine izin verir.
Aynı zamanda, yoğun şekilde kullanılan bir kod parçacığını karşılaştırıyorsanız realloc()
, yeterince yüksek bellek parçalanması olan bir üretim ürününde kod parçacığı daha yavaş çalışabilir. Bu nedenle, "bütün, parçalarının toplamından daha fazlasıdır" ifadesi bu duruma uygulanır, çünkü nihai üretim yapısındaki kod, hız testi yaptığınız tekil parçacığa göre fark edilir derecede daha hızlı veya daha yavaş çalışabilir.
Uyuşmazlığı azaltabilecek kısmi bir çözüm, ölü kod / döngü eliminasyonunu önlemek için teste dahil olan değişkenlere -Ofast
ek olarak hız testi için kullanmaktır asm volatile("" :: "r"(var))
.
İşte bir Windows bilgisayarda karekök işlevlerinin nasıl karşılaştırılacağına dair bir örnek.
// 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;
}
Ayrıca, Timer'ı için Mike Jarvis'e teşekkür edin.
Lütfen (bu çok önemlidir) daha büyük kod parçacıkları çalıştıracaksanız, bilgisayarınızın donmasını önlemek için yineleme sayısını gerçekten azaltmanız gerektiğini unutmayın.