2009-09-22 7 views
22

पर विचार इस तरह एक उदाहरण:क्या सी ++ कंपाइलर्स "लूप" के अंदर "if" कथन अनुकूलित कर सकते हैं?

for (condition) 
    if (flag) 
    do_something(); 
    else 
    do_something_else(); 

केवल पहले मामले में, कोड हो सकता है:

if (flag) 
    for (condition) 
    do_something(); 
else 
    for (condition) 
    do_something_else(); 

तो flagfor छोरों के अंदर परिवर्तन नहीं करता है, यह अर्थ की दृष्टि से बराबर करने के लिए किया जाना चाहिए बहुत लंबा (उदाहरण के लिए यदि for लूप का उपयोग किया जाता है या do_something() एक कोड ब्लॉक है जो अधिकतर do_something_else() के समान होता है), जबकि दूसरे मामले में, ध्वज कई बार चेक किया जाता है।

मुझे उत्सुकता है कि मौजूदा सी ++ कंपाइलर्स (सबसे महत्वपूर्ण बात यह है कि जी ++) for लूप के अंदर दोहराए गए परीक्षणों से छुटकारा पाने के लिए दूसरे उदाहरण को अनुकूलित करने में सक्षम होंगे। यदि हां, तो यह किस स्थितियों के तहत संभव है?

उत्तर

18

हाँ, अगर यह है कि झंडा परिवर्तन नहीं करता है निर्धारित किया जाता है और do_something या do_something_else द्वारा बदला नहीं जा सकता है, यह पाश बाहर खींचा जा सकता है। मैंने इस लूप होस्टिंग के बारे में सुना है, लेकिन विकिपीडिया में entry है जिसे "लूप इनवेरिएंट कोड गति" कहा जाता है।

तो झंडे एक स्थानीय चर रहा है, संकलक के बाद से यह उत्पन्न कोड के व्यवहार पर कोई असर नहीं गारंटी है इस अनुकूलन करने के लिए सक्षम होना चाहिए।

झंडे एक वैश्विक चर रहा है, और आप अपने पाश अंदर कार्यों कॉल यह अनुकूलन प्रदर्शन नहीं दिख रहा है - यह निर्धारित करने के लिए उन कार्यों वैश्विक संशोधित करता है, तो सक्षम नहीं हो सकता।

यह भी अनुकूलन की तरह से प्रभावित हो सकते आप करते हैं - आकार के लिए अनुकूलित करने के साथ गति के लिए अनुकूलन शायद फहराया संस्करण एहसान होगा गैर फहराया संस्करण एहसान होगा।

आम तौर पर, यह ऐसी चीज नहीं है जिसके बारे में आपको चिंता करनी चाहिए, जब तक कि प्रोफाइलिंग आपको बताए कि फ़ंक्शन एक हॉटस्पॉट है और आप देखते हैं कि वास्तव में असेंबली पर असेंबली द्वारा कुशल कोड से कम उत्पन्न किया जा रहा है आउटपुट। इस तरह के माइक्रो-ऑप्टिमाइज़ेशन आपको हमेशा कंपाइलर तक छोड़ देना चाहिए जबतक कि आपको बिल्कुल नहीं करना चाहिए।

+5

उत्तर के लिए धन्यवाद। आपके विकिपीडिया लिंक से मुझे "लूप अनविचिंग" के लिए पृष्ठ मिला है, जो इस तरह के अनुकूलन के लिए एक और अधिक सटीक शब्द प्रतीत होता है। –

+0

हालांकि शब्द वास्तव में कट और सूखे नहीं होते हैं, लूप-होस्टिंग और लूप-इनवेरिएंट-कोड-गति वास्तव में इस अनुकूलन का वर्णन करने के लिए उपयोग नहीं किया जाता है। वे एकल निर्देशों के लिए अधिक हैं। –

+0

ओपी द्वारा संदर्भित 'लूप अनविचिंग' से लिंक: https://en.wikipedia.org/wiki/Loop_unswitching – BrodieG

1

मैं अगर संकलक निर्धारित कर सकते हैं कि झंडा स्थिर रहेंगे यकीन है, यह कुछ shufflling कर सकते हैं:,

const bool flag = /* ... */; 
for (..;..;..;) 
{ 
    if (flag) 
    { 
     // ... 
    } 
    else 
    { 
     // ... 
    } 
} 

