19

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

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

मुझे पता है कि मैं इन चीजों को जानना चाहता हूं जो भयानक प्रोग्रामिंग आदतें हैं, लेकिन कृपया मुझे बताएं कि यह बुद्धिमान है या नहीं। मैं वैसे भी कोशिश करने जा रहा हूं लेकिन कृपया मुझे अपनी राय दें।

+10

प्रोफाइलिंग से पहले कोड अनुकूलित न करें! ... –

+1

http://stackoverflow.com/questions/3730000/can-static-local-variables-cut-down-on-memory-allocation-time – jamesdlin

+0

आम तौर पर खराब करना हजारों बार नामक एक समारोह के लिए मुश्किल से मामूली गतिरोध प्राप्त करने के लिए चीजें वास्तव में एक बुरा विचार है। यदि आप हजारों बार नामक फ़ंक्शन के लिए 10ns प्रति फ़ंक्शन कॉल सहेज सकते हैं ... आपने 10 माइक्रोसॉन्ड के कुछ से अधिक सहेजे हैं, जो कि तुच्छ है, जब तक कि आप हार्ड रीयल-टाइम सिस्टम पर काम नहीं कर रहे हों और एक गंभीर समस्या हो समय slicing के साथ। प्रोफाइलर का सुझाव देने वाले सभी के लिए –

उत्तर

22

स्थानीय चर के ओवरहेड शून्य है। प्रत्येक बार जब आप फ़ंक्शन को कॉल करते हैं, तो आप पहले से ही पैरामीटर के लिए स्टैक सेट कर रहे हैं, मूल्यों को वापस कर सकते हैं, आदि। स्थानीय चर जोड़ना मतलब है कि आप स्टैक पॉइंटर में थोड़ा बड़ा नंबर जोड़ रहे हैं (एक संख्या जिसे संकलन समय पर गणना की जाती है) ।

इसके अलावा, कैश इलाके के कारण स्थानीय चर संभवतः तेज़ हैं।

यदि आप केवल अपने काम "हजारों" (लाखों या अरबों) नहीं हैं, तो आपको के बाद अपने एल्गोरिदम को देखना चाहिए, आपने एक प्रोफाइलर चलाया है।


पुन: कैश इलाके (read more here): बार-बार पहुँचा वैश्विक चर शायद अस्थायी इलाके की है। फ़ंक्शन निष्पादन के दौरान उन्हें एक रजिस्टर में भी कॉपी किया जा सकता है, लेकिन फ़ंक्शन रिटर्न के बाद मेमोरी (कैश) में वापस लिखा जाएगा (अन्यथा वे किसी और चीज़ तक पहुंच योग्य नहीं होंगे; रजिस्टरों के पास पते नहीं हैं)।

स्थानीय चरों में आम तौर पर दोनों अस्थायी और स्थानिक इलाके होंगे (वे इसे ढेर पर बनाए जाने के आधार पर प्राप्त करते हैं)। इसके अतिरिक्त, उन्हें सीधे रजिस्ट्रारों को "आवंटित" किया जा सकता है और स्मृति में कभी भी लिखा नहीं जा सकता है।

+3

+1, "एक हजार बार एक सेकंड" "हर कुछ मिलियन चक्रों में" होता है। –

+2

+1 हालांकि यह निश्चित रूप से निर्भर करता है कि संकलक कोड कैसे उत्पन्न करता है। बुद्धिमान कंपाइलर्स के लिए। अंतर 'एसपी एसपी, 20' और' उप स्पैम, 24' के बीच है, जो कि कोई अंतर नहीं है। – paxdiablo

+0

+1: मैं बस लगभग एक ही प्रतिक्रिया लिख ​​रहा था। – dawg

9

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

यादृच्छिक माइक्रो कोड-ट्यूनिंग न करें क्योंकि आपको लगता है कि यह तेजी से होगा। कंपाइलर्स में चीजों के थोड़ा अलग कार्यान्वयन होते हैं और एक पर्यावरण पर एक कंपाइलर पर सत्य क्या होता है, यह किसी अन्य कॉन्फ़िगरेशन पर गलत हो सकता है।

कम पैरामीटर के बारे में उस टिप्पणी से निपटने के लिए: "इनलाइनिंग" कार्यों की प्रक्रिया अनिवार्य रूप से फ़ंक्शन को कॉल करने से संबंधित ओवरहेड को हटा देती है। संभावना है कि एक छोटा सा फ़ंक्शन संकलक द्वारा स्वचालित रूप से रेखांकित किया जाएगा, लेकिन आप suggest a function be inlined भी कर सकते हैं।

एक अलग भाषा में, सी ++, नया मानक आ रहा है, पूर्ण अग्रेषण का समर्थन करता है, और रावल संदर्भों के साथ सही कदम अर्थशास्त्र का समर्थन करता है जो कुछ मामलों में अस्थायी की आवश्यकता को हटा देता है जो एक समारोह को कॉल करने की लागत को कम कर सकता है।

