सामान्य अनुकूलन
यहाँ मेरे पसंदीदा अनुकूलन में से कुछ के रूप में। मैंने वास्तव में इनका उपयोग करके निष्पादन समय और कार्यक्रम के आकार को कम किया है।
के रूप में छोटे कार्यों की घोषणा inline
या मैक्रोज़
एक फ़ंक्शन (या विधि) के लिए प्रत्येक कॉल ओवरहेड को सम्मिलित करता है, जैसे कि स्टैक पर चर धक्का। कुछ कार्यों के रूप में अच्छी तरह से वापसी पर एक भूमि के ऊपर रख सकते हैं। एक अक्षम समारोह या विधि की संयुक्त ओवरहेड की तुलना में इसकी सामग्री में कम बयान हैं। ये इनलाइनिंग के लिए अच्छे उम्मीदवार हैं, चाहे वह #define
मैक्रोज़ हो या inline
फ़ंक्शंस। (हां, मुझे पता inline
है कि यह केवल एक सुझाव है, लेकिन इस मामले में मैं इसे संकलक के अनुस्मारक के रूप में मानता हूं ।)
मृत और निरर्थक कोड निकालें
यदि कोड का उपयोग नहीं किया गया है या प्रोग्राम के परिणाम में योगदान नहीं करता है, तो इसे हटा दें।
एल्गोरिदम के डिजाइन को सरल बनाएं
मैंने एक बार एक प्रोग्राम से बहुत सारे असेंबली कोड और एक्जीक्यूशन टाइम निकाले थे, जो बीजीय समीकरण लिखकर गणना कर रहा था और फिर बीजीय अभिव्यक्ति को सरल बनाया। सरलीकृत बीजीय अभिव्यक्ति के कार्यान्वयन ने मूल फ़ंक्शन की तुलना में कम कमरा और समय लिया।
लूप अनरोलिंग
प्रत्येक लूप में वेतन वृद्धि और समाप्ति जाँच होती है। प्रदर्शन कारक का अनुमान प्राप्त करने के लिए, ओवरहेड में निर्देशों की संख्या गिनें (न्यूनतम 3: वेतन वृद्धि, चेक, लूप की शुरुआत) और लूप के अंदर बयानों की संख्या से विभाजित करें। कम संख्या बेहतर है।
संपादित करें: इससे पहले लूप के अनियंत्रित होने का एक उदाहरण प्रदान करें :
unsigned int sum = 0;
for (size_t i; i < BYTES_TO_CHECKSUM; ++i)
{
sum += *buffer++;
}
अनियंत्रित होने के बाद:
unsigned int sum = 0;
size_t i = 0;
**const size_t STATEMENTS_PER_LOOP = 8;**
for (i = 0; i < BYTES_TO_CHECKSUM; **i = i / STATEMENTS_PER_LOOP**)
{
sum += *buffer++; // 1
sum += *buffer++; // 2
sum += *buffer++; // 3
sum += *buffer++; // 4
sum += *buffer++; // 5
sum += *buffer++; // 6
sum += *buffer++; // 7
sum += *buffer++; // 8
}
// Handle the remainder:
for (; i < BYTES_TO_CHECKSUM; ++i)
{
sum += *buffer++;
}
इस लाभ में, एक माध्यमिक लाभ प्राप्त होता है: प्रोसेसर के अनुदेश कैश को फिर से लोड करने से पहले अधिक विवरण निष्पादित किए जाते हैं।
मेरे पास आश्चर्यजनक परिणाम हैं जब मैंने 32 कथनों में एक लूप को अनियंत्रित किया। यह 2 जीबी फ़ाइल पर चेकसम की गणना करने के बाद से यह एक अड़चन थी। ब्लॉक रीडिंग के साथ संयुक्त इस अनुकूलन ने 1 घंटे से 5 मिनट तक बेहतर प्रदर्शन किया। लूप अनरोलिंग ने असेंबली लैंग्वेज में भी बेहतरीन परफॉर्मेंस दी, मेरा memcpy
कंपाइलर के मुकाबले काफी तेज था memcpy
। - टीएम
if
बयानों में कमी
प्रोसेसर शाखाओं से नफरत करते हैं, या कूदते हैं, क्योंकि यह प्रोसेसर को निर्देशों की अपनी कतार को फिर से लोड करने के लिए मजबूर करता है।
बूलियन अंकगणित ( संपादित: कोड टुकड़ा करने के लिए कोड प्रारूप लागू, उदाहरण जोड़ा गया)
if
बूलियन असाइनमेंट में स्टेटमेंट को कन्वर्ट करें । कुछ प्रोसेसर बिना ब्रांचिंग के निर्देशों को निष्पादित कर सकते हैं:
bool status = true;
status = status && /* first test */;
status = status && /* second test */;
लॉजिकल एंड ऑपरेटर ( ) की शॉर्ट सर्किटिंग , यदि है तो परीक्षणों के निष्पादन को रोकती है ।&&
status
false
उदाहरण:
struct Reader_Interface
{
virtual bool write(unsigned int value) = 0;
};
struct Rectangle
{
unsigned int origin_x;
unsigned int origin_y;
unsigned int height;
unsigned int width;
bool write(Reader_Interface * p_reader)
{
bool status = false;
if (p_reader)
{
status = p_reader->write(origin_x);
status = status && p_reader->write(origin_y);
status = status && p_reader->write(height);
status = status && p_reader->write(width);
}
return status;
};
छोरों के बाहर फैक्टर चर आवंटन
यदि लूप के अंदर मक्खी पर एक चर बनाया जाता है, तो लूप से पहले निर्माण / आवंटन को स्थानांतरित करें। अधिकांश उदाहरणों में, चर को प्रत्येक पुनरावृत्ति के दौरान आवंटित करने की आवश्यकता नहीं होती है।
छोरों के बाहर लगातार स्थिर भाव
यदि गणना या चर मान लूप इंडेक्स पर निर्भर नहीं करता है, तो इसे लूप के बाहर (पहले) स्थानांतरित करें।
I / O ब्लॉकों में
बड़े चंक्स (ब्लॉक) में डेटा पढ़ें और लिखें। जितना बड़ा उतना अच्छा। उदाहरण के लिए, एक समय में एक ऑक्टेक्ट को पढ़ना एक रीड के साथ 1024 ऑक्टेट पढ़ने की तुलना में कम कुशल है।
उदाहरण:
static const char Menu_Text[] = "\n"
"1) Print\n"
"2) Insert new customer\n"
"3) Destroy\n"
"4) Launch Nasal Demons\n"
"Enter selection: ";
static const size_t Menu_Text_Length = sizeof(Menu_Text) - sizeof('\0');
//...
std::cout.write(Menu_Text, Menu_Text_Length);
इस तकनीक की दक्षता को नेत्रहीन रूप से प्रदर्शित किया जा सकता है। :-)
निरंतर डेटा के लिए printf
परिवार का उपयोग न करें
एक ब्लॉक राइट का उपयोग करके लगातार डेटा आउटपुट किया जा सकता है। फ़ॉर्मेट किया गया लेखन वर्णों को प्रारूपित करने या आदेश स्वरूपण के लिए पाठ को स्कैन करने में समय बर्बाद करेगा। ऊपर कोड उदाहरण देखें।
स्मृति को प्रारूपित करें, फिर लिखें
char
एकाधिक का उपयोग करके किसी सरणी में प्रारूपित करें sprintf
, फिर उपयोग करें fwrite
। यह डेटा लेआउट को "निरंतर वर्गों" और चर खंडों में विभाजित करने की अनुमति देता है। मेल-मर्ज के बारे में सोचो ।
के रूप में निरंतर पाठ (स्ट्रिंग शाब्दिक) की घोषणा करें static const
जब चर बिना घोषित किए जाते हैं static
, तो कुछ संकलक स्टैक पर स्थान आवंटित कर सकते हैं और रोम से डेटा की प्रतिलिपि बना सकते हैं। ये दो अनावश्यक ऑपरेशन हैं। यह static
उपसर्ग का उपयोग करके तय किया जा सकता है ।
अंत में, कोड संकलक की तरह होगा
कभी-कभी, कंपाइलर कई छोटे बयानों को एक जटिल संस्करण से बेहतर रूप से अनुकूलित कर सकता है। साथ ही, कंपाइलर ऑप्टिमाइज़ करने में मदद के लिए कोड लिखना भी मदद करता है। यदि मैं चाहता हूं कि कंपाइलर विशेष ब्लॉक ट्रांसफर निर्देशों का उपयोग करें, तो मैं कोड लिखूंगा जो दिखता है कि इसे विशेष निर्देशों का उपयोग करना चाहिए।