फ़ंक्शन कॉल कितना प्रभाव प्रदर्शन करते हैं?


13

तरीकों या कार्यों में कार्यक्षमता निकालना कोड मॉड्युलैरिटी, पठनीयता और अंतर के लिए बहुत जरूरी है, विशेषकर ओओपी में।

लेकिन इसका मतलब है कि अधिक फ़ंक्शन कॉल किए जाएंगे।

हमारे कोड को विधियों या कार्यों में विभाजित करना वास्तव में आधुनिक * भाषाओं में प्रदर्शन को कैसे प्रभावित करता है?

* सबसे लोकप्रिय: सी, जावा, सी ++, सी #, पायथन, जावास्क्रिप्ट, रूबी ...



1
मेरे नमक के लायक हर भाषा का कार्यान्वयन कई दशकों से इनलाइनिंग कर रहा है, मुझे लगता है। IOW, ओवरहेड ठीक 0. है
जोर्ग डब्ल्यू मित्तग

1
"अधिक फ़ंक्शन कॉल किया जाएगा" अक्सर सही नहीं होता है क्योंकि उन कॉलों में से कई में उनके ओवरहेड को आपके कोड और इनलाइनिंग सामग्री को संसाधित करने वाले विभिन्न कंपाइलर / दुभाषियों द्वारा अनुकूलित किया जाएगा। यदि आपकी भाषा में इस प्रकार के अनुकूलन नहीं हैं, तो मैं इसे आधुनिक नहीं मान सकता।
Ixrec

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

1
प्रभाव, यदि कोई हो, इतना छोटा है कि आप, एक व्यक्ति, कभी भी इसे नोटिस नहीं करेंगे । चिंता करने के लिए अन्य महत्वपूर्ण चीजें हैं। जैसे कि टैब 5 या 7 रिक्त स्थान होना चाहिए।
मेटाफ़ाइट

जवाबों:


21

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

void DoSomething()
{
   a = a + 1;
   DoSomethingElse(a);
}

void DoSomethingElse(int a)
{
   b = a + 3;
}

कंपाइलर इनलाइन तय करता है DoSomethingElse, और कोड बन जाता है

void DoSomething()
{
   a = a + 1;
   b = a + 3;
}

जब फ़ंक्शन इनबिल्ड नहीं होते हैं, हाँ एक फंक्शन कॉल करने के लिए एक प्रदर्शन हिट होता है। हालाँकि, यह इतना छोटा है कि फंक्शन कॉल के बारे में चिंता करने के लिए केवल बेहद उच्च प्रदर्शन कोड की जरूरत है। और उन प्रकार की परियोजनाओं पर, कोड आमतौर पर विधानसभा में लिखा जाता है।

फ़ंक्शन कॉल (प्लेटफ़ॉर्म पर निर्भर करता है) में आमतौर पर निर्देशों के कुछ 10s शामिल होते हैं, और यह स्टैक को बचाने / बहाल करने सहित है। कुछ फ़ंक्शन कॉल में जंप और वापसी निर्देश शामिल होते हैं।

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

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


1
मैंने आधुनिक संकलकों (जीसीसी, क्लैंग) के साथ उन स्थितियों में देखा है जहां मुझे वास्तव में परवाह है कि उन्होंने एक बड़े फ़ंक्शन के अंदर लूप के लिए काफी बुरा कोड बनाया है । स्थैतिक कार्य में लूप को निकालने से इनलाइनिंग के कारण मदद नहीं मिली। कुछ मामलों में महत्वपूर्ण (बेंचमार्क में औसत दर्जे का) गति में सुधार में बनाए गए बाहरी फ़ंक्शन में लूप को निकालना।
gnasher729

1
मैं इस पर गुदगुदाता हूं और कहता हूं कि ओपी को समयपूर्व अनुकूलन के
पैट्रिक

1
@ पैट्रिक बिंगो। यदि आप ऑप्टिमाइज़ करने जा रहे हैं, तो यह देखने के लिए प्रोफाइलर का उपयोग करें कि धीमे सेक्शन कहाँ हैं। अनुमान मत करो। आप आमतौर पर यह महसूस कर सकते हैं कि धीमे वर्ग कहां हो सकते हैं, लेकिन इसकी पुष्टि किसी प्रोफाइलर से करें।
CHendrix

