2011-08-17 8 views
9

के रूप में, कहते हैं कि मेरी हेडर फ़ाइल है:आधुनिक सी ++ कंपाइलर्स इनलाइन फ़ंक्शन करें जिन्हें बिल्कुल एक बार कहा जाता है?

class A 
{ 
    void Complicated(); 
} 

और मेरे स्रोत फ़ाइल

void A::Complicated() 
{ 
    ...really long function... 
} 

मैं पूरी तरह से पढ़ने के लिए और बिना किसी डर के

void DoInitialStuff(pass necessary vars by ref or value) 
{ 
    ... 
} 
void HandleCaseA(pass necessary vars by ref or value) 
{ 
    ... 
} 
void HandleCaseB(pass necessary vars by ref or value) 
{ 
    ... 
} 
void FinishUp(pass necessary vars by ref or value) 
{ 
    ... 
} 
void A::Complicated() 
{ 
    ... 
    DoInitialStuff(...); 
    switch ... 
     HandleCaseA(...) 
     HandleCaseB(...) 
    ... 
    FinishUp(...) 
} 

में स्रोत फ़ाइल विभाजित कर सकते हैं प्रदर्शन के मामले में प्रभाव का?

+1

शायद, शायद नहीं। आप जिस कंपाइलर का उपयोग कर रहे हैं उस पर निर्भर करते हुए कंपाइलर प्रोग्रामर आपकी सबसे अच्छी शर्त हो सकता है। – DumbCoder

+0

मुझे आश्चर्य है कि इनलाइनिंग का बिंदु क्या होगा ... कॉल काफी सस्ते हैं। –

+2

इनमें से कोई भी लूप में नहीं होता है? वास्तव में आप कुछ फ़ंक्शन कॉल के ओवरहेड से बचने से कितना समय प्राप्त करने की उम्मीद करते हैं? एक नैनोसेकंद? – UncleBens

उत्तर

10

आपको static फ़ंक्शंस को चिह्नित करना चाहिए ताकि संकलक को पता हो कि वे उस अनुवाद इकाई के लिए स्थानीय हैं।

static के बिना संकलक (एलटीओ/डब्ल्यूपीए को छोड़कर) नहीं मान सकता है कि फ़ंक्शन को केवल एक बार बुलाया जाता है, इसलिए इसे इनलाइन करने की संभावना कम होती है।

LLVM Try Out पृष्ठ का उपयोग करके प्रदर्शन।

जिसके अनुसार, पठनीयता के लिए कोड पहले, सूक्ष्म अनुकूलन केवल प्रदर्शन उपायों के बाद आना चाहिए (और इस तरह फेरबदल एक सूक्ष्म अनुकूलन है)।


उदाहरण:

#include <cstdio> 

static void foo(int i) { 
    int m = i % 3; 
    printf("%d %d", i, m); 
} 

int main(int argc, char* argv[]) { 
    for (int i = 0; i != argc; ++i) { 
    foo(i); 
    } 
} 

static साथ उत्पादन:

; ModuleID = '/tmp/webcompile/_27689_0.bc' 
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" 
target triple = "x86_64-unknown-linux-gnu" 

