थ्रेड शेड्यूलिंग का एक पूर्ण निराधार समाधान, जो प्रत्येक परीक्षण के अनुसार ठीक उसी समय प्राप्त करना चाहिए, अपने प्रोग्राम को ओएस स्वतंत्र होने के लिए संकलित करना और अपने कंप्यूटर को बूट करना है ताकि प्रोग्राम को ओएस-मुक्त वातावरण में चलाया जा सके। फिर भी, यह काफी हद तक अव्यवहारिक है और सबसे बेहतर होगा।
OS-free में जाने का एक अच्छा विकल्प सिर्फ वर्तमान थ्रेड की आत्मीयता को 1 कोर पर सेट करना और सर्वोच्चता को प्राथमिकता देना है। इस विकल्प को लगातार-पर्याप्त परिणाम प्रदान करना चाहिए।
इसके अलावा, आपको उन ऑप्टिमाइज़ेशन को बंद करना चाहिए जो डिबगिंग में हस्तक्षेप करेंगे, जो कि जी ++ या जीसीसी के लिए कमांड लाइन को जोड़ने -Og
का मतलब है , ताकि कोड को अनुकूलित होने से रोका जा सके। -O0
झंडा, क्योंकि यह अतिरिक्त अनावश्यक भूमि के ऊपर जो समय परिणामों में शामिल किया जाएगा पेश करता है, इस प्रकार कोड के समय की गति को बढ़ा नहीं किया जाना चाहिए।
इसके विपरीत, दोनों मानते हैं कि आप अंतिम उत्पादन बिल्ड पर " -Ofast
(या बहुत कम से कम -O3
) का उपयोग करते हैं और" मृत "कोड उन्मूलन के मुद्दे को अनदेखा करते हैं, -Og
की तुलना में बहुत कम अनुकूलन करते हैं -Ofast
; इस प्रकार -Og
अंतिम उत्पाद में कोड की वास्तविक गति का गलत विवरण दिया जा सकता है।
इसके अलावा, सभी गति परीक्षण (कुछ हद तक) perjure: के साथ संकलित अंतिम उत्पादन उत्पाद में -Ofast
, प्रत्येक स्निपेट / अनुभाग / कोड के फ़ंक्शन को अलग नहीं किया जाता है; बल्कि, कोड का प्रत्येक स्निपेट लगातार अगले में बहता है, इस प्रकार संकलक को संभावित रूप से जुड़ने, विलय करने और सभी जगह से कोड के टुकड़ों को एक साथ अनुकूलित करने की अनुमति मिलती है।
उसी समय, यदि आप कोड के एक स्निपेट को बेंचमार्क कर रहे हैं, जिसका भारी उपयोग हो रहा है realloc()
, तो कोड का स्निप उच्च उत्पादन में धीमी स्मृति विखंडन के साथ उत्पादन उत्पाद में धीमी गति से चल सकता है। इसलिए, अभिव्यक्ति "पूरी तरह से इसके भागों के योग से अधिक है" इस स्थिति पर लागू होती है क्योंकि अंतिम उत्पादन बिल्ड में कोड व्यक्तिगत स्निपेट की तुलना में काफी तेज या धीमी गति से चल सकता है जिसे आप गति परीक्षण कर रहे हैं।
एक आंशिक समाधान जो असंगति को कम कर सकता है -Ofast
गति परीक्षण के लिए उपयोग कर रहा है asm volatile("" :: "r"(var))
जिसमें मृत कोड / लूप उन्मूलन को रोकने के लिए परीक्षण में शामिल चर शामिल किए गए हैं।
यहाँ एक उदाहरण है कि विंडोज कंप्यूटर पर स्क्वायर रूट फ़ंक्शन को कैसे बेंच दिया जाए।
// 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;
}
इसके अलावा, अपने टाइमर के लिए माइक जार्विस को श्रेय दिया।
कृपया ध्यान दें (यह बहुत महत्वपूर्ण है) कि यदि आप बड़े कोड स्निपेट चलाने जा रहे हैं, तो आपको वास्तव में अपने कंप्यूटर को ठंड से बचाने के लिए पुनरावृत्तियों की संख्या को बंद करना होगा।