मुझे संदेह है कि आप समय-समय पर अनुकूलन कर रहे हैं, हालांकि, आपको तब तक प्रदर्शन से संबंधित नहीं होना चाहिए जब तक कि आप अपनी असली बाधाओं को नहीं खोज लेते।

+0

+1 के लिए समझदार होने और अनुमान लगाने के लिए आग्रह का विरोध करने के लिए +1। :) –

+0

धन्यवाद! मैंने कोशिश की, जैसा कि मैंने कहा था कि मैं चाहता हूं। मेरे कार्यक्रम में पहले से ही कोड का एक टुकड़ा था जो इसकी चीज करने के लिए किए गए सेकंडों की गणना करता था। स्थिर/वैश्विक चीज़ से 60 सेकेंड में यह क्या हुआ, अब 49 सेकंड लगे। मैं अभी भी यह नहीं कह सकता कि यह एक अच्छा विचार था, लेकिन यह इस समय काम करता प्रतीत होता है, लगातार परिणाम देता है :) मुझे संकलक अनुकूलन के बारे में पता नहीं था या स्टैक का उपयोग स्थानीय चर के कार्यों के लिए भी किया जाता था (मैं अभी भी बहुत कुछ हूं एक नौसिखिया के)। भी, मैं निश्चित रूप से सी ++ 0x में देखता हूं जब इसकी यहां (इसकी सभी विशेषताएं: मुझे लगता है कि रैवल्यू चीज और लैम्ब्डा पहले ही जीसीसी में हैं: डी)। धन्यवाद!! –

3

बिलकुल नहीं! केवल "प्रदर्शन" अंतर जब चर initialised कर रहे हैं

int anint = 42; 
vs 
    static int anint = 42; 

पहला मामला पूर्णांक 42 के लिए हर समय समारोह दूसरे मामले OT 42 पर सेट हो जाएगा में कहा जाता है की स्थापना की जाएगी में जब कार्यक्रम भरी हुई है ।

हालांकि अंतर बहुत ही कमजोर होने के लिए इतना छोटा है। यह एक आम गलत धारणा है कि भंडारण को प्रत्येक कॉल पर "स्वचालित" चर के लिए आवंटित किया जाना है। ऐसा नहीं है इसलिए सी इन चर के लिए स्टैक में पहले से आवंटित स्थान का उपयोग करता है।

स्थिर चर वास्तव में आपको धीमा कर सकते हैं क्योंकि स्थिर चर पर कुछ आक्रामक अनुकूलन संभव नहीं हैं। इसके अलावा स्थानीय लोग ढेर के एक संगत क्षेत्र में हैं, इसलिए उन्हें कुशलता से कैश करना आसान होता है।

1

हां, स्थैतिक चर का उपयोग करके एक कार्य थोड़ा तेज़ हो जाएगा। हालांकि, यदि आप कभी भी अपने प्रोग्राम को बहु-थ्रेडेड बनाना चाहते हैं तो इससे समस्याएं पैदा होंगी। चूंकि फ़ंक्शन इनवॉक्शंस के बीच स्थैतिक चर साझा किए जाते हैं, इसलिए अलग-अलग थ्रेड में एक साथ फ़ंक्शन का आविष्कार करने से अपरिभाषित व्यवहार होगा।बहु-थ्रेडिंग उस चीज का प्रकार है जिसे आप भविष्य में करना चाहते हैं ताकि वास्तव में आपके कोड को तेज़ी से बढ़ाया जा सके।

आपके द्वारा उल्लिखित अधिकांश चीजों को माइक्रो-ऑप्टिमाइज़ेशन के रूप में जाना जाता है। आम तौर पर, इस तरह की चीजों के बारे में चिंता करना bad idea है। यह आपके कोड को पढ़ने के लिए कठिन बनाता है, और बनाए रखने के लिए कठिन है। यह भी बग पेश करने की संभावना है। आपको उच्च स्तर पर अपने बकाया अनुकूलन के लिए अधिक धमाके मिलेंगे।

एम 2 टीएम सुझावों के रूप में, एक प्रोफाइलर चलाने के लिए भी एक अच्छा विचार है। gprof देखें जो कि उपयोग में आसान है।

1

आप हमेशा अपने आवेदन को सही ढंग से निर्धारित करने के लिए समय दे सकते हैं कि सबसे तेज़ क्या है। यहां मैं समझता हूं: (यह सब आपके प्रोसेसर के आर्किटेक्चर पर निर्भर करता है, बीटीडब्ल्यू)

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

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

आपके परिदृश्यों के बीच बड़ा अंतर स्मृति पदचिह्न है, इतना तेज नहीं है।

+2

यह एक महत्वपूर्ण बिंदु है - जब तक आपके चर प्रारंभ नहीं किए जाते हैं, आवंटित 100 स्वचालित चर एक के रूप में बस आवंटित होते हैं। – caf

+0

यह ध्यान दिया जाना चाहिए कि संकलक स्मृति को "आवंटित" कर रहा है, स्मृति प्रबंधन प्रणाली नहीं। आधुनिक CPU गति के संदर्भ में – KFro