@.str = private constant [6 x i8] c"%d %d\00"  ; <[6 x i8]*> [#uses=1] 

define i32 @main(i32 %argc, i8** nocapture %argv) nounwind { 
entry: 
    %cmp4 = icmp eq i32 %argc, 0     ; <i1> [#uses=1] 
    br i1 %cmp4, label %for.end, label %for.body 

for.body:           ; preds = %for.body, %entry 
    %0 = phi i32 [ %inc, %for.body ], [ 0, %entry ] ; <i32> [#uses=3] 
    %rem.i = srem i32 %0, 3       ; <i32> [#uses=1] 
    %call.i = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str, i64 0, i64 0), i32 %0, i32 %rem.i) nounwind ; <i32> [#uses=0] 
    %inc = add nsw i32 %0, 1      ; <i32> [#uses=2] 
    %exitcond = icmp eq i32 %inc, %argc    ; <i1> [#uses=1] 
    br i1 %exitcond, label %for.end, label %for.body 

for.end:           ; preds = %for.body, %entry 
    ret i32 0 
} 

declare i32 @printf(i8* nocapture, ...) nounwind 

static के बिना:

; ModuleID = '/tmp/webcompile/_27859_0.bc' 
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" 
target triple = "x86_64-unknown-linux-gnu" 

@.str = private constant [6 x i8] c"%d %d\00"  ; <[6 x i8]*> [#uses=1] 

define void @foo(int)(i32 %i) nounwind { 
entry: 
    %rem = srem i32 %i, 3       ; <i32> [#uses=1] 
    %call = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str, i64 0, i64 0), i32 %i, i32 %rem) ; <i32> [#uses=0] 
    ret void 
} 

declare i32 @printf(i8* nocapture, ...) nounwind 

define i32 @main(i32 %argc, i8** nocapture %argv) nounwind { 
entry: 
    %cmp4 = icmp eq i32 %argc, 0     ; <i1> [#uses=1] 
    br i1 %cmp4, label %for.end, label %for.body 

for.body:           ; preds = %for.body, %entry 
    %0 = phi i32 [ %inc, %for.body ], [ 0, %entry ] ; <i32> [#uses=3] 
    %rem.i = srem i32 %0, 3       ; <i32> [#uses=1] 
    %call.i = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str, i64 0, i64 0), i32 %0, i32 %rem.i) nounwind ; <i32> [#uses=0] 
    %inc = add nsw i32 %0, 1      ; <i32> [#uses=2] 
    %exitcond = icmp eq i32 %inc, %argc    ; <i1> [#uses=1] 
    br i1 %exitcond, label %for.end, label %for.body 

for.end:           ; preds = %for.body, %entry 
    ret i32 0 
} 
+3

मुझे लगता है कि आपको "सिद्धांत" के अनुसार, उन्हें एक अनाम नामस्थान में रखना चाहिए। – GManNickG

+0

क्लैंग ने इसे आपके उदाहरण में स्थिर और गैर-संस्करण दोनों संस्करणों में रेखांकित किया है ... मुझे नहीं लगता कि कॉल में इनलाइनों में कोई महत्वपूर्ण अंतर है या नहीं। फ़ंक्शन का कोड उत्सर्जित होगा या नहीं, इसमें कोई अंतर है। –

+1

@GMan: मैं 'स्थैतिक' का पक्ष लेता हूं क्योंकि मुझे यह मानव के लिए अधिक पठनीय लगता है, एक अनाम नामस्थान के लिए मनुष्य को याद रखना चाहिए कि वह एक में है, और यह स्पष्ट नहीं है। 'स्थिर 'को इस" संदर्भ "की आवश्यकता नहीं है। –

7

एलियासिंग (उस फ़ंक्शन पर पॉइंटर्स) और फ़ंक्शन लम्बाई पर निर्भर करता है (शाखा में उल्लिखित एक बड़ा फ़ंक्शन अन्य शाखा को कैश से बाहर फेंक सकता है, जिससे प्रदर्शन को नुकसान पहुंचा सकता है)।

उस के बारे में चिंता करने दें संकलक, आप अपने कोड :) के बारे में चिंता

+1

क्या एक कंपाइलर भी लंबे कार्यों में छोटे कार्यों को विभाजित कर सकता है? – Cookie

+0

ओपी उपर्युक्त कोड लिखने और मैन्युअल रूप से सभी कोड इनलाइन लिखने के बीच तुलना के बारे में पूछ रहा है। तो मुझे लगता है कि वह एक जवाब चाहता है जो बताता है कि एक समारोह को तोड़ने से कभी नकारात्मक हो जाएगा। –

+0

यह थोड़ा कम लगता है, लाभ क्या होगा? मुझे लगता है कि आप संकलक को अपने ब्लॉक को दूसरे के बाद एक से अधिक की प्रतिलिपि बनाने की अधिक संभावना है :) – Blindy

6

एक जटिल समारोह इसकी गति समारोह के भीतर संचालन का प्रभुत्व है की संभावना है; फंक्शन कॉल का ओवरहेड ध्यान देने योग्य नहीं होगा, भले ही यह रेखांकित न हो।

आपके पास किसी फ़ंक्शन की इनलाइनिंग पर अधिक नियंत्रण नहीं है, तो इसका पता लगाने का सबसे अच्छा तरीका यह है कि इसे आजमाएं और पता लगाएं।

एक कंपाइलर का अनुकूलक कोड के छोटे टुकड़ों के साथ अधिक प्रभावी हो सकता है, इसलिए यदि आप रेखांकित नहीं हैं तो भी आपको तेज़ी से मिल रहा है।

+0

और एक साधारण कार्य के बारे में क्या? –

+0

@ ओली, सरल कार्यों को स्वचालित रूप से किसी भी तरह से रेखांकित होने की अधिक संभावना है। –

+0

ज्यादा नियंत्रण नहीं है? यही कारण है कि एक "इनलाइन" कीवर्ड है ... सरल कार्यों को तब तक रेखांकित नहीं किया जा सकता जब तक उन्हें स्थिर घोषित न किया जाए। या अधिक विशेष रूप से गैर-इनलाइन संस्करण बाहरी कॉलर्स के लिए उपलब्ध होना चाहिए। इसके अलावा, यदि 15 बड़े लोगों (जैसे ओपी करना चाहता है) में एक बड़ा फ़ंक्शन टूटा हुआ है, तो यह 15 ओवर कॉल ओवरहेड होगा। – phkahler

0

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

+0

यदि फ़ंक्शन को केवल एक बार बुलाया जाता है, तो मुझे यकीन नहीं है कि कोड को इनलाइन नहीं करके हम एक गति देखेंगे ... * जब तक कि आप वास्तव में फ़ंक्शन कॉल को छोड़ना नहीं चाहते हैं और फ़ंक्शन को रेखांकित करना चाहते हैं इसलिए आपके निर्देश कैश को मिटा दें। क्या यह वर्तमान है? –

+0

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

0

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

इस बिंदु पर अनुकूलन पर ध्यान केंद्रित न करें, सुनिश्चित करें कि आपका कोड पठनीय है और आप 99% समय ठीक होंगे।

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