2016-04-11 24 views
8

के बीच अंतर मैंने विभिन्न परियोजनाओं में इन दोनों कंपाइलरों का उपयोग किया।क्लैंग और जीसीसी

कोड प्रसंस्करण और आउटपुट पीढ़ियों के मामले में वे अलग कैसे हैं? उदाहरण के लिए gcc और clang दोनों ऑप्टिमाइज़ेशन के लिए -O2 विकल्प हैं। क्या वे अनुकूलन कोड के मामले में एक ही तरीके (उच्च स्तर) का संचालन कर रहे हैं? मैं एक छोटे से परीक्षण किया, उदाहरण के लिए अगर मैं निम्नलिखित कोड है:

int foo(int num) { 
    if(num % 2 == 1) 
     return num * num; 
    else 
     return num * num +1; 
} 

निम्नलिखित -O2 साथ बजना और जीसीसी के साथ उत्पादन विधानसभाओं हैं:

----gcc 5.3.0-----        ----clang 3.8.0---- 
foo(int):          foo(int): 
     movl %edi, %edx        movl %edi, %eax 
     shrl $31, %edx        shrl $31, %eax 
     leal (%rdi,%rdx), %eax      addl %edi, %eax 
     andl $1, %eax        andl $-2, %eax 
     subl %edx, %eax        movl %edi, %ecx 
     cmpl $1, %eax        subl %eax, %ecx 
     je  .L5          imull %edi, %edi 
     imull %edi, %edi        cmpl $1, %ecx 
     leal 1(%rdi), %eax       setne %al 
     ret            movzbl %al, %eax 
.L5:             addl %edi, %eax 
     movl %edi, %eax        retq 
     imull %edi, %eax 
     ret 

के रूप में यह उत्पादन देखा जा सकता है अलग-अलग निर्देश हैं। तो मेरा सवाल यह है कि उनमें से एक को अलग-अलग परियोजनाओं में एक दूसरे का फायदा होता है?

+2

आप 'int foo (int num) {return num * num + ~ num & 1;}' लिखकर अपना कोड सुधार सकते हैं। – fuz

+1

@FUZxxl: महान बिंदु, [[बेहतर कोड बनाता है] (https://godbolt.org/g/Y1RZuj), लेकिन आपको माता-पिता 'int foo (int num) की आवश्यकता है {वापसी संख्या * num + (~ num & 1);} 'क्योंकि' और 'की तुलना में कम प्राथमिकता * और + है। इसके अलावा, नकारात्मक संख्याओं के लिए इसका अलग-अलग व्यवहार होता है। सी में,' -1% 2' '-1' है, इसलिए यदि गलत है। gcc का कोड इसके लिए खाता है यदि आप 'n * n + (n% 2) लिखते हैं। –

+0

@FUZxxl नोट के लिए धन्यवाद, यह देखने के लिए एक साधारण परीक्षण है कि कौन से कंपाइलर्स आउटपुट – Pooya

उत्तर

12

हां। और नहीं।

यह पूछने की तरह है कि क्या ऑडी कार को मर्सिडीज कार पर लाभ होता है या नहीं। उनके जैसे, दो कंपाइलर एक ही चीज करने के उद्देश्य से दो अलग-अलग परियोजनाएं हैं। कुछ मामलों में, gcc बेहतर कोड उत्सर्जित करेगा, अन्य में यह clang होगा।

जब आपको पता होना चाहिए, तो आपको अपने कोड को दोनों के साथ संकलित करना होगा और फिर इसे मापना होगा।

एक तर्क here और कुछ हद तक कम here है।

10

इस मामले में क्लैंग आउटपुट बेहतर है, क्योंकि यह शाखा नहीं है; इसके बजाए यह num % 2 == 1 से al का मान लोड करता है जो जीसीसी द्वारा उत्पन्न कोड कूदता है। यदि num 50% संभावनाओं के साथ भी/विषम होने की उम्मीद है, और दोहराने वाले पैटर्न के साथ, जीसीसी द्वारा उत्पन्न कोड susceptible to branch prediction failure होगा।


हालांकि आप रूप में अच्छी तरह इससे भी अधिक ताकि

int foo(int num) { 
    return num * num + (num % 2 != 1); 
} 

करके कोड जीसीसी पर अच्छी तरह से व्यवहार कर सकते हैं, के रूप में ऐसा लगता है कि अपने एल्गोरिथ्म वास्तव में केवल अहस्ताक्षरित संख्या के लिए परिभाषित किया गया है, तो आप का उपयोग करना चाहिए unsigned int (वे ऋणात्मक संख्याओं के लिए अलग हैं) - वास्तव में आप के रूप में अब जीसीसी/बजना अनुकूलन कर सकते हैं, तर्क के लिए unsigned int का उपयोग करके एक प्रमुख speedup मिल num % 2num & 1 रहे हैं:

unsigned int foo(unsigned int num) { 
    return num * num + (num % 2 != 1); 
} 

जिसके परिणामस्वरूप कोड द्वारा gcc -O2

movl %edi, %edx 
imull %edi, %edi 
andl $1, %edx 
xorl $1, %edx 
leal (%rdi,%rdx), %eax 
ret 

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

+1

हम्म मैंने गलती की होगी, निश्चित रूप से '! = 1', या '== 0' होना चाहिए था –

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