2015-03-21 6 views
5

इस कोड है कि मैं परीक्षण किया है:जीसीसी इस रिकर्सिव फाइबोनैकी कोड में क्लैंग की तुलना में एक तेज प्रोग्राम क्यों उत्पन्न करता है?

#include <iostream> 
#include <chrono> 
using namespace std; 

#define CHRONO_NOW     chrono::high_resolution_clock::now() 
#define CHRONO_DURATION(first,last) chrono::duration_cast<chrono::duration<double>>(last-first).count() 

int fib(int n) { 
    if (n<2) return n; 
    return fib(n-1) + fib(n-2); 
} 

int main() { 
    auto t0 = CHRONO_NOW; 
    cout << fib(45) << endl; 
    cout << CHRONO_DURATION(t0, CHRONO_NOW) << endl; 
    return 0; 
} 
बेशक

, वहाँ फिबोनैकी संख्या की गणना के बहुत तेजी से तरीके हैं, लेकिन यह एक अच्छा थोड़ा तनाव परीक्षण है कि पुनरावर्ती क्रिया कॉल पर केंद्रित है। समय मापने के लिए क्रोोनो के उपयोग के अलावा, कोड के लिए कुछ और नहीं है।

सबसे पहले मैंने ओओ एक्स पर एक्सकोड में दो बार परीक्षण किया (इसलिए यह क्लैंग) है, ओओ 3 अनुकूलन का उपयोग कर। इसे चलाने में लगभग 9 सेकंड लग गए।

फिर, मैंने उबंटू (जीओ ++) पर जीसीसी (जी ++) के साथ एक ही कोड संकलित किया, और उस संस्करण को चलाने के लिए केवल 6.3 सेकंड लग गए! इसके अलावा, मैं अपने मैक पर वर्चुअलबॉक्स के अंदर उबंटू चला रहा था, जो केवल प्रदर्शन को नकारात्मक रूप से प्रभावित कर सकता है, अगर बिलकुल भी।

तो वहाँ

तुम जाओ: ओएस पर

बजना एक्स -> ~ 9 उबंटू पर सेकेंड

जीसीसी VirtualBox में -> ~ 6.3 सेकेंड।

मुझे पता है कि ये पूरी तरह से अलग कंपाइलर हैं इसलिए वे अलग-अलग सामान करते हैं, लेकिन जीसीसी और क्लैंग की विशेषता वाले सभी परीक्षणों में केवल एक अंतर दिखाई देता है, और कुछ मामलों में, अंतर एक और तरीका था (झुकाव तेजी से हो रहा है)।

तो क्या कोई तार्किक स्पष्टीकरण है कि जीसीसी इस विशेष उदाहरण में मील से क्यों घिरा हुआ है?

+0

पर कॉल करने के लिए 20 से अधिक लेबल वाले केवल एक ही कॉल है, क्या आपने असेंबली आउटपुट की जांच की है? और क्लैंग और जीसीसी का संस्करण क्या है? –

+0

इन परिभाषाओं का उपयोग न करें।'उपयोग' निर्देश का यही अर्थ है: 'chrono_time_point = chrono :: high_resolution_clock :: time_point; ' – Cubic

+2

उत्पन्न कोड को देखें, यह एक साधारण कार्य है –

उत्तर

3

मैं यह नहीं कहूंगा कि जीसीसी मील से घिरा हुआ है। मेरी राय में, प्रदर्शन अंतर (6.3 सेकंड बनाम 9 सेकंड) बल्कि छोटा है। मेरे फ्रीबीएसडी सिस्टम पर, क्लैंग को 26.12 सेकंड की आवश्यकता होती है और जीसीसी को 10.55 सेकेंड की आवश्यकता होती है।

हालांकि, इसे डीबग करने का तरीका असेंबली आउटपुट प्राप्त करने के लिए g ++ -S और clang ++ -S का उपयोग करना है।

मैंने इसे अपने फ्रीबीएसडी सिस्टम पर परीक्षण किया। असेंबली भाषा फाइलें यहां पोस्ट करने में बहुत लंबी हैं, लेकिन ऐसा लगता है कि जीसीसी फिबोनाकी गणना समारोह में इनलाइनिंग के कई स्तरों को निष्पादित करता है (वहां 20 फाइब() कॉल थे!) जबकि क्लैंग बस फाइब (एन -1) और फाइब को कॉल करता है (एन -2) इनलाइनिंग के स्तर के साथ।

वैसे, मेरा जीसीसी संस्करण 4.2.1 20070831 पैच किया गया था [फ्रीबीएसडी] और क्लैंग संस्करण 3.1 (शाखाएं/रिलीज_31 156863) 20120523 था। ये वे संस्करण थे जो फ्रीबीएसडी 9.1-रिलीज बेस सिस्टम के साथ आते हैं। सीपीयू एएमडी ट्यूरियन II नियो एन 40 एल ड्यूल-कोर प्रोसेसर (1497.54-मेगाहर्ट्ज) है।

+0

तो मूल रूप से यह इनलाइनिंग के लिए नीचे आता है , कम से कम "गहराई" की एक निश्चित राशि के लिए। दिलचस्प ... – adam10603

+7

एक कारक 1.4 छोटा नहीं है, यह बहुत बड़ा है। – harold

6

मैं जीसीसी उपलब्ध लेकिन जीसीसी 4.9.2 gcc godbolt में वास्तव में एक बहुत फ़ंक्शन कॉल unrolls की जरूरत नहीं है, जबकि बजना कॉल मिथ्या दो बार नीचे

fibonacci(int):       # @fibonacci(int) 
    push rbp 
    push rbx 
    push rax 
    mov ebx, edi 
    cmp ebx, 2 
    jge .LBB0_1 
    mov eax, ebx 
    jmp .LBB0_3 
.LBB0_1: 
    lea edi, dword ptr [rbx - 1] 
    call fibonacci(int) 
    mov ebp, eax 
    add ebx, -2 
    mov edi, ebx 
    call fibonacci(int) 
    add eax, ebp 
.LBB0_3: 
    add rsp, 8 
    pop rbx 
    pop rbp 
    ret 

http://goo.gl/SFPWsr

की तरह भी पूंछ कॉल अनुकूलन के बिना प्रत्येक यात्रा

जीसीसी संस्करण में कॉल

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