2016-09-13 11 views
9

में लूप अनोलिंग व्यवहार यह प्रश्न GCC 5.1 Loop unrolling पर एक अनुवर्ती प्रश्न है।जीसीसी

GCC documentation के अनुसार

, और इसके बाद के संस्करण सवाल का मेरा उत्तर में कहा गया है, झंडे ऐसे पर -funroll-loops बारी "पूरा पाश छीलने (पुनरावृत्तियों के एक छोटे से लगातार संख्या के साथ छोरों की अर्थात पूरी तरह से निकाला)" के रूप में। इसलिए, जब ऐसा ध्वज सक्षम होता है, तो संकलक एक लूप को अनलोल करना चुन सकता है यदि यह निर्धारित करता है कि यह कोड के दिए गए टुकड़े के निष्पादन को अनुकूलित करेगा।

फिर भी, मैंने अपनी परियोजनाओं में से एक में देखा कि जीसीसी कभी-कभी को अनलॉक कर देगा, भले ही संबंधित झंडे सक्षम नहीं थे। उदाहरण के लिए, कोड की निम्न सरल टुकड़ा पर विचार करें:

int main(int argc, char **argv) 
{ 
    int k = 0; 
    for(k = 0; k < 5; ++k) 
    { 
    volatile int temp = k; 
    } 
} 

जब -O1 साथ संकलन, पाश unrolled है और निम्नलिखित विधानसभा कोड जीसीसी के किसी भी आधुनिक संस्करण के साथ उत्पन्न होता है:

main: 
     movl $0, -4(%rsp) 
     movl $1, -4(%rsp) 
     movl $2, -4(%rsp) 
     movl $3, -4(%rsp) 
     movl $4, -4(%rsp) 
     movl $0, %eax 
     ret 

यहां तक ​​कि जब यह सुनिश्चित करने के लिए अतिरिक्त -fno-unroll-loops -fno-peel-loops के साथ संकलन अक्षम, जीसीसी अप्रत्याशित रूप से ऊपर वर्णित उदाहरण पर लूप अनोलिंग करता है।

यह अवलोकन मुझे निम्नलिखित निकट से संबंधित प्रश्नों की ओर ले जाता है। जीसीसी इस व्यवहार से संबंधित झंडे अक्षम होने के बावजूद लूप अनोलिंग क्यों करता है? अनलॉकिंग अन्य झंडे द्वारा नियंत्रित भी है जो कुछ मामलों में संकलक को लूप को अनलॉक कर सकता है भले ही -funroll-loops अक्षम है? क्या GCC में लूप अनोलिंग को पूरी तरह अक्षम करने का कोई तरीका है (-O0 के साथ संकलित करने का एक हिस्सा)?

दिलचस्प बात यह है बजना संकलक यहां अपेक्षित व्यवहार है, और केवल unrolling प्रदर्शन करने के लिए जब -funroll-loops सक्षम किया गया है, और अन्य मामलों में नहीं लगता है।

अग्रिम धन्यवाद, इस मामले पर किसी भी अतिरिक्त अंतर्दृष्टि की सराहना की जाएगी!

+0

बधाई। आपने पाया है कि अलग-अलग कंपाइलर व्यवहार में भिन्न होते हैं और आप जिन झंडे को पास करते हैं, उनका हमेशा यह अर्थ नहीं होता कि आप क्या सोच सकते हैं। असल दुनिया में आपका स्वागत है। –

+0

क्या यह आपके प्रोग्राम की कार्यक्षमता को तोड़ता है? – Serge

+0

नहीं, यह कार्यक्षमता को तोड़ता नहीं है। यह सामान्य ब्याज का सवाल है कि कैसे जीसीसी लूप अनोलिंग करता है और इस व्यवहार को कैसे ट्यून करता है। – Pyves

उत्तर

7

जीसीसी क्यों अनलॉकिंग करता है भले ही इस व्यवहार से संबंधित ध्वज अक्षम हैं?