तो flagconst नहीं है, संकलक जरूरी पाश अनुकूलित नहीं कर सकते क्योंकि यह सुनिश्चित नहीं हो सकता flag नहीं बदलेगा। यह कर सकता है अगर यह स्थिर विश्लेषण करता है, लेकिन सभी कंपाइलर नहीं करते हैं, मुझे लगता है। const संकलक को बताने का निश्चित अग्नि तरीका है ध्वज नहीं बदलेगा, उसके बाद यह कंपाइलर पर निर्भर करेगा।

सामान्य रूप से, प्रोफ़ाइल और पता लगाएं कि यह वास्तव में एक समस्या है या नहीं।

+2

'const' एक कंपाइलर-चेक की गई स्थिति है, लेकिन इसका अनुकूलन पर कोई प्रभाव नहीं पड़ता है। – peterchen

+0

चर का दायरा शायद अधिक महत्वपूर्ण है लेकिन स्थिरता अनुकूलन पर असर डाल सकती है। यह 'ऑब्जेक्ट' वाली ऑब्जेक्ट को बदलने के लिए अपरिभाषित व्यवहार है (यह किसी ऑब्जेक्ट को बदलने के लिए 'const_cast' का उपयोग करने से अलग है जो' कॉन्स्ट 'ऑब्जेक्ट नहीं है, लेकिन जिस संदर्भ के माध्यम से ऑब्जेक्ट ज्ञात है, वह' const 'संदर्भ) तो एक कंपाइलर * इस जानकारी का उपयोग अपने मूल्य को कैश करने के लिए कर सकता है। –

+2

पीटर, 'कॉन्स्ट' में अनुकूलन के साथ सबकुछ करना है। – GManNickG

0

यह एक पाश अपरिवर्तनीय कहा जाता है और अनुकूलन पाश अपरिवर्तनीय कोड गति कहा जाता है और यह भी कोड उत्थापन। तथ्य यह है कि यह एक सशर्त में है, निश्चित रूप से कोड विश्लेषण को अधिक जटिल बना देगा और संकलक लूप को सशर्त कर सकता है या सशर्त नहीं कर सकता है कि अनुकूलक कितना चालाक है।

इस तरह के प्रश्न के किसी भी विशिष्ट मामले के लिए एक सामान्य उत्तर है, और यह आपके प्रोग्राम को संकलित करना और जेनरेट कोड को देखना है।

0

मैं यह कहने से सावधान रहूंगा कि यह होगा। क्या यह गारंटी दे सकता है कि मूल्य इस, या किसी अन्य धागे द्वारा संशोधित नहीं किया जाएगा?

यह कहा गया है कि कोड का दूसरा संस्करण आमतौर पर अधिक पठनीय है और यह कोड के ब्लॉक में अनुकूलित करने की आखिरी बात होगी।

+0

@patros - यदि यह एक स्थानीय चर है, तो बहुप्रचारित खेलने में नहीं आना चाहिए। भले ही चर अन्य धागे के लिए दृश्यमान है, संकलक अभी भी अनुकूलन निष्पादित कर सकता है - जब तक आप अस्थिर के रूप में चिह्नित नहीं करते हैं, संकलक को प्रत्येक एक्सेस पर रीफ्रैच करने की आवश्यकता नहीं होती है और कोडेजन मान्य है। – Michael

+0

@ माइकल - सहमत है, लेकिन हमें इसकी परिभाषा नहीं दिखाई दे रही है इस स्निपेट में ध्वज, इसलिए यह स्थानीय नहीं हो सकता है। मैं यह भी मानता हूं कि संकलक कानूनी रूप से ओ कर सकता है इसे पीटीआईमाइज़ करें, बस यह नहीं कि यह हमेशा होगा। यदि ऐसा होगा तो यह संभवतः चरणीय दायरे और कंपाइलर झंडे का एक कार्य है। – patros

1

आम तौर पर, हाँ। लेकिन इसकी कोई गारंटी नहीं है, और उन जगहों पर जहां संकलक ऐसा करेंगे, शायद दुर्लभ हैं।

किसी भी समस्या के बिना अधिकांश कंपाइलर क्या करते हैं, लूप से अपरिवर्तनीय मूल्यांकन हो रहा है, उदा। यदि आपका हालत

if (a<b) .... 

है जब ए और बी पाश से प्रभावित नहीं हैं, तुलना पाश से पहले एक बार किया जाएगा।

इसका मतलब है कि यदि संकलक निर्धारित कर सकता है कि स्थिति बदलती नहीं है, तो परीक्षण सस्ता है और कूदने की भविष्यवाणी की गई है। बदले में इसका मतलब है कि परीक्षण में स्वयं एक चक्र या कोई चक्र नहीं होता है (वास्तव में)।