@ gnasher729 उस विशेष मुद्दे को हल करने के लिए, किसी व्यक्ति को एक प्रोफाइलर से अधिक की आवश्यकता होगी - किसी को असंतुष्ट मशीन कोड को भी पढ़ना सीखना होगा। जबकि समयपूर्व अनुकूलन है, समय से पहले सीखने (कम से कम सॉफ्टवेयर विकास) जैसी कोई चीज नहीं है ।
rwong

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

5

यह संकलक या रनटाइम (और इसके विकल्प) के कार्यान्वयन का मामला है और इसे किसी निश्चितता के साथ नहीं कहा जा सकता है।

C और C ++ के भीतर, कुछ कंपाइलर ऑप्टिमाइज़ेशन सेटिंग्स के आधार पर कॉल को इनलाइन करेंगे - यह https://gcc.godbolt.org/ जैसे टूल को देखते हुए उत्पन्न असेंबली की जांच करके तुच्छ रूप से देखा जा सकता है।

अन्य भाषाएं, जैसे कि जावा में रनटाइम का हिस्सा है। यह JIT का हिस्सा है और इस SO प्रश्न में विस्तृत है । हॉटस्पॉट के लिए जेवीएम विकल्पों पर विशेष रूप से देखें

-XX:InlineSmallCode=n पहले से संकलित पद्धति को केवल तभी इनलाइन किया जाता है जब उसका जनित कोड आकार इससे कम हो। डिफ़ॉल्ट मान उस प्लेटफ़ॉर्म के साथ बदलता रहता है जिस पर JVM चल रहा है।
-XX:MaxInlineSize=35 एक विधि का अधिकतम बाइटकोड आकार इनलाइन होना चाहिए।
-XX:FreqInlineSize=n इनलाइन होने के लिए अक्सर निष्पादित विधि का अधिकतम बाइटकोड आकार। डिफ़ॉल्ट मान उस प्लेटफ़ॉर्म के साथ बदलता रहता है जिस पर JVM चल रहा है।

तो हां, हॉटस्पॉट जेआईटी कंपाइलर कुछ मानदंडों को पूरा करने वाले तरीकों को इनलाइन करेगा।

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

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

भाषाएँ इनलाइन नहीं होती हैं। कार्यान्वयन हो सकता है


5

आप गलत जगह प्रदर्शन की तलाश कर रहे हैं। फ़ंक्शन कॉल के साथ समस्या यह नहीं है कि उनकी लागत बहुत अधिक है। एक और समस्या है। फ़ंक्शन कॉल बिल्कुल मुफ्त हो सकती हैं, और आपको अभी भी यह अन्य समस्या होगी।

यह है कि एक फ़ंक्शन क्रेडिट कार्ड की तरह है। चूंकि आप इसे आसानी से उपयोग कर सकते हैं, इसलिए आप इसे जितना संभव हो उतना अधिक उपयोग करना चाहते हैं। मान लीजिए कि आप इसे ज़रूरत से 20% अधिक कहते हैं। फिर, विशिष्ट बड़े सॉफ़्टवेयर में कई परतें होती हैं, प्रत्येक कॉलिंग नीचे की परत में होती है, इसलिए 1.2 का कारक परतों की संख्या से जटिल हो सकता है। (उदाहरण के लिए, यदि पांच परतें हैं, और प्रत्येक परत में 1.2 का मंदी कारक है, मिश्रित मंदी का कारक 1.2 ^ 5 या 2.5 है।) इसके बारे में सोचने का यह सिर्फ एक तरीका है।

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

जोड़ा गया: छोटा उदाहरण। एक बार मैंने फ़ैक्टरी-फ़्लोर सॉफ़्टवेयर पर एक टीम में काम किया, जिसने वर्क ऑर्डर या "जॉब" की एक श्रृंखला को ट्रैक किया। एक फ़ंक्शन था जो JobDone(idJob)बता सकता है कि क्या नौकरी की गई थी। एक कार्य तब किया गया था जब इसके सभी उप-कार्य किए गए थे, और उनमें से प्रत्येक तब किया गया था जब इसके सभी उप-संचालन किए गए थे। इन सभी चीजों को एक रिलेशनल डेटाबेस में ट्रैक किया गया था। किसी अन्य फ़ंक्शन के लिए एक एकल कॉल वह सारी जानकारी निकाल सकता है, इसलिए JobDoneउस अन्य फ़ंक्शन को कॉल किया जाता है, यह देखा जाता है कि क्या काम किया गया था, और बाकी को फेंक दिया। तब लोग आसानी से इस तरह कोड लिख सकते थे:

while(!JobDone(idJob)){
    ...
}

या

foreach(idJob in jobs){
    if (JobDone(idJob)){
        ...
    }
}

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


1

मुझे लगता है कि यह वास्तव में भाषा और कार्य पर निर्भर करता है। जबकि c और c ++ कंपाइलर बहुत सारे कार्यों को इनलाइन कर सकते हैं, यह पायथन या जावा के लिए ऐसा नहीं है।

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

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

परंतु

जब आप वेरिएबल्स को फंक्शन्स के अंदर परिभाषित करते हैं, तो इंटरप्रेटर बायटेकोड में सामान्य लोड निर्देश के बजाय LOADFAST का उपयोग करता है, जिससे आपका कोड तेजी से बढ़ता है ...

एक और बात यह है कि जब आप किसी कॉल करने योग्य वस्तु को परिभाषित करते हैं, तो संस्मरण जैसे पैटर्न संभव होते हैं और वे प्रभावी रूप से आपकी गणना को बहुत तेज कर सकते हैं (अधिक मेमोरी का उपयोग करने की लागत पर)। मूल रूप से यह हमेशा एक व्यापार है। फंक्शन कॉल की लागत मापदंडों पर भी निर्भर करती है, क्योंकि वे निर्धारित करते हैं कि स्टैक पर आपको वास्तव में कितना सामान कॉपी करना है (इस प्रकार c / c ++ में बड़े पैरामीटर जैसे पॉइंटर्स / रेफरेंस बाय वैल्यू के बजाय पास करना आम बात है)।

मुझे लगता है कि स्टैकएक्सचेंज पर पूरी तरह से उत्तर देने के लिए आपका प्रश्न बहुत व्यापक है।

मैं आपको जो करने का सुझाव देता हूं वह एक भाषा के साथ शुरू करना है और यह समझने के लिए कि यह कैसे विशिष्ट भाषा द्वारा कार्य कॉल कार्यान्वित किया जाता है, यह समझने के लिए उन्नत प्रलेखन का अध्ययन करें।

आप आश्चर्यचकित होंगे कि आप इस प्रक्रिया में कितनी चीजें सीखेंगे।

यदि आपको कोई विशिष्ट समस्या है, तो माप / रूपरेखा बनाएं और मौसम तय करें कि एक फ़ंक्शन बनाने या समकक्ष कोड को कॉपी / पेस्ट करना बेहतर है।

यदि आप अधिक विशिष्ट प्रश्न पूछते हैं, तो अधिक विशिष्ट उत्तर प्राप्त करना आसान होगा, मुझे लगता है।


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

मुद्दा यह है कि यह भाषा पर निर्भर करता है। C और C ++ में, यदि फ़ंक्शन अछूता है, तो प्रभाव 0. है, यदि
इनलेट

1

मैंने कुछ समय पहले एक्सॉन पावरपीसी पर डायरेक्ट और वर्चुअल सी ++ फ़ंक्शन कॉल के ओवरहेड को मापा

विचाराधीन कार्यों में एक एकल पैरामीटर और एक एकल वापसी थी, इसलिए पैरामीटर पासिंग रजिस्टरों पर हुई।

लंबी कहानी छोटी, एक इनलाइन फ़ंक्शन कॉल की तुलना में एक प्रत्यक्ष (गैर-आभासी) फ़ंक्शन कॉल का ओवरहेड लगभग 5.5 नैनोसेकंड या 18 घड़ी चक्र था। इनलाइन की तुलना में आभासी फ़ंक्शन कॉल का ओवरहेड 13.2 नैनोसेकंड या 42 घड़ी चक्र था।

ये समय अलग-अलग प्रोसेसर परिवारों पर अलग-अलग हैं। मेरा परीक्षण कोड यहाँ है ; आप अपने हार्डवेयर पर एक ही प्रयोग चला सकते हैं। अपने CFastTimer कार्यान्वयन के लिए rdtsc जैसे उच्च-सटीक टाइमर का उपयोग करें ; सिस्टम समय () लगभग सटीक नहीं है।

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