इसे व्यावहारिक दृश्य से सोचें: संकलक को ऐसे ध्वज को पारित करते समय आप क्या चाहते हैं? कोई सी ++ डेवलपर जीसीसी को अनलोल करने या अनलोल लूप नहीं करने के लिए कहेंगे, सिर्फ लूप होने या असेंबली कोड में नहीं, एक लक्ष्य है। -fno-unroll-loops के साथ लक्ष्य, उदाहरण के लिए, अपने बाइनरी के आकार को कम करने के लिए थोड़ा सा बलिदान देना है, यदि आप सीमित स्टोरेज वाले एम्बेडेड सॉफ़्टवेयर विकसित कर रहे हैं। दूसरी ओर, -funrool-loops के साथ लक्ष्य संकलक को बताना है कि आपको बाइनरी के आकार की परवाह नहीं है, इसलिए इसे लूप को अनलॉक करने में संकोच नहीं करना चाहिए।

लेकिन इसका मतलब यह नहीं है कि संकलक अंधेरे अनलॉक या आपके सभी लूप नहीं होगा!

अपने उदाहरण में, वजह साफ है: पाश केवल एक अनुदेश शामिल हैं - किसी भी प्लेटफॉर्म पर कुछ बाइट्स - और संकलक जानता है कि इस negligeable है और वैसे भी के लिए आवश्यक विधानसभा कोड के रूप में लगभग एक ही आकार ले जाएगा लूप (sub + mov + jne x86-64 पर)।

यही कारण है कि जीसीसी 6.2 है, -O3 -fno-unroll-loops इस कोड को बदल जाता है के साथ:

int mul(int k, int j) 
{ 
    for (int i = 0; i < 5; ++i) 
    volatile int k = j; 

    return k; 
} 

... निम्नलिखित विधानसभा कोड के लिए:

mul(int, int): 
    mov DWORD PTR [rsp-0x4],esi 
    mov eax,edi 
    mov DWORD PTR [rsp-0x4],esi 
    mov DWORD PTR [rsp-0x4],esi 
    mov DWORD PTR [rsp-0x4],esi 
    mov DWORD PTR [rsp-0x4],esi 
    ret  

यह आप पर नहीं सुनता क्योंकि यह होगा (लगभग , वास्तुकला के आधार पर) बाइनरी के आकार को नहीं बदलता है लेकिन यह तेज़ है। हालांकि, अगर आप थोड़ा अपने पाश काउंटर बढ़ाने ...

int mul(int k, int j) 
{ 
    for (int i = 0; i < 20; ++i) 
    volatile int k = j; 

    return k; 
} 

... यह अपने संकेत इस प्रकार है:

mul(int, int): 
    mov eax,edi 
    mov edx,0x14 
    nop WORD PTR [rax+rax*1+0x0] 
    sub edx,0x1 
    mov DWORD PTR [rsp-0x4],esi 
    jne 400520 <mul(int, int)+0x10> 
    repz ret 

अगर आप 5 पर अपने पाश काउंटर रखने आप एक ही व्यवहार मिल जाएगा लेकिन आप लूप में कुछ कोड जोड़ते हैं।

समेकित करने के लिए, संकलक के लिए संकेत के रूप में इन सभी अनुकूलन झंडे के बारे में सोचें, और एक व्यावहारिक डेवलपर बिंदु दृश्य से। यह हमेशा एक व्यापार बंद होता है, और जब आप एक सॉफ्टवेयर बनाते हैं, तो आप कभीसभी या नहीं लूप अनोलिंग के लिए पूछना चाहते हैं।

अंतिम नोट के रूप में, एक और बहुत ही समान उदाहरण -f(no-)inline-functions ध्वज है। मैं हर दिन कंपाइलर को अपने कुछ कार्यों (inline कीवर्ड और __attribute__ ((noinline)) जीसीसी के साथ) इनलाइन (या नहीं!) से लड़ रहा हूं, और जब मैं असेंबली कोड की जांच करता हूं, तो मुझे लगता है कि यह स्मार्टस अभी भी कभी-कभी जो चाहती है वह कर रहा है, जब मैं एक ऐसे फ़ंक्शन को रेखांकित करना चाहता हूं जो इसके स्वाद के लिए निश्चित रूप से बहुत लंबा हो। और ज्यादातर समय, यह करना सही बात है और मैं खुश हूं!

+0

कम से कम कंपाइलर्स * करते हैं * आमतौर पर '__attribute__ (((नहीं) इनलाइन) ('' और तेज़/सख्त गणित जैसी चीजें सुनते हैं। मैं कल्पना नहीं कर सकता कि एक सख्त-गणित ध्वज को अनदेखा करने के आसपास एक कंपाइलर जा रहा है। – Mysticial