क्या एक पुनरावर्ती कार्य इनलाइन हो सकता है?


134
inline int factorial(int n)
{
    if(!n) return 1;
    else return n*factorial(n-1);
}

जैसा कि मैंने पढ़ रहा था इस में पाया गया कि इसके बाद के संस्करण कोड "अनंत संकलन" करने के लिए नेतृत्व करेंगे अगर सही ढंग से संकलक द्वारा नियंत्रित नहीं।

कंपाइलर कैसे तय करता है कि फंक्शन को इनलाइन करें या नहीं?

जवाबों:


137

सबसे पहले, inlineएक फ़ंक्शन पर विनिर्देश केवल एक संकेत है। कंपाइलर (और अक्सर करता है) एक inlineक्वालीफायर की उपस्थिति या अनुपस्थिति को पूरी तरह से अनदेखा कर सकता है । उस के साथ, एक संकलक एक पुनरावर्ती कार्य को इनलाइन कर सकता है , जितना कि यह एक अनंत लूप को नियंत्रित कर सकता है। इसे बस उस स्तर पर एक सीमा रखनी है, जिस पर यह फ़ंक्शन को "अनियंत्रित" करेगा।

एक अनुकूलन कंपाइलर इस कोड को चालू कर सकता है:

inline int factorial(int n)
{
    if (n <= 1)
    {
        return 1;
    }
    else
    {
        return n * factorial(n - 1);
    }
}

int f(int x)
{
    return factorial(x);
}

इस कोड में:

int factorial(int n)
{
    if (n <= 1)
    {
        return 1;
    }
    else
    {
        return n * factorial(n - 1);
    }
}

int f(int x)
{
    if (x <= 1)
    {
        return 1;
    }
    else
    {
        int x2 = x - 1;
        if (x2 <= 1)
        {
            return x * 1;
        }
        else
        {
            int x3 = x2 - 1;
            if (x3 <= 1)
            {
                return x * x2 * 1;
            }
            else
            {
                return x * x2 * x3 * factorial(x3 - 1);
            }
        }
    }
}

इस मामले में, हमने मूल रूप से फ़ंक्शन को 3 बार इनलाइन किया है। कुछ कंपाइलर इस अनुकूलन को करते हैं। मुझे याद है कि MSVC ++ आवर्ती कार्यों (20 तक, मुझे विश्वास है) पर होने वाले इनलाइनिंग के स्तर को ट्यून करने के लिए एक सेटिंग है।


20
यह #pragma inline_recursion (ऑन) है। अधिकतम गहराई के बारे में प्रलेखन सुसंगत या अनिर्णायक नहीं है। मान # 8, 16 या #pragma inline_depth का मान संभव है।
पीटरचेन

@peterchen यदि फ़ंक्शन इनलाइन अपने तर्कों के एक मूल्य को बदल रहा है तो क्या होता है, मुझे लगता है कि मुख्य के बजाय तथ्य के अंदर फ़ंक्शन को इनलाइन करना बेहतर है। मेरी अंग्रेजी के लिए खेद है
ob_dev

1
@obounaim: आप सोच सकते हैं कि। MSVC नहीं करता है।
सिक्योरिटीमैट

23

वास्तव में, यदि आपका कंपाइलर समझदारी से काम नहीं लेता है, तो यह आपके inlined फंक्शन की कॉपियों को आवर्ती रूप से डालने की कोशिश कर सकता है , जिससे असीम रूप से बड़े कोड का निर्माण किया जा सकता है। हालांकि अधिकांश आधुनिक कंपाइलर इसे पहचान लेंगे। वे या तो कर सकते हैं:

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

केस 2 के लिए, कई कंपाइलर्स #pragmaआपके द्वारा निर्धारित अधिकतम गहराई को निर्दिष्ट करने के लिए सेट हो सकते हैं, जो यह किया जाना चाहिए। में जीसीसी , आप भी इस में कमांड लाइन से गुजरती साथ कर सकते हैं --max-inline-insns-recursive(अधिक जानकारी देखने के लिए यहाँ )।


7

यदि संभव हो तो AFAIK GCC पुनरावर्ती कार्यों पर पूंछ कॉल उन्मूलन करेगा। आपका कार्य हालांकि पुनरावर्ती नहीं है।