लूप को विभाजित करने वाले मामलों में कौन सा मामला फायदेमंद होगा?

कोड के बारे में मान्यताओं क) एक बहुत तंग पाश जहां 1 चक्र एक महत्वपूर्ण लागत
ख) दोनों भागों के साथ पूरे पाश अब कोड कैश

फिट नहीं करता है, संकलक केवल कर सकते हैं कैश, और आमतौर पर कोड को इस तरह से ऑर्डर कर सकता है कि एक शाखा कैश फिट करेगी।

किसी भी परीक्षण के बिना, I'dexpect क) केवल इस मामले में जहां इस तरह के एक अनुकूलन लागू किया जाएगा, क्योंकि यह nto हमेशा बेहतर विकल्प है:

जिसमें पाश बंटवारे मामलों बुरा हो सकता है?

लूप को विभाजित करते समय कोड कैश से परे कोड आकार बढ़ाता है, तो आप एक महत्वपूर्ण हिट लेंगे। अब, यह केवल आपको प्रभावित करता है अगर लूप को किसी अन्य लूप के भीतर बुलाया जाता है, लेकिन ऐसा कुछ ऐसा होता है जो आमतौर पर संकलक निर्धारित नहीं कर सकता है।


मैं VC9 निम्नलिखित पाश

extern volatile int vflag = 0; 

int foo(int count) 
{ 
    int sum = 0; 
    int flag = vflag; 
    for(int i=0; i<count; ++i) 
    { 
     if (flag) 
     sum += i; 
     else 
     sum -= i; 
    } 

    return sum; 
} 

[संपादित करें 2]
(कुछ मामलों में जहां यह वास्तव में फायदेमंद हो सकता है में से एक) विभाजित करने के लिए नहीं मिल सका [संपादित करें] ध्यान दें कि int flag = true; के साथ दूसरी शाखा अनुकूलित हो जाती है। (और नहीं, कॉन्स यहां कोई फर्क नहीं पड़ता;))

इसका क्या अर्थ है? या तो इससे कोई फर्क नहीं पड़ता, इससे कोई फर्क नहीं पड़ता, मेरा विश्लेषण गलत है ;-)

आम तौर पर, मुझे लगता है कि यह एक अनुकूलन है जो केवल कुछ ही मामलों में मूल्यवान है, और किया जा सकता है ज्यादातर परिदृश्यों में आसानी से हाथ से।

+0

पीटर, आप अपने कोड स्निपेट को संकलित करने के लिए किस झंडे का उपयोग करते थे? – Michael

+0

कोई डीबग जानकारी, हमेशा इनलाइन (/ ओबी 2),/ऑक्स/ओएस,/ओट या बाद वाले दो में से कोई भी नहीं। (मैंने केवल कंपाइलर आउटपुट को देखा है, लेकिन/जीएल को इससे प्रभावित नहीं होना चाहिए) – peterchen

1

जैसा कि कई ने कहा है: यह निर्भर करता है।

यदि आप सुनिश्चित करना चाहते हैं, तो आपको संकलन-समय निर्णय लागू करने का प्रयास करना चाहिए। टेम्पलेट्स अक्सर इस के लिए काम में आते हैं:

for (condition) 
    do_it<flag>(); 
17

साथ की कोशिश की जीसीसी और -O3:

void foo(); 
void bar(); 

int main() 
{ 
    bool doesnt_change = true; 
    for (int i = 0; i != 3; ++i) { 
     if (doesnt_change) { 
      foo(); 
     } 
     else { 
      bar(); 
     } 
    } 
} 

मुख्य के लिए परिणाम:

_main: 
pushl %ebp 
movl %esp, %ebp 
andl $-16, %esp 
call ___main 
call __Z3foov 
call __Z3foov 
call __Z3foov 
xorl %eax, %eax 
leave 
ret 

तो यह पसंद दूर का अनुकूलन करता है (और unrolls छोटे loops)।

यह अनुकूलन नहीं किया जाता है यदि doesnt_change वैश्विक है।

+2

+1: परीक्षण करने और जेनरेट कोड दिखाने के लिए। – Clifford

+3

क्या आप नीचे इस्तेमाल किए गए स्निपेट को आजमा सकते हैं? आपके स्निपेट में कंपाइलर बस दूसरी शाखा को हटा रहा है क्योंकि यह कभी निष्पादित नहीं होगा। (चाल एक बाहरी अस्थिर से 'doesnt_change' प्रारंभ करना है, इसलिए संकलक यह निर्धारित नहीं कर सकता कि उसके पास कौन सा मूल्य होगा) – peterchen

संबंधित मुद्दे