0

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

1

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

+0

* हर बार फ़ंक्शन कहलाता है, इसे यह सुनिश्चित करने के लिए जांचना पड़ता है कि स्थैतिक चर अभी तक प्रारंभ नहीं हुआ है * <- यह गलत है। मुख्य() रन से पहले, सभी स्थैतिक चर प्रारंभ किए जाते हैं (__start() में)। इस समय ग्लोबल्स भी शुरू किए गए हैं। –

+0

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

+0

@dwelch: बिंदु यह है कि एक स्थानीय मुख्य स्मृति में दिखाई नहीं दे सकता है, इसे केवल एक रजिस्टर में रहने के लिए अनुकूलित (सुरक्षित रूप से) किया जा सकता है। – SingleNegationElimination

3

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

दो चरम सीमाओं पर विचार करें; यदि आपके पास केवल एक या कुछ स्थानीय चर हैं, तो उन्हें आवंटित स्मृति स्थानों की बजाय रजिस्टरों में आसानी से संग्रहीत किया जा सकता है। यदि रजिस्टर "दबाव" पर्याप्त रूप से कम है कि यह किसी भी निर्देश को निष्पादित किए बिना हो सकता है।

विपरीत चरम पर कुछ मशीनों (जैसे, आईबीएम mainframes) सब पर ढेर की जरूरत नहीं है कि देखते हैं। इस मामले में, हम सामान्य रूप से स्टैक फ्रेम के रूप में क्या सोचते हैं, वास्तव में ढेर पर एक लिंक्ड सूची के रूप में आवंटित किए जाते हैं। जैसा कि आप शायद अनुमान लगाएंगे, यह काफी धीमा हो सकता है।

यह चर तक पहुँचने की बात आती है, स्थिति की कुछ ऐसी ही है - एक मशीन रजिस्टर करने के लिए उपयोग बहुत अच्छी तरह से स्मृति में आवंटित कुछ भी की तुलना में तेजी होने की गारंटी है कर सकते हैं के लिए संभव आशा। ओटीओएच, स्टैक पर चर धीमी होने के चर के लिए संभव है - इसे सामान्य रूप से अनुक्रमित अप्रत्यक्ष पहुंच की तरह कुछ चाहिए, जो (विशेष रूप से पुराने CPUs के साथ) काफी धीमी हो जाती है। ओटीओएच, एक वैश्विक तक पहुंच (जो एक स्थैतिक है, भले ही उसका नाम विश्व स्तर पर दिखाई नहीं दे रहा हो) आम तौर पर एक पूर्ण पता बनाने की आवश्यकता होती है, जो कुछ सीपीयू कुछ डिग्री तक दंडित करते हैं।

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

+0

उम, जैरी, नोटिस – Will

2

ऐसा लगता है कि स्थिर बनाम गैर स्थिर की तरह पूरी तरह से कवर किया गया है, लेकिन वैश्विक चर के विषय पर। अक्सर ये इसे गति देने के बजाय प्रोग्राम निष्पादन को धीमा कर देगा।

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

int myFunction() 
{ 
    SomeStruct *A, *B; 
    FillOutSomeStruct(B); 
    memcpy(A, B, sizeof(A); 
    return A.result; 
} 

संकलक जानता है कि सूचक ए और बी ओवरलैप कभी नहीं कर सकते हैं और इसलिए यह प्रति अनुकूलन कर सकते हैं:

इस संयोजन होता है जब आप संकेत दिए गए परिचय, आप निम्न कोड है का कहना है। यदि ए और बी वैश्विक हैं तो वे संभावित रूप से ओवरलैपिंग या समान मेमोरी को इंगित कर सकते हैं, इसका मतलब है कि कंपाइलर को 'इसे सुरक्षित खेलना' चाहिए जो धीमा है। समस्या को आम तौर पर 'पॉइंटर एलियासिंग' कहा जाता है और कई परिस्थितियों में केवल स्मृति प्रतियां नहीं हो सकती हैं।

http://en.wikipedia.org/wiki/Pointer_alias

0

रूपरेखा अंतर नहीं देख सकते हैं, वियोजन और पराक्रम देखने के लिए क्या जानते हुए भी।

मुझे संदेह है कि आपको केवल कुछ घड़ी चक्र प्रति लूप (कंपाइलर के आधार पर औसत) के रूप में भिन्नता प्राप्त करने जा रहे हैं। कभी-कभी परिवर्तन नाटकीय सुधार या नाटकीय रूप से धीमा हो जाएगा, और यह अनिवार्य रूप से नहीं होगा क्योंकि चर घरों को ढेर से/स्थानांतरित कर दिया गया है। आइए मान लें कि आप 2GHz प्रोसेसर पर 10000 कॉल के लिए फ़ंक्शन कॉल के चार घड़ी चक्र सहेजते हैं। बहुत मोटा गणना: 20 microseconds बचाया। 20 माइक्रोसॉन्ड आपके वर्तमान निष्पादन समय की तुलना में बहुत कम या थोड़ा है?

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

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

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