6

संकलक एक कॉल ग्राफ बनाता है; जब एक चक्र खुद को बुलाते हुए पाया जाता है, तो फ़ंक्शन एक निश्चित गहराई (एन = 1, 10, 100, जो भी संकलक को ट्यून किया जाता है) के बाद इनलाइन नहीं होता है।


3

कुछ पुनरावर्ती कार्यों को छोरों में तब्दील किया जा सकता है, जो प्रभावी रूप से असीम रूप से इनलाइन करता है। मेरा मानना ​​है कि जीसीसी यह कर सकता है, लेकिन मुझे अन्य संकलक के बारे में पता नहीं है।


2

आमतौर पर काम नहीं करेगा, इसके लिए पहले से दिए गए उत्तर देखें।

एक "फुटनोट" के रूप में, आप उस प्रभाव को प्राप्त कर सकते हैं जो आप देख रहे हैं (कम से कम इस तथ्य के लिए कि आप एक उदाहरण के रूप में उपयोग कर रहे हैं) टेम्पलेट मेटाप्रोग्रामिंग का उपयोग कर सकते हैं । विकिपीडिया से चिपकाना:

template <int N>
struct Factorial 
{
    enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0> 
{
    enum { value = 1 };
};

1
यह बहुत प्यारा है, लेकिन कृपया ध्यान दें कि मूल पोस्टिंग में एक चर तर्क "int n" था।
विंडोज प्रोग्रामर 5

1
सच है, लेकिन "पुनरावर्ती इनलाइनिंग" चाहने में बहुत कम बिंदु है जब संकलन समय पर n ज्ञात नहीं है ... तो संकलक कभी भी कैसे प्राप्त कर सकता है? इसलिए सवाल के संदर्भ में मुझे लगता है कि यह एक प्रासंगिक विकल्प है।
युंगचिन

1
डेरेक पार्क का उदाहरण देखें कि यह कैसे करना है: दो बार inlining से, आप n >> 2 बार पुनरावृत्ति करते हैं और आपके पास परिणामी कोड से 2 + 2 रिटर्न होते हैं।
MSALERS

1

कंपाइलर इन प्रकार की चीजों का पता लगाने और उन्हें रोकने के लिए एक कॉल ग्राफ बनाएगा। इसलिए यह देखा जाएगा कि फ़ंक्शन स्वयं को कॉल करता है और इनलाइन नहीं।

लेकिन मुख्य रूप से यह इनलाइन कीवर्ड और कंपाइलर स्विच द्वारा नियंत्रित किया जाता है (उदाहरण के लिए, आप इसे बिना कीवर्ड के ऑटो इनलाइन छोटे कार्य कर सकते हैं।) यह ध्यान रखना महत्वपूर्ण है कि डिबग संकलन कभी भी इनलाइन नहीं होने चाहिए क्योंकि कॉलस्टैक को मिरर में संरक्षित नहीं किया जाएगा। आपके द्वारा कोड में बनाई गई कॉल।


1

"कंपाइलर कैसे तय करता है कि फंक्शन को इनलाइन करें या नहीं?"

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

कार्यक्रम के स्रोत कोड को अभी भी इनलाइन कार्यों के लिए नियमों का पालन करना है। फ़ंक्शन को इनलाइन किया जाए या नहीं, आपको इस संभावना के लिए तैयार करना होगा कि यह इनलेट (कुछ अज्ञात संख्या में) होगा।

विकिपीडिया स्टेटमेंट जो पुनरावर्ती मैक्रों को आमतौर पर अवैध रूप से सूचित करता है, बल्कि खराब लगता है। C और C ++ पुनरावर्ती आक्रमणों को रोकते हैं, लेकिन एक अनुवाद इकाई मैक्रो कोड युक्त अवैध नहीं बनती है जो दिखता है कि यह पुनरावर्ती होगा। कोडांतरकों में, पुनरावर्ती मैक्रोज़ आमतौर पर कानूनी हैं।


0

कुछ संकलक (Ie बोरलैंड C ++) इनलाइन कोड नहीं करते हैं जिसमें सशर्त बयान (यदि, मामला, जबकि आदि ..) शामिल हैं, तो आपके उदाहरण में पुनरावर्ती फ़ंक्शन इनलेट नहीं